Skip to content

Commit 6103e2d

Browse files
committed
Improve mob tagging and instancing
1 parent 75ff36e commit 6103e2d

8 files changed

Lines changed: 394 additions & 98 deletions

File tree

src/main/java/de/erethon/aether/Aether.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import de.erethon.aether.creature.SkinCache;
77
import de.erethon.aether.listener.EntityListener;
88
import de.erethon.aether.listener.PlayerListener;
9-
import de.erethon.aether.qxl.actions.MobWalkAction;
9+
import de.erethon.aether.qxl.actions.HideMobAction;
10+
import de.erethon.aether.qxl.actions.WalkMobAction;
11+
import de.erethon.aether.qxl.actions.ShowMobAction;
1012
import de.erethon.aether.qxl.actions.SpawnMobAction;
1113
import de.erethon.aether.qxl.actions.SpawnerAction;
1214
import de.erethon.aether.qxl.objectives.InteractMobObjective;
@@ -26,7 +28,6 @@
2628
import de.erethon.questsxl.common.QRegistries;
2729
import net.kyori.adventure.text.Component;
2830
import net.kyori.adventure.text.format.NamedTextColor;
29-
import net.minecraft.world.entity.EntityType;
3031
import org.bukkit.Bukkit;
3132
import org.bukkit.NamespacedKey;
3233
import org.bukkit.entity.Player;
@@ -43,7 +44,6 @@
4344
import java.util.ArrayList;
4445
import java.util.List;
4546
import java.util.Locale;
46-
import java.util.Map;
4747

4848
public final class Aether extends EPlugin implements Listener {
4949

@@ -163,7 +163,9 @@ public void onEnable() {
163163
if (questsXL != null) {
164164
questsXL.registerComponent(QRegistries.ACTIONS, "spawn_mob", SpawnMobAction::new);
165165
questsXL.registerComponent(QRegistries.ACTIONS, "spawner", SpawnerAction::new);
166-
questsXL.registerComponent(QRegistries.ACTIONS, "mob_walk", MobWalkAction::new);
166+
questsXL.registerComponent(QRegistries.ACTIONS, "walk_mob", WalkMobAction::new);
167+
questsXL.registerComponent(QRegistries.ACTIONS, "show_mob", ShowMobAction::new);
168+
questsXL.registerComponent(QRegistries.ACTIONS, "hide_mob", HideMobAction::new);
167169
questsXL.registerComponent(QRegistries.OBJECTIVES, "kill_mob", KillMobObjective::new);
168170
questsXL.registerComponent(QRegistries.OBJECTIVES, "interact_mob", InteractMobObjective::new);
169171
}

src/main/java/de/erethon/aether/creature/AetherBaseMob.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@
5656
import net.minecraft.world.entity.Pose;
5757
import net.minecraft.world.entity.ai.attributes.Attribute;
5858
import net.minecraft.world.entity.ai.attributes.Attributes;
59+
import net.minecraft.world.entity.ai.behavior.StayCloseToTarget;
5960
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
61+
import net.minecraft.world.entity.ai.goal.MoveTowardsRestrictionGoal;
6062
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
6163
import net.minecraft.world.entity.ai.goal.RandomStrollGoal;
6264
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
@@ -144,6 +146,9 @@ public AetherBaseMob(NPCData data, World world, Integer overrideLevel) {
144146

145147
public void addToWorld() {
146148
ServerLevel level = (ServerLevel) level();
149+
if (data.isInstancable()) {
150+
getBukkitLivingEntity().setVisibleByDefault(false);
151+
}
147152
level.addFreshEntity(this);
148153
}
149154

@@ -343,7 +348,7 @@ public void die(DamageSource damageSource) {
343348
holder.onDeath();
344349
}
345350
if (mobTag != null) {
346-
plugin.getCreatureManager().removeTaggedMob(mobTag);
351+
plugin.getCreatureManager().removeTaggedMob(mobTag, this);
347352
}
348353
}
349354

@@ -629,6 +634,8 @@ private void registerAetherGoals() {
629634
}
630635
targetSelector.addGoal(aeGoal.getPrio(), aeGoal.get(this));
631636
}
637+
// Always add the "go home" goal, it won't do anything if no home is set
638+
goalSelector.addGoal(16, new MoveTowardsRestrictionGoal(this, 1.2f));
632639
}
633640

634641
@Override

src/main/java/de/erethon/aether/creature/CreatureManager.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
public class CreatureManager {
1717

1818
private Set<NPCData> creatures = new HashSet<>();
19-
private Map<String, AetherBaseMob> activeTaggedMobs = new HashMap<>();
19+
private Map<String, Set<AetherBaseMob>> activeTaggedMobs = new HashMap<>();
2020

2121
public CreatureManager() {
2222
load();
@@ -45,17 +45,50 @@ public List<String> getIDs() {
4545
}
4646

4747
public void addTaggedMob(String tag, AetherBaseMob mob) {
48-
activeTaggedMobs.put(tag, mob);
48+
activeTaggedMobs.computeIfAbsent(tag, k -> new HashSet<>()).add(mob);
4949
}
5050

51+
/**
52+
* Get the first mob with the specified tag.
53+
* @param tag The tag to search for
54+
* @return The first mob with the tag, or null if none exist
55+
*/
5156
public AetherBaseMob getTaggedMob(String tag) {
52-
return activeTaggedMobs.get(tag);
57+
Set<AetherBaseMob> mobs = activeTaggedMobs.get(tag);
58+
if (mobs == null || mobs.isEmpty()) {
59+
return null;
60+
}
61+
return mobs.iterator().next();
62+
}
63+
64+
/**
65+
* Get all mobs with the specified tag.
66+
* @param tag The tag to search for
67+
* @return A set of all mobs with the tag, or an empty set if none exist
68+
*/
69+
public Set<AetherBaseMob> getTaggedMobs(String tag) {
70+
return activeTaggedMobs.getOrDefault(tag, new HashSet<>());
5371
}
5472

5573
public void removeTaggedMob(String tag) {
5674
activeTaggedMobs.remove(tag);
5775
}
5876

77+
/**
78+
* Remove a specific mob from a tag.
79+
* @param tag The tag to remove the mob from
80+
* @param mob The specific mob to remove
81+
*/
82+
public void removeTaggedMob(String tag, AetherBaseMob mob) {
83+
Set<AetherBaseMob> mobs = activeTaggedMobs.get(tag);
84+
if (mobs != null) {
85+
mobs.remove(mob);
86+
if (mobs.isEmpty()) {
87+
activeTaggedMobs.remove(tag);
88+
}
89+
}
90+
}
91+
5992
public void reload() {
6093
creatures.clear();
6194
load();
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package de.erethon.aether.qxl.actions;
2+
3+
import de.erethon.aether.Aether;
4+
import de.erethon.aether.creature.AetherBaseMob;
5+
import de.erethon.aether.creature.CreatureManager;
6+
import de.erethon.questsxl.QuestsXL;
7+
import de.erethon.questsxl.action.QBaseAction;
8+
import de.erethon.questsxl.common.QConfig;
9+
import de.erethon.questsxl.common.QLoadableDoc;
10+
import de.erethon.questsxl.common.QParamDoc;
11+
import de.erethon.questsxl.common.Quester;
12+
import de.erethon.questsxl.error.FriendlyError;
13+
import de.erethon.questsxl.livingworld.QEvent;
14+
import de.erethon.questsxl.player.QPlayer;
15+
import org.bukkit.Bukkit;
16+
import org.bukkit.entity.Player;
17+
18+
import java.util.Set;
19+
20+
@QLoadableDoc(
21+
value = "hide_mob",
22+
description = "Hides tagged mobs. They have to be set to `instanceable` for this to work.",
23+
shortExample = "hide_mob: tag=bandit; mode=event_in_range",
24+
longExample = {
25+
"hide_mob:",
26+
" tag: bandit",
27+
}
28+
)
29+
public class HideMobAction extends QBaseAction {
30+
31+
Aether aether = Aether.getInstance();
32+
CreatureManager creatureManager = aether.getCreatureManager();
33+
34+
@QParamDoc(name = "tag" , description = "The mob tag to show")
35+
String tag = null;
36+
37+
@QParamDoc(name = "mode", description = "The show mode. Possible values: ALL, SELF, GROUP, EVENT_IN_RANGE, EVENT_PARTICIPANTS. Default is ALL")
38+
ShowMode mode = ShowMode.ALL;
39+
40+
41+
@Override
42+
public void play(Quester quester) {
43+
if (!conditions(quester)) return;
44+
try {
45+
Set<AetherBaseMob> mobs = creatureManager.getTaggedMobs(tag);
46+
if (mobs == null) {
47+
throw new IllegalArgumentException("No mobs with tag " + tag + " found.");
48+
}
49+
for (AetherBaseMob mob : mobs) {
50+
if (quester instanceof QEvent event) {
51+
switch (mode) {
52+
case EVENT_IN_RANGE -> execute(quester, player -> player.getPlayer().hideEntity(Aether.getInstance(), mob.getBukkitLivingEntity()));
53+
case EVENT_PARTICIPANTS -> {
54+
for (QPlayer player : event.getParticipants().keySet()) {
55+
player.getPlayer().hideEntity(Aether.getInstance(), mob.getBukkitLivingEntity());
56+
}
57+
}
58+
case ALL -> {
59+
for (Player player : Bukkit.getOnlinePlayers()) {
60+
player.hideEntity(Aether.getInstance(), mob.getBukkitLivingEntity());
61+
}
62+
}
63+
default -> {
64+
// Not applicable to events
65+
}
66+
}
67+
}
68+
if (quester instanceof QPlayer qPlayer) {
69+
switch (mode) {
70+
case SELF -> qPlayer.getPlayer().hideEntity(Aether.getInstance(), mob.getBukkitLivingEntity());
71+
case GROUP -> {
72+
// TODO, need to implement groups with Aergia
73+
}
74+
case ALL -> {
75+
for (Player player : Bukkit.getOnlinePlayers()) {
76+
player.hideEntity(Aether.getInstance(), mob.getBukkitLivingEntity());
77+
}
78+
}
79+
default -> {
80+
// Not applicable to players
81+
}
82+
}
83+
}
84+
}
85+
}
86+
catch (Exception e) {
87+
FriendlyError error = new FriendlyError(id,"Failed to hide mob", e.getMessage(), "Mob tag: " + tag).addStacktrace(e.getStackTrace());
88+
QuestsXL.get().addRuntimeError(error);
89+
}
90+
onFinish(quester);
91+
}
92+
93+
94+
@Override
95+
public void load(QConfig cfg) {
96+
super.load(cfg);
97+
tag = cfg.getString("tag", null);
98+
String modeString = cfg.getString("mode", "ALL").toUpperCase();
99+
try {
100+
mode = ShowMode.valueOf(modeString);
101+
} catch (IllegalArgumentException e) {
102+
throw new IllegalArgumentException("Invalid mode: " + modeString + ". Valid modes are: ALL, SELF, GROUP, EVENT_IN_RANGE, EVENT_PARTICIPANTS.");
103+
}
104+
if (tag == null) {
105+
throw new IllegalArgumentException("Tag cannot be null.");
106+
}
107+
}
108+
}

src/main/java/de/erethon/aether/qxl/actions/MobWalkAction.java

Lines changed: 0 additions & 90 deletions
This file was deleted.

0 commit comments

Comments
 (0)