/
EntityDeathScriptEvent.java
221 lines (206 loc) · 8.81 KB
/
EntityDeathScriptEvent.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package com.denizenscript.denizen.events.entity;
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.objects.ItemTag;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
import com.denizenscript.denizen.events.BukkitScriptEvent;
import com.denizenscript.denizencore.objects.*;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.core.ListTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public class EntityDeathScriptEvent extends BukkitScriptEvent implements Listener {
// <--[event]
// @Events
// <entity> dies|death
//
// @Cancellable true
//
// @Group Entity
//
// @Location true
// @Switch by:<entity> to only process the event if the killer is known and matches the specified entity matcher.
// @Switch cause:<cause> to only process the event if it was caused by a specific damage cause.
//
// @Triggers when an entity dies. Note that this fires *after* the entity dies, and thus some data may be lost from the entity.
// The death can only be cancelled on Paper.
//
// @Context
// <context.entity> returns the EntityTag that died.
// <context.damager> returns the EntityTag damaging the other entity, if any.
// <context.projectile> returns the EntityTag of a projectile used to kill the entity, if one was used.
// <context.message> returns an ElementTag of a player's death message.
// <context.cause> returns an ElementTag of the cause of the death. See <@link language damage cause> for a list of possible damage causes.
// <context.drops> returns a ListTag of all pending item drops.
// <context.xp> returns an ElementTag of the amount of experience to be dropped.
//
// @Determine
// ElementTag to change the death message.
// "NO_DROPS" to specify that any drops should be removed.
// "NO_XP" to specify that any XP orbs should be removed.
// ListTag(ItemTag) to specify new items to be dropped.
// ElementTag(Number) to specify the new amount of XP to be dropped.
// "KEEP_INV" to specify (if a player death) that the inventory should be kept.
// "KEEP_LEVEL" to specify (if a player death) that the XP level should be kept.
// "NO_MESSAGE" to hide a player death message.
//
// @Player when the entity that died is a player.
//
// @NPC when the entity that died is an NPC.
//
// -->
public EntityDeathScriptEvent() {
instance = this;
registerCouldMatcher("<entity> dies|death");
registerSwitches("by", "cause");
}
public static EntityDeathScriptEvent instance;
public EntityTag entity;
public EntityTag damager;
public EntityTag projectile;
public ElementTag cause;
public EntityDeathEvent event;
@Override
public boolean matches(ScriptPath path) {
String target = path.eventArgLowerAt(0);
if (!entity.tryAdvancedMatcher(target)) {
return false;
}
if (!runInCheck(path, entity.getLocation())) {
return false;
}
if (path.switches.containsKey("by") && (damager == null || !damager.tryAdvancedMatcher(path.switches.get("by")))) {
return false;
}
if (!runGenericSwitchCheck(path, "cause", cause == null ? null : cause.asString())) {
return false;
}
return super.matches(path);
}
@Override
public String getName() {
return "EntityDies";
}
@Override
public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
String determination = determinationObj.toString();
String lower = CoreUtilities.toLowerCase(determination);
if (lower.startsWith("drops ")) { // legacy drops determination format
lower = lower.substring(6);
determination = determination.substring(6);
}
if (lower.startsWith("no_drops")) {
event.getDrops().clear();
if (lower.endsWith("_or_xp")) {
event.setDroppedExp(0);
}
return true;
}
else if (lower.equals("no_xp")) {
event.setDroppedExp(0);
return true;
}
else if (lower.equals("keep_inv") && event instanceof PlayerDeathEvent) {
((PlayerDeathEvent) event).setKeepInventory(true);
return true;
}
else if (lower.equals("keep_level") && event instanceof PlayerDeathEvent) {
((PlayerDeathEvent) event).setKeepLevel(true);
return true;
}
else if (lower.equals("no_message") && event instanceof PlayerDeathEvent) {
((PlayerDeathEvent) event).setDeathMessage(null);
return true;
}
else if (determinationObj instanceof ElementTag && ((ElementTag) determinationObj).isInt()) {
event.setDroppedExp(((ElementTag) determinationObj).asInt());
return true;
}
else if (Argument.valueOf(lower).matchesArgumentList(ItemTag.class)) {
List<ItemStack> drops = event.getDrops();
drops.clear();
for (ItemTag item : ListTag.getListFor(determinationObj, getTagContext(path)).filter(ItemTag.class, getTagContext(path), true)) {
if (item != null) {
drops.add(item.getItemStack());
}
}
return true;
}
else if (event instanceof PlayerDeathEvent) {
((PlayerDeathEvent) event).setDeathMessage(determination);
return true;
}
else {
return super.applyDetermination(path, determinationObj);
}
}
@Override
public ScriptEntryData getScriptEntryData() {
return new BukkitScriptEntryData(entity);
}
@Override
public ObjectTag getContext(String name) {
switch (name) {
case "entity": return entity.getDenizenObject();
case "projectile": return projectile == null ? null : projectile.getDenizenObject();
case "damager": return damager == null ? null : damager.getDenizenObject();
case "message": return event instanceof PlayerDeathEvent ? new ElementTag(((PlayerDeathEvent) event).getDeathMessage()) : null;
case "cause": return cause;
case "xp": return new ElementTag(event.getDroppedExp());
case "drops":
ListTag list = new ListTag();
for (ItemStack stack : event.getDrops()) {
list.addObject(new ItemTag(stack));
}
return list;
}
return super.getContext(name);
}
@Override
public void cancellationChanged() {
if (cancelled && event instanceof PlayerDeathEvent) {
((PlayerDeathEvent) event).setDeathMessage(null); // Historical no_message was by cancelling.
}
super.cancellationChanged();
}
@EventHandler
public void onEntityDeath(EntityDeathEvent event) {
LivingEntity livingEntity = event.getEntity();
EntityTag.rememberEntity(livingEntity);
entity = new EntityTag(livingEntity);
cause = null;
damager = null;
projectile = null;
EntityDamageEvent lastDamage = entity.getBukkitEntity().getLastDamageCause();
if (lastDamage != null) {
cause = new ElementTag(event.getEntity().getLastDamageCause().getCause().toString());
if (lastDamage instanceof EntityDamageByEntityEvent) {
damager = new EntityTag(((EntityDamageByEntityEvent) lastDamage).getDamager());
EntityTag shooter = damager.getShooter();
if (damager instanceof Projectile) {
projectile = damager;
}
if (shooter != null) {
projectile = damager;
damager = shooter;
}
}
else if (livingEntity.getKiller() != null) {
damager = new EntityTag(livingEntity.getKiller());
}
}
cancelled = false;
this.event = event;
fire(event);
EntityTag.forgetEntity(livingEntity);
}
}