From af4010f770d67d8fa8007930583fb918a37b570f Mon Sep 17 00:00:00 2001 From: totemo Date: Sat, 18 Apr 2020 00:01:25 +0930 Subject: [PATCH] Prevent mobs targeting mobs in their friends group. New/changed mob type properties: * Comma- or space-separated lists of tag strings are now merged into sorted sets with case-insensitive string comparison, for properties `tags`, `groups` (new) and `friend-groups` (new). * `groups` is the set of groups a mob type belongs to. * `friend-groups` is the set of groups that the mob type refuses to target. --- src/nu/nerd/beastmaster/BeastMaster.java | 41 ++++++++++++++++++++++ src/nu/nerd/beastmaster/mobs/DataType.java | 28 +++++++++------ src/nu/nerd/beastmaster/mobs/MobType.java | 13 ++++--- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/src/nu/nerd/beastmaster/BeastMaster.java b/src/nu/nerd/beastmaster/BeastMaster.java index 5b19987..74dd078 100644 --- a/src/nu/nerd/beastmaster/BeastMaster.java +++ b/src/nu/nerd/beastmaster/BeastMaster.java @@ -1,7 +1,9 @@ package nu.nerd.beastmaster; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import org.bukkit.Bukkit; @@ -29,6 +31,7 @@ import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityTargetLivingEntityEvent; import org.bukkit.event.entity.EntityTeleportEvent; import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; @@ -742,6 +745,44 @@ protected void onEntityDeath(EntityDeathEvent event) { } // onEntityDeath + // ------------------------------------------------------------------------ + /** + * Prevent a mob from targeting any other mob whose "groups" set includes a + * name that is in the targeting mob's "friend-groups". + */ + @SuppressWarnings("unchecked") + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + protected void onEntityTargetLivingEntity(EntityTargetLivingEntityEvent event) { + Entity targetter = event.getEntity(); + LivingEntity target = event.getTarget(); + if (!(targetter instanceof LivingEntity)) { + return; + } + + // If the targeting mob is not friendly to any groups, no action needed. + MobType mobType = getMobType(targetter); + if (mobType == null) { + return; + } + Set friendGroups = (Set) mobType.getDerivedProperty("friend-groups").getValue(); + if (friendGroups == null || friendGroups.isEmpty()) { + return; + } + + // Targeted mob. + MobType targetType = getMobType(target); + Set targetGroups = targetType == null ? Collections.EMPTY_SET + : (Set) targetType.getDerivedProperty("groups").getValue(); + + // If the targeted mob is a friend, don't target. + for (String friend : friendGroups) { + if (targetGroups.contains(friend)) { + event.setCancelled(true); + return; + } + } + } + // ------------------------------------------------------------------------ /** * Play the teleport-sound, if configured for the mob. diff --git a/src/nu/nerd/beastmaster/mobs/DataType.java b/src/nu/nerd/beastmaster/mobs/DataType.java index b858631..96b76a1 100644 --- a/src/nu/nerd/beastmaster/mobs/DataType.java +++ b/src/nu/nerd/beastmaster/mobs/DataType.java @@ -2,6 +2,10 @@ import java.lang.reflect.InvocationTargetException; import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -103,17 +107,15 @@ public int compare(Object o1, Object o2) { // ------------------------------------------------------------------------ /** - * Comma separated list of Strings assumed to be scoreboard tags. + * Comma separated ordered set of case-insensitive Strings. * * Spaces are treated as equivalent to commas. - * - * Tags are compared case-sensitively. I assume that it might occasionally - * matter for scoreboard tags. */ - public static final IDataType TAG_LIST = new IDataType() { + public static final IDataType TAG_SET = new IDataType() { + @SuppressWarnings("unchecked") @Override public String format(Object value) { - return String.join(",", (String[]) value); + return ((Set) value).stream().collect(Collectors.joining(",")); } @Override @@ -128,15 +130,19 @@ public String serialise(Object value) { @Override public Object deserialise(String value) throws IllegalArgumentException { - // Sort to account for hand-edited configs. - String[] tags = value.split(","); - Arrays.sort(tags); - return tags; + Set set = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + // Handle input like: "," by removing empty tags. + List tags = Arrays.asList(value.split(",")).stream() + .filter(tag -> !tag.isEmpty()) + .sorted(String.CASE_INSENSITIVE_ORDER) + .collect(Collectors.toList()); + set.addAll(tags); + return set; } @Override public int compare(Object o1, Object o2) { - return serialise(o1).compareTo(serialise(o2)); + return serialise(o1).compareToIgnoreCase(serialise(o2)); } }; diff --git a/src/nu/nerd/beastmaster/mobs/MobType.java b/src/nu/nerd/beastmaster/mobs/MobType.java index 2a78acf..2e954a4 100644 --- a/src/nu/nerd/beastmaster/mobs/MobType.java +++ b/src/nu/nerd/beastmaster/mobs/MobType.java @@ -622,6 +622,14 @@ protected void addProperties() { // Behaviour ---------------------------------------------------------- + addProperty(new MobProperty("groups", DataType.TAG_SET, null)); + addProperty(new MobProperty("friend-groups", DataType.TAG_SET, null)); + addProperty(new MobProperty("tags", DataType.TAG_SET, (mob, logger) -> { + @SuppressWarnings("unchecked") + Set tags = (Set) getDerivedProperty("tags").getValue(); + mob.getScoreboardTags().addAll(tags); + })); + addProperty(new MobProperty("anger-ticks", DataType.INTEGER, (mob, logger) -> { int ticks = (Integer) getDerivedProperty("anger-ticks").getValue(); if (mob instanceof Bee) { @@ -641,11 +649,6 @@ protected void addProperties() { boolean canDespawn = (Boolean) getDerivedProperty("can-despawn").getValue(); mob.setRemoveWhenFarAway(canDespawn); })); - addProperty(new MobProperty("tags", DataType.TAG_LIST, (mob, logger) -> { - String[] tags = (String[]) getDerivedProperty("tags").getValue(); - mob.getScoreboardTags().addAll(Arrays.asList(tags)); - })); - // projectile-... properties are enforced in ProjectileLaunchEvent and // ProectileHitEvent handlers. addProperty(new MobProperty("projectile-mobs", DataType.LOOT_OR_MOB, null));