Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add splash potions entity to Spawner module #1002

Merged
merged 23 commits into from
May 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6d8ed1f
Add dropped potions support to spawners
CoWinkKeyDinkInc May 2, 2022
05bbe97
Make Splash potions have multiple effects, simplify code
CoWinkKeyDinkInc May 2, 2022
2487058
Simplify code
CoWinkKeyDinkInc May 2, 2022
dc2fbaa
Use <effect> inside <potion> for parsing
CoWinkKeyDinkInc May 2, 2022
f779edd
Add evaluation
CoWinkKeyDinkInc May 2, 2022
c627279
Fix evaluation of <potion> and <element>
CoWinkKeyDinkInc May 3, 2022
5a3e16b
Simplify Code
CoWinkKeyDinkInc May 3, 2022
a6a533c
Use finals for SpawnableItem and SpawnablePotion
CoWinkKeyDinkInc May 3, 2022
3e405a9
Refactor, convert spawner ID to string
CoWinkKeyDinkInc May 4, 2022
48eae5c
Use AtomicInteger for numericID, rearrange SpawnerDefinition
CoWinkKeyDinkInc May 5, 2022
3212cb2
Clone potionItem
CoWinkKeyDinkInc May 5, 2022
e956706
Parse potion effects properly
CoWinkKeyDinkInc May 5, 2022
fb03925
Rename spawner Element to spawnerEl
CoWinkKeyDinkInc May 5, 2022
ff95283
Reformat code
CoWinkKeyDinkInc May 5, 2022
cd081d4
Add damage value to determine potion color
CoWinkKeyDinkInc May 5, 2022
f0630e5
Make spawnerID string
CoWinkKeyDinkInc May 6, 2022
d610cbc
Rename variable
CoWinkKeyDinkInc May 9, 2022
cdf5fa9
Use NMSHacks to create EntityPotion
CoWinkKeyDinkInc May 9, 2022
f13edf6
Use NMSHacks properly
CoWinkKeyDinkInc May 11, 2022
04de451
Rework spawner IDs
CoWinkKeyDinkInc May 11, 2022
3e5624e
Simplify EntityPotion spawning
CoWinkKeyDinkInc May 12, 2022
682b7f3
Do pablo's suggestions
CoWinkKeyDinkInc May 13, 2022
1de5d6f
Set metadata to EntityPotion
CoWinkKeyDinkInc May 13, 2022
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
51 changes: 13 additions & 38 deletions core/src/main/java/tc/oc/pgm/spawner/Spawner.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package tc.oc.pgm.spawner;

import java.util.Objects;
import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
Expand Down Expand Up @@ -36,7 +37,6 @@ public class Spawner implements Listener, Tickable {
public Spawner(SpawnerDefinition definition, Match match) {
this.definition = definition;
this.match = match;

this.lastTick = match.getTick().tick;
this.players = new OnlinePlayerMapAdapter<>(PGM.get());
calculateDelay();
Expand All @@ -51,7 +51,6 @@ public void tick(Match match, Tick tick) {
definition.spawnRegion.getRandom(match.getRandom()).toLocation(match.getWorld());
spawnable.spawn(location, match);
match.getWorld().spigot().playEffect(location, Effect.FLAME, 0, 0, 0, 0.15f, 0, 0, 40, 64);

spawnedEntities = spawnedEntities + spawnable.getSpawnCount();
}
calculateDelay();
Expand Down Expand Up @@ -82,47 +81,23 @@ private boolean canSpawn() {

@EventHandler(priority = EventPriority.HIGHEST)
public void onItemMerge(ItemMergeEvent event) {
boolean entityTracked = false;
boolean targetTracked = false;
if (event.getEntity().hasMetadata(METADATA_KEY)) {
entityTracked = true;
}
if (event.getTarget().hasMetadata(METADATA_KEY)) {
targetTracked = true;
}

// Do nothing if neither item is from a PGM Spawner
if (!entityTracked && !targetTracked) {
return;
}

// Cancel the merge if only 1 of the items is from a PGM Spawner
if ((entityTracked && !targetTracked) || (!entityTracked && targetTracked)) {
event.setCancelled(true);
return;
}

int entitySpawnerID = -1;
int targetSpawnerID = -1;
if (event.getEntity().hasMetadata(METADATA_KEY)) {
entitySpawnerID =
MetadataUtils.getMetadata(event.getEntity(), METADATA_KEY, PGM.get()).asInt();
}
if (event.getTarget().hasMetadata(METADATA_KEY)) {
targetSpawnerID =
MetadataUtils.getMetadata(event.getTarget(), METADATA_KEY, PGM.get()).asInt();
}
// Cancel the merge if the items are from different PGM spawners
if (entitySpawnerID != targetSpawnerID) {
event.setCancelled(true);
return;
boolean entityTracked = event.getEntity().hasMetadata(METADATA_KEY);
boolean targetTracked = event.getTarget().hasMetadata(METADATA_KEY);
if (!entityTracked && !targetTracked) return; // None affected
if (entityTracked && targetTracked) {
String entitySpawnerId =
MetadataUtils.getMetadata(event.getEntity(), METADATA_KEY, PGM.get()).toString();
String targetSpawnerId =
MetadataUtils.getMetadata(event.getTarget(), METADATA_KEY, PGM.get()).toString();
if (entitySpawnerId.equals(targetSpawnerId)) return; // Same spawner, allow merge
}
event.setCancelled(true);
}

private void handleEntityRemoveEvent(Metadatable metadatable, int amount) {
if (metadatable.hasMetadata(METADATA_KEY)) {
if (MetadataUtils.getMetadata(metadatable, METADATA_KEY, PGM.get()).asInt()
== definition.numericID) {
if (Objects.equals(
MetadataUtils.getMetadata(metadatable, METADATA_KEY, PGM.get()), definition.getId())) {
spawnedEntities -= amount;
spawnedEntities = Math.max(0, spawnedEntities);
}
Expand Down
26 changes: 19 additions & 7 deletions core/src/main/java/tc/oc/pgm/spawner/SpawnerDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,33 @@

import java.time.Duration;
import java.util.List;
import tc.oc.pgm.api.feature.FeatureDefinition;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import tc.oc.pgm.api.feature.FeatureInfo;
import tc.oc.pgm.api.filter.Filter;
import tc.oc.pgm.api.region.Region;
import tc.oc.pgm.features.SelfIdentifyingFeatureDefinition;

@FeatureInfo(name = "spawner")
public class SpawnerDefinition implements FeatureDefinition {

public class SpawnerDefinition extends SelfIdentifyingFeatureDefinition {
public final Region spawnRegion;
public final Region playerRegion;
public final int maxEntities;
public final Duration minDelay, maxDelay, delay;
public final List<Spawnable> objects;
public final Filter playerFilter;
public final int numericID;

public SpawnerDefinition(
String id,
List<Spawnable> objects,
Region spawnRegion,
Region playerRegion,
Filter playerFilter,
Duration delay,
Duration minDelay,
Duration maxDelay,
int maxEntities,
int numericID) {
int maxEntities) {
super(id);
this.spawnRegion = spawnRegion;
this.playerRegion = playerRegion;
this.maxEntities = maxEntities;
Expand All @@ -36,6 +37,17 @@ public SpawnerDefinition(
this.delay = delay;
this.objects = objects;
this.playerFilter = playerFilter;
this.numericID = numericID;
}

@Override
protected String getDefaultId() {
return super.makeDefaultId();
}

public static String makeDefaultId(@Nullable String name, AtomicInteger serial) {
return "--"
+ makeTypeName(SpawnerDefinition.class)
+ "-"
+ (name != null ? makeId(name) : serial.getAndIncrement());
}
}
71 changes: 51 additions & 20 deletions core/src/main/java/tc/oc/pgm/spawner/SpawnerModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionType;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
Expand All @@ -24,6 +27,8 @@
import tc.oc.pgm.regions.RegionModule;
import tc.oc.pgm.regions.RegionParser;
import tc.oc.pgm.spawner.objects.SpawnableItem;
import tc.oc.pgm.spawner.objects.SpawnablePotion;
import tc.oc.pgm.util.xml.InheritingElement;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.XMLUtils;

Expand All @@ -49,58 +54,84 @@ public SpawnerModule parse(MapFactory factory, Logger logger, Document doc)
RegionParser regionParser = factory.getRegions();
KitParser kitParser = factory.getKits();
FilterParser filterParser = factory.getFilters();
AtomicInteger postIdSerial = new AtomicInteger(1);
CoWinkKeyDinkInc marked this conversation as resolved.
Show resolved Hide resolved

int numericID = 0;
for (Element element :
for (Element spawnerEl :
XMLUtils.flattenElements(doc.getRootElement(), "spawners", "spawner")) {
Region spawnRegion = regionParser.parseRequiredRegionProperty(element, "spawn-region");
Region playerRegion = regionParser.parseRequiredRegionProperty(element, "player-region");
Attribute delayAttr = element.getAttribute("delay");
Attribute minDelayAttr = element.getAttribute("min-delay");
Attribute maxDelayAttr = element.getAttribute("max-delay");
String id = spawnerEl.getAttributeValue("id");
Region spawnRegion = regionParser.parseRequiredRegionProperty(spawnerEl, "spawn-region");
Region playerRegion = regionParser.parseRequiredRegionProperty(spawnerEl, "player-region");
Attribute delayAttr = spawnerEl.getAttribute("delay");
Attribute minDelayAttr = spawnerEl.getAttribute("min-delay");
Attribute maxDelayAttr = spawnerEl.getAttribute("max-delay");

if (id == null) id = SpawnerDefinition.makeDefaultId("spawner", postIdSerial);
CoWinkKeyDinkInc marked this conversation as resolved.
Show resolved Hide resolved

if ((minDelayAttr != null || maxDelayAttr != null) && delayAttr != null) {
throw new InvalidXMLException(
"Attribute 'minDelay' and 'maxDelay' cannot be combined with 'delay'", element);
"Attribute 'minDelay' and 'maxDelay' cannot be combined with 'delay'", spawnerEl);
}

Duration delay = XMLUtils.parseDuration(delayAttr, Duration.ofSeconds(10));
Duration minDelay = XMLUtils.parseDuration(minDelayAttr, delay);
Duration maxDelay = XMLUtils.parseDuration(maxDelayAttr, delay);

if (maxDelay.compareTo(minDelay) <= 0 && minDelayAttr != null && maxDelayAttr != null) {
throw new InvalidXMLException("Max-delay must be longer than min-delay", element);
throw new InvalidXMLException("Max-delay must be longer than min-delay", spawnerEl);
}

int maxEntities =
XMLUtils.parseNumber(
element.getAttribute("max-entities"), Integer.class, Integer.MAX_VALUE);
spawnerEl.getAttribute("max-entities"), Integer.class, Integer.MAX_VALUE);
Filter playerFilter =
filterParser.parseFilterProperty(element, "filter", StaticFilter.ALLOW);
filterParser.parseFilterProperty(spawnerEl, "filter", StaticFilter.ALLOW);

List<Spawnable> objects = new ArrayList<>();
for (Element spawnable :
XMLUtils.getChildren(
element, "item")) { // TODO Add more types of spawnables once entity parser is built
ItemStack stack = kitParser.parseItem(spawnable, false);
SpawnableItem item = new SpawnableItem(stack, numericID);
for (Element itemEl : XMLUtils.getChildren(spawnerEl, "item")) {
ItemStack stack = kitParser.parseItem(itemEl, false);
SpawnableItem item = new SpawnableItem(stack, id);
objects.add(item);
}

for (Element potionEl : XMLUtils.getChildren(spawnerEl, "potion")) {
ImmutableList.Builder<PotionEffect> effectsBuilder = ImmutableList.builder();
for (Element potionChild : potionEl.getChildren("effect")) {
effectsBuilder.add(XMLUtils.parsePotionEffect(new InheritingElement(potionChild)));
}
ImmutableList<PotionEffect> effects = effectsBuilder.build();
if (effects.isEmpty()) {
throw new InvalidXMLException("Expected child effects, but found none", spawnerEl);
}
int damageValue = 0;
if (potionEl.getAttribute("damage") != null) {
damageValue = XMLUtils.parseNumber(potionEl.getAttribute("damage"), Integer.class, 0);
} else {
for (PotionEffect potionEffect : effects) {
// PotionType lists "true" potions, PotionEffectType "potionEffect.getType()" lists
// all possible status effects (ie wither, blindness, etc)
// Use the first listed PotionType for potion color
if (PotionType.getByEffect(potionEffect.getType()) != null) {
damageValue = PotionType.getByEffect(potionEffect.getType()).getDamageValue();
break;
}
}
}
objects.add(new SpawnablePotion(effects, damageValue, id));
}

SpawnerDefinition spawnerDefinition =
new SpawnerDefinition(
id,
objects,
spawnRegion,
playerRegion,
playerFilter,
delay,
minDelay,
maxDelay,
maxEntities,
numericID);
factory.getFeatures().addFeature(element, spawnerDefinition);
maxEntities);
factory.getFeatures().addFeature(spawnerEl, spawnerDefinition);
Pablete1234 marked this conversation as resolved.
Show resolved Hide resolved
spawnerModule.spawnerDefinitions.add(spawnerDefinition);
numericID++;
}

return spawnerModule.spawnerDefinitions.isEmpty() ? null : spawnerModule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@

public class SpawnableItem implements Spawnable {

private ItemStack stack;
private String METADATA_VALUE;
private final ItemStack stack;
private final String spawnerId;

public SpawnableItem(ItemStack stack, int spawnerID) {
public SpawnableItem(ItemStack stack, String spawnerId) {
this.stack = stack;
this.METADATA_VALUE = Integer.toString(spawnerID);
this.spawnerId = spawnerId;
}

@Override
public void spawn(Location location, Match match) {
Item item = location.getWorld().dropItem(location, stack);
item.setMetadata(Spawner.METADATA_KEY, new FixedMetadataValue(PGM.get(), METADATA_VALUE));
item.setMetadata(Spawner.METADATA_KEY, new FixedMetadataValue(PGM.get(), spawnerId));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package tc.oc.pgm.spawner.objects;

import java.util.List;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionEffect;
import tc.oc.pgm.api.PGM;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.spawner.Spawnable;
import tc.oc.pgm.spawner.Spawner;
import tc.oc.pgm.util.nms.NMSHacks;

public class SpawnablePotion implements Spawnable {
private final ItemStack potionItem;
private final String spawnerId;

public SpawnablePotion(List<PotionEffect> potion, int damageValue, String spawnerId) {
this.spawnerId = spawnerId;
// Potion "name" determines potion color
ItemStack potionItem = new Potion(damageValue).splash().toItemStack(1);
PotionMeta potionMeta = (PotionMeta) potionItem.getItemMeta();
for (PotionEffect effect : potion) {
potionMeta.addCustomEffect(effect, false);
}
potionItem.setItemMeta(potionMeta);
this.potionItem = potionItem;
}

@Override
public void spawn(Location location, Match match) {
NMSHacks.EntityPotion entityPotion = new NMSHacks.EntityPotion(location, potionItem);
entityPotion
.getBukkitEntity()
.setMetadata(Spawner.METADATA_KEY, new FixedMetadataValue(PGM.get(), spawnerId));
entityPotion.spawn();
}

@Override
public int getSpawnCount() {
return potionItem.getAmount();
}
}
15 changes: 15 additions & 0 deletions util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,21 @@ protected Packet<?> spawnPacket() {
public void wear(Player viewer, int slot, ItemStack item) {}
}

class EntityPotion extends net.minecraft.server.v1_8_R3.EntityPotion {
public EntityPotion(Location location, ItemStack potionItem) {
super(
((CraftWorld) location.getWorld()).getHandle(),
location.getX(),
location.getY(),
location.getZ(),
CraftItemStack.asNMSCopy(potionItem));
}

public void spawn() {
world.addEntity(this);
}
}

static void setFireworksExpectedLifespan(Firework firework, int ticks) {
((CraftFirework) firework).getHandle().expectedLifespan = ticks;
}
Expand Down