Skip to content

Commit

Permalink
Add splash potions to spawner module (#1002)
Browse files Browse the repository at this point in the history
Signed-off-by: Patrick <[email protected]>
  • Loading branch information
CoWinkKeyDinkInc authored May 22, 2022
1 parent 13b0a94 commit 6003eff
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 70 deletions.
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);

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);

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);
spawnerModule.spawnerDefinitions.add(spawnerDefinition);
numericID++;
}

return spawnerModule.spawnerDefinitions.isEmpty() ? null : spawnerModule;
Expand Down
10 changes: 5 additions & 5 deletions core/src/main/java/tc/oc/pgm/spawner/objects/SpawnableItem.java
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
45 changes: 45 additions & 0 deletions core/src/main/java/tc/oc/pgm/spawner/objects/SpawnablePotion.java
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

0 comments on commit 6003eff

Please sign in to comment.