Skip to content
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e4d849b
Add display entities as a type of projectile for kits. They do not mo…
KaevonD Dec 25, 2024
1a0accf
Remove logs
KaevonD Dec 25, 2024
f26e050
We have janky movement
chatasma Jan 11, 2025
8254291
It moves in waves now
chatasma Jan 11, 2025
e96d71f
interpolation added to nms hacks
chatasma Jan 11, 2025
9b72e57
Add interpolation
KaevonD Jan 11, 2025
3675f0b
help
KaevonD Jan 14, 2025
978d6fd
this is for tank, no sleeping allowed
KaevonD Jan 14, 2025
f9f70df
Aligning block-display projectiles w.r.t player facing & teleportatio…
chatasma Jan 28, 2025
5d966ca
damage enemy players upon impact
KaevonD Feb 3, 2025
6e69021
Add scale to block display from xml.
KaevonD Feb 8, 2025
4352ed9
get projectile damage and velocity from map xml
KaevonD Feb 18, 2025
a8fa373
Cleaning up code and removing unused functions
KaevonD Mar 10, 2025
e2a746b
it hits EVERY SINGLE TIMEEEEE
KaevonD May 3, 2025
b6fb019
At block collision
KaevonD May 9, 2025
9763acf
Check between teleport locations with respect to size of projectile
KaevonD May 10, 2025
00706de
Fix all sources of possible lag
KaevonD May 10, 2025
5688d86
Remove unused function
KaevonD May 10, 2025
9fe84da
Remove while true loop
KaevonD May 10, 2025
20a889a
Remove unnecessary comments, files, and folders
KaevonD May 10, 2025
73be187
Add max travel time attribute
KaevonD May 10, 2025
47e03dd
wip add block projectile
chatasma May 12, 2025
7e78085
more block projectile stuff things
chatasma May 17, 2025
34cc4d6
fix more block projectile stuff things
chatasma May 17, 2025
10c0c0d
made it nicer than tank's
KaevonD May 17, 2025
4497d8f
clean up clean up everybody clean up
chatasma May 17, 2025
00282c6
removed unnecessary NSM_HACKS functions and one other fun thing
KaevonD May 17, 2025
f98dd14
progress
chatasma May 18, 2025
79e53b6
add other requested changes
KaevonD May 18, 2025
b8e5d91
apply velocity to falling block
KaevonD May 18, 2025
3fd6a6d
fix duplicate falling blocks
KaevonD May 18, 2025
5a9698a
Add a bunch of cleanup to the impl
Pablete1234 May 18, 2025
b61b00b
Further cleanup and bugfixes
Pablete1234 May 19, 2025
25afbbc
fix rotation
KaevonD May 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ public class ProjectileDefinition extends SelfIdentifyingFeatureDefinition {
protected @Nullable Float power;
protected double velocity;
protected ClickAction clickAction;
protected Class<? extends Entity> projectile;
protected ProjectileEntity projectile;
protected List<PotionEffect> potion;
protected Filter destroyFilter;
protected Duration coolDown;
protected boolean throwable;
protected boolean precise;
protected BlockMaterialData blockMaterial;
protected float scale;
protected boolean solidBlockCollision;
protected Duration maxTravelTime;

public ProjectileDefinition(
@Nullable String id,
Expand All @@ -30,13 +33,16 @@ public ProjectileDefinition(
@Nullable Float power,
double velocity,
ClickAction clickAction,
Class<? extends Entity> entity,
ProjectileEntity entity,
List<PotionEffect> potion,
Filter destroyFilter,
Duration coolDown,
boolean throwable,
boolean precise,
BlockMaterialData blockMaterial) {
BlockMaterialData blockMaterial,
float scale,
boolean solidBlockCollision,
Duration maxTravelTime) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These seem to only affect custom projectile entities, so it probably makes sense that they'd go in as part of the CustomEntity implementation of ProjectileEntity.

Additionally scale should probably be size as that leaves it more open to future projectiles (ie: raytraced beam)

super(id);
this.name = name;
this.damage = damage;
Expand All @@ -50,6 +56,31 @@ public ProjectileDefinition(
this.throwable = throwable;
this.precise = precise;
this.blockMaterial = blockMaterial;
this.scale = scale;
this.solidBlockCollision = solidBlockCollision;
this.maxTravelTime = maxTravelTime;
}

public static sealed class ProjectileEntity {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A class with no members and no functions, may as well be a sealed interface instead:

Suggested change
public static sealed class ProjectileEntity {
public sealed interface ProjectileEntity permits RealEntity, CustomEntity {

public static final class RealEntity extends ProjectileEntity {
public final Class<? extends Entity> entityType;

public RealEntity(Class<? extends Entity> entityType) {
this.entityType = entityType;
}
}

public static final class AbstractEntity extends ProjectileEntity {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abstract has a very specific meaning in the java world, and given this class isn't even abstract, i'd vote against it, and instead probably use something like CustomEntity

public final AbstractEntityType entityType;

public AbstractEntity(AbstractEntityType entityType) {
this.entityType = entityType;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably use a record for these:

Suggested change
public static final class RealEntity extends ProjectileEntity {
public final Class<? extends Entity> entityType;
public RealEntity(Class<? extends Entity> entityType) {
this.entityType = entityType;
}
}
public static final class AbstractEntity extends ProjectileEntity {
public final AbstractEntityType entityType;
public AbstractEntity(AbstractEntityType entityType) {
this.entityType = entityType;
}
}
record RealEntity(Class<? extends Entity> entityType) implements ProjectileEntity {}
record CustomEntity(CustomEntityType type) implements ProjectileEntity {}


enum AbstractEntityType {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, avoid Abstract when something is not abstract

BLOCK
}
}

public @Nullable String getName() {
Expand Down
155 changes: 149 additions & 6 deletions core/src/main/java/tc/oc/pgm/projectile/ProjectileMatchModule.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package tc.oc.pgm.projectile;

import static tc.oc.pgm.util.Assert.assertTrue;
import static tc.oc.pgm.util.nms.Packets.ENTITIES;

import com.google.common.collect.ImmutableSet;

import java.util.Collection;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.*;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid wildcard imports, configure your ide not to use them

import java.util.function.BooleanSupplier;

import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
Expand Down Expand Up @@ -37,16 +44,22 @@
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.match.MatchModule;
import tc.oc.pgm.api.match.MatchScope;
import tc.oc.pgm.api.party.Party;
import tc.oc.pgm.api.player.MatchPlayer;
import tc.oc.pgm.api.player.ParticipantState;
import tc.oc.pgm.events.ListenerScope;
import tc.oc.pgm.events.PlayerParticipationStopEvent;
import tc.oc.pgm.filters.query.BlockQuery;
import tc.oc.pgm.filters.query.PlayerBlockQuery;
import tc.oc.pgm.kits.tag.ItemTags;
import tc.oc.pgm.projectile.path.LinearProjectilePath;
import tc.oc.pgm.projectile.path.ProjectilePath;
import tc.oc.pgm.util.bukkit.MetadataUtils;
import tc.oc.pgm.util.inventory.InventoryUtils;
import tc.oc.pgm.util.nms.NMSHacks;
import tc.oc.pgm.util.nms.packets.BlockEntity;
import tc.oc.pgm.util.nms.packets.FakeEntity;
import tc.oc.pgm.util.nms.packets.Packet;

@ListenerScope(MatchScope.RUNNING)
public class ProjectileMatchModule implements MatchModule, Listener {
Expand Down Expand Up @@ -88,32 +101,96 @@ && isValidProjectileAction(event.getAction(), projectileDefinition.clickAction))

if (this.isCooldownActive(player, projectileDefinition)) return;

boolean realProjectile = Projectile.class.isAssignableFrom(projectileDefinition.projectile);
boolean realProjectile = false;
if (projectileDefinition.projectile instanceof ProjectileDefinition.ProjectileEntity.RealEntity) {
realProjectile = Projectile.class.isAssignableFrom(
((ProjectileDefinition.ProjectileEntity.RealEntity) projectileDefinition.projectile).entityType
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given how much projectileDefinition.projectile is used, not just here but in the following blocks too, you may as well extract it

Suggested change
boolean realProjectile = false;
if (projectileDefinition.projectile instanceof ProjectileDefinition.ProjectileEntity.RealEntity) {
realProjectile = Projectile.class.isAssignableFrom(
((ProjectileDefinition.ProjectileEntity.RealEntity) projectileDefinition.projectile).entityType
);
}
var projType = projectileDefinition.projectile;
boolean realProjectile = projType instanceof ProjectileDefinition.ProjectileEntity.RealEntity re
&& Projectile.class.isAssignableFrom(re.entityType());

Let's make use of java 17 features too

Vector velocity =
player.getEyeLocation().getDirection().multiply(projectileDefinition.velocity);
Entity projectile;
Entity projectile = null;
BlockEntity blockEntity = null;
try {
assertTrue(launchingDefinition.get() == null, "nested projectile launch");
launchingDefinition.set(projectileDefinition);
if (realProjectile) {
projectile = player.launchProjectile(
projectileDefinition.projectile.asSubclass(Projectile.class), velocity);
(((ProjectileDefinition.ProjectileEntity.RealEntity) projectileDefinition.projectile).entityType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is very ugly, and i'm starting to believe now that we have wrapper entities we can probably hide away these differences inside such that you can:

projectileDefinition.projectile.launch(player, velocity) and this logic is within there, not just for this but for the others too

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was attempting to do this, but it was taking some time and effort to figure out. In the end, I left it and I would rather potentially come back to it later than to do it as part of this PR. I think it is ok to have here for now.

.asSubclass(Projectile.class),
velocity
);
if (projectile instanceof Fireball fireball && projectileDefinition.precise) {
NMSHacks.NMS_HACKS.setFireballDirection(fireball, velocity);
}
} else {
if (FallingBlock.class.isAssignableFrom(projectileDefinition.projectile)) {
if ((projectileDefinition.projectile instanceof ProjectileDefinition.ProjectileEntity.RealEntity) &&
FallingBlock.class.isAssignableFrom((((ProjectileDefinition.ProjectileEntity.RealEntity) projectileDefinition.projectile).entityType))) {
projectile =
projectileDefinition.blockMaterial.spawnFallingBlock(player.getEyeLocation());
} else {
projectile =
player.getWorld().spawn(player.getEyeLocation(), projectileDefinition.projectile);
Location loc = player.getEyeLocation();
if ((projectileDefinition.projectile instanceof ProjectileDefinition.ProjectileEntity.AbstractEntity) &&
((ProjectileDefinition.ProjectileEntity.AbstractEntity) projectileDefinition.projectile).entityType == ProjectileDefinition.ProjectileEntity.AbstractEntityType.BLOCK) {
loc.setPitch(0);
loc.setYaw(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like logic that the version-dependant impl should handle, if it needs it

blockEntity = ENTITIES.spawnBlockEntity(loc, projectileDefinition.blockMaterial);
projectile = blockEntity.entity();
} else if (projectileDefinition.projectile instanceof ProjectileDefinition.ProjectileEntity.RealEntity) {
projectile =
player.getWorld().spawn(loc, ((ProjectileDefinition.ProjectileEntity.RealEntity) projectileDefinition.projectile).entityType);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else if (projectileDefinition.projectile instanceof ProjectileDefinition.ProjectileEntity.RealEntity) {
projectile =
player.getWorld().spawn(loc, ((ProjectileDefinition.ProjectileEntity.RealEntity) projectileDefinition.projectile).entityType);
}
} else if (projectileDefinition.projectile instanceof ProjectileDefinition.ProjectileEntity.RealEntity re) {
projectile = player.getWorld().spawn(loc, re.entityType());
}

really should use java 17's features in a bunch of these

}
projectile.setVelocity(velocity);
}
if (projectileDefinition.power != null && projectile instanceof Explosive) {
((Explosive) projectile).setYield(projectileDefinition.power);
}
if (blockEntity != null && blockEntity.isDisplayEntity()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a nope, you're running version-dependant logic in the common area, this is something the wrapper for your entity should handle

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as what I mentioned in the other comment, I am hoping we can let this go for now and perhaps come back to add the wrapper later. I was having trouble finding a nice way to implement it to properly account for all scenarios, perhaps if you know a good way then you could add it quickly.

Location loc = player.getEyeLocation();

blockEntity.align(loc.getPitch(), loc.getYaw(), projectileDefinition.scale);

blockEntity.setBlock(projectileDefinition.blockMaterial.getItemType());

final Vector normalizedDirection = player.getLocation().getDirection().normalize();
final LinearProjectilePath linearProjectilePath = new LinearProjectilePath(
normalizedDirection, projectileDefinition.velocity
);
final Location initialLocation = projectile.getLocation();

BlockEntity finalBlockEntity = blockEntity;
Entity finalProjectile = projectile;
runFixedTimesAtPeriod(
match.getExecutor(MatchScope.RUNNING),
new BooleanSupplier() {
private int progress = 0;


@Override
public boolean getAsBoolean() {
finalBlockEntity.setTeleportationDuration(1);

Location currentLocation = finalProjectile.getLocation();
Location incrementingLocation = currentLocation.clone();
Location newLocation = calculateTo(initialLocation, linearProjectilePath, ++progress);

finalProjectile.teleport(newLocation);
if (projectileDefinition.damage != null || projectileDefinition.solidBlockCollision) {
while (currentLocation.distanceSquared(incrementingLocation) < currentLocation.distanceSquared(newLocation)) {
if (blockDisplayCollision(projectileDefinition, player, incrementingLocation)) {
return true;
}
incrementingLocation.add(normalizedDirection.clone().multiply(projectileDefinition.scale));
}
return blockDisplayCollision(projectileDefinition, player, incrementingLocation);
}
return false;
}
},
1L, projectileDefinition.maxTravelTime.toMillis(), projectile::remove
);
}
projectile.setMetadata(
"projectileDefinition", new FixedMetadataValue(PGM.get(), projectileDefinition));
} finally {
Expand Down Expand Up @@ -142,6 +219,72 @@ && isValidProjectileAction(event.getAction(), projectileDefinition.clickAction))
}
}

private boolean blockDisplayCollision(ProjectileDefinition projectileDefinition, Player player, Location location) {
if (projectileDefinition.damage != null) {
Collection<Entity> nearbyEntities = location.getNearbyEntities(0.5 * projectileDefinition.scale, 0.5 * projectileDefinition.scale, 0.5 * projectileDefinition.scale);
if (!nearbyEntities.isEmpty()) {
Party playerParty = PGM.get().getMatchManager().getPlayer(player).getParty();
for (Entity entity : nearbyEntities) {
if (entity instanceof Player) {
if (playerParty != PGM.get().getMatchManager().getPlayer(((Player) entity)).getParty()) {
((Player) entity).damage(projectileDefinition.damage, player);
return true;
}
}
}
}
}
if (projectileDefinition.solidBlockCollision) {
double posScale = 0.5 * projectileDefinition.scale;
double negScale = -0.5 * projectileDefinition.scale;

Block b1 = location.clone().add(negScale, negScale, negScale).getBlock();
Block b2 = location.clone().add(posScale, negScale, negScale).getBlock();
Block b3 = location.clone().add(negScale, posScale, negScale).getBlock();
Block b4 = location.clone().add(posScale, posScale, negScale).getBlock();
Block b5 = location.clone().add(negScale, negScale, posScale).getBlock();
Block b6 = location.clone().add(posScale, negScale, posScale).getBlock();
Block b7 = location.clone().add(negScale, posScale, posScale).getBlock();
Block b8 = location.clone().add(posScale, posScale, posScale).getBlock();

return b1.getType().isSolid() || b2.getType().isSolid() || b3.getType().isSolid() || b4.getType().isSolid() || b5.getType().isSolid() || b6.getType().isSolid() || b7.getType().isSolid() || b8.getType().isSolid();
}
return false;
}

// For entities which need their velocity simulated
private Location calculateTo(
final Location entityLocation,
final ProjectilePath path,
final int progress) {
return entityLocation.clone().add(path.getPositionAtProgress(progress));
}

private static void runFixedTimesAtPeriod(
final ScheduledExecutorService scheduledExecutorService, final BooleanSupplier runnable,
final long periodTicks, final long upperBoundMillis,
final Runnable finishHandler
) {
final ScheduledFuture<?>[] ref = new ScheduledFuture[2];
final ScheduledFuture<?> mainTask = scheduledExecutorService.scheduleAtFixedRate(
() -> {
final boolean shouldCancel = runnable.getAsBoolean();
if (shouldCancel) {
ref[0].cancel(true);
ref[1].cancel(true);
finishHandler.run();
}
}, 0L, periodTicks * 50L, TimeUnit.MILLISECONDS
);
final ScheduledFuture<?> cleanupTask = scheduledExecutorService.schedule(() -> {
if (ref[0].isCancelled()) return;
ref[0].cancel(true);
finishHandler.run();
}, upperBoundMillis, TimeUnit.MILLISECONDS);
ref[0] = mainTask;
ref[1] = cleanupTask;
}

@EventHandler
public void onProjectileHurtEvent(EntityDamageByEntityEvent event) {
if (!(event.getEntity() instanceof LivingEntity)) return;
Expand Down
54 changes: 50 additions & 4 deletions core/src/main/java/tc/oc/pgm/projectile/ProjectileModule.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package tc.oc.pgm.projectile;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.time.Duration;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.bukkit.entity.Arrow;
Expand All @@ -23,6 +25,7 @@
import tc.oc.pgm.filters.parse.FilterParser;
import tc.oc.pgm.kits.KitParser;
import tc.oc.pgm.util.material.BlockMaterialData;
import tc.oc.pgm.util.nms.NMSHacks;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.Node;
import tc.oc.pgm.util.xml.XMLUtils;
Expand Down Expand Up @@ -62,9 +65,9 @@ public ProjectileModule parse(MapFactory factory, Logger logger, Document doc)
Node.fromChildOrAttr(projectileElement, "velocity"), Double.class, 1.0);
ClickAction clickAction = XMLUtils.parseEnum(
Node.fromAttr(projectileElement, "click"), ClickAction.class, ClickAction.BOTH);
Class<? extends Entity> entity =
XMLUtils.parseEntityTypeAttribute(projectileElement, "projectile", Arrow.class);
BlockMaterialData blockMaterial = entity.isAssignableFrom(FallingBlock.class)
ProjectileDefinition.ProjectileEntity entity =
parseProjectileEntity(projectileElement, "projectile", Arrow.class);
BlockMaterialData blockMaterial = acceptsBlockMaterial(entity)
? XMLUtils.parseBlockMaterialData(Node.fromAttr(projectileElement, "material"))
: null;
Float power = XMLUtils.parseNumber(
Expand All @@ -76,6 +79,9 @@ public ProjectileModule parse(MapFactory factory, Logger logger, Document doc)
boolean throwable =
XMLUtils.parseBoolean(projectileElement.getAttribute("throwable"), true);
boolean precise = XMLUtils.parseBoolean(projectileElement.getAttribute("precise"), true);
float scale = XMLUtils.parseNumber(Node.fromChildOrAttr(projectileElement, "scale"), Float.class, 1.0f);
boolean solidBlockCollision = XMLUtils.parseBoolean(projectileElement.getAttribute("solid-block-collision"), true);
Duration maxTravelTime = XMLUtils.parseDuration(projectileElement.getAttribute("max-travel-time"), Duration.ofSeconds(1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as mentioned above, these seem to only matter for the block custom entity, so you can put them as part of the parseProjectileEntity method and have them live inside the custom entity.


ProjectileDefinition projectileDefinition = new ProjectileDefinition(
id,
Expand All @@ -90,13 +96,53 @@ public ProjectileModule parse(MapFactory factory, Logger logger, Document doc)
coolDown,
throwable,
precise,
blockMaterial);
blockMaterial,
scale,
solidBlockCollision,
maxTravelTime);

factory.getFeatures().addFeature(projectileElement, projectileDefinition);
projectiles.add(projectileDefinition);
}

return projectiles.isEmpty() ? null : new ProjectileModule(ImmutableSet.copyOf(projectiles));
}

private static final Map<String, ProjectileDefinition.ProjectileEntity.AbstractEntity> ABSTRACT_ENTITY_MAP =
ImmutableMap.of(
"block", new ProjectileDefinition.ProjectileEntity.AbstractEntity(
ProjectileDefinition.ProjectileEntity.AbstractEntityType.BLOCK
)
);

private static ProjectileDefinition.ProjectileEntity parseProjectileEntity(
final Element element,
final String attributeName,
final Class<? extends Entity> def
) throws InvalidXMLException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private static ProjectileDefinition.ProjectileEntity parseProjectileEntity(
final Element element,
final String attributeName,
final Class<? extends Entity> def
) throws InvalidXMLException {
private static ProjectileDefinition.ProjectileEntity parseProjectileEntity(XmlFluentParser parser, Element el) throws InvalidXMLException {

Attribute name is always "projectile" and default entity is always arrow, there's no need to make them parameters, instead pass in the parser which will be of use

final Node node = Node.fromAttr(element, attributeName);
if (node == null) {
return new ProjectileDefinition.ProjectileEntity.RealEntity(def);
}
final String entityText = node.getValue();
if (!entityText.matches("[a-zA-Z0-9_]+")) {
throw new InvalidXMLException("Invalid entity type '" + entityText + "'", node);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't see the need for this, if nothing matches it you'll find out anyways

if (ABSTRACT_ENTITY_MAP.containsKey(entityText.toLowerCase())) {
return ABSTRACT_ENTITY_MAP.get(entityText.toLowerCase());
}
return new ProjectileDefinition.ProjectileEntity.RealEntity(XMLUtils.parseEntityTypeAttribute(element, attributeName, def));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given custom logic will likely have to be involved, consider just using a switch, and using the fluent parser:

Suggested change
if (ABSTRACT_ENTITY_MAP.containsKey(entityText.toLowerCase())) {
return ABSTRACT_ENTITY_MAP.get(entityText.toLowerCase());
}
return new ProjectileDefinition.ProjectileEntity.RealEntity(XMLUtils.parseEntityTypeAttribute(element, attributeName, def));
return switch(entityText.toLowerCase(Locale.ROOT)) {
case "entity" -> new ProjectileDefinition.BlockEntity(
parser.parseFloat(el, "scale").optional(1.0f),
parser.parseBool(el, "solid-block-collision").orTrue(),
parser.parseDuration(el, "max-travel-time").optional(DEFAULT_MAX_TRAVEL);
default -> new ProjectileDefinition.RealEntity(XMLUtils.parseEntityTypeAttribute(node));
}

}

private static boolean acceptsBlockMaterial(final ProjectileDefinition.ProjectileEntity entity) {
if (entity instanceof ProjectileDefinition.ProjectileEntity.RealEntity) {
final Class<? extends Entity> cls = ((ProjectileDefinition.ProjectileEntity.RealEntity) entity).entityType;
return cls.isAssignableFrom(FallingBlock.class) || NMSHacks.NMS_HACKS.isBlockDisplayEntity(cls);
} else if (entity instanceof ProjectileDefinition.ProjectileEntity.AbstractEntity) {
return ((ProjectileDefinition.ProjectileEntity.AbstractEntity) entity).entityType ==
ProjectileDefinition.ProjectileEntity.AbstractEntityType.BLOCK;
}
return false;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like logic that ProjectileEntity should implement instead

}
}
Loading