-
-
Notifications
You must be signed in to change notification settings - Fork 103
/
EntityHelper.java
502 lines (429 loc) · 18.5 KB
/
EntityHelper.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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
package com.denizenscript.denizen.nms.interfaces;
import com.denizenscript.denizen.events.entity.EntityEntersVehicleScriptEvent;
import com.denizenscript.denizen.events.entity.EntityExitsVehicleScriptEvent;
import com.denizenscript.denizen.nms.util.jnbt.CompoundTag;
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.objects.LocationTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.core.MapTag;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.*;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import java.util.List;
import java.util.UUID;
public abstract class EntityHelper {
public abstract void setInvisible(Entity entity, boolean invisible);
public abstract boolean isInvisible(Entity entity);
public abstract void setPose(Entity entity, Pose pose);
public void setSneaking(Entity entity, boolean sneak) {
if (entity instanceof Player player) {
player.setSneaking(sneak);
}
setPose(entity, sneak ? Pose.SNEAKING : Pose.STANDING);
}
public abstract double getDamageTo(LivingEntity attacker, Entity target);
public abstract void setRiptide(Entity entity, boolean state);
public abstract void forceInteraction(Player player, Location location);
public abstract CompoundTag getNbtData(Entity entity);
public abstract void setNbtData(Entity entity, CompoundTag compoundTag);
public abstract void stopFollowing(Entity follower);
public abstract void stopWalking(Entity entity);
public abstract void follow(final Entity target, final Entity follower, final double speed, final double lead,
final double maxRange, final boolean allowWander, final boolean teleport);
public abstract void walkTo(final LivingEntity entity, Location location, Double speed, final Runnable callback);
public abstract void sendHidePacket(Player pl, Entity entity);
public abstract void sendShowPacket(Player pl, Entity entity);
/**
* Rotates an entity.
*
* @param entity The Entity you want to rotate.
* @param yaw The new yaw of the entity.
* @param pitch The new pitch of the entity.
*/
public abstract void rotate(Entity entity, float yaw, float pitch);
public abstract float getBaseYaw(LivingEntity entity);
// Taken from C2 NMS class for less dependency on C2
public abstract void look(Entity entity, float yaw, float pitch);
public MapTag mapTrace(LivingEntity inputEntity) {
double range = 200;
Location start = inputEntity.getEyeLocation();
Vector startVec = start.toVector();
Vector direction = start.getDirection();
double bestDist = Double.MAX_VALUE;
ItemFrame best = null;
Vector bestHitPos = null;
BlockFace bestHitFace = null;
for (Entity entity : start.getWorld().getNearbyEntities(start.clone().add(direction.clone().multiply(50)), 100, 100, 100, (e) -> e instanceof ItemFrame itemFrame && itemFrame.getItem().getType() == Material.FILLED_MAP)) {
double centerDist = entity.getLocation().distanceSquared(start);
if (centerDist > bestDist) {
continue;
}
ItemFrame frame = (ItemFrame) entity;
double EXP_RATE = 0.125;
double expandX = 0, expandY = 0, expandZ = 0;
BlockFace face = frame.getFacing();
switch (face) {
case SOUTH, NORTH -> {
expandX = EXP_RATE;
expandY = EXP_RATE;
}
case EAST, WEST -> {
expandZ = EXP_RATE;
expandY = EXP_RATE;
}
case UP, DOWN -> {
expandX = EXP_RATE;
expandZ = EXP_RATE;
}
}
RayTraceResult traced = frame.getBoundingBox().expand(expandX, expandY, expandZ).rayTrace(startVec, direction, range);
if (traced == null || traced.getHitBlockFace() == null || traced.getHitBlockFace() != face) {
continue;
}
bestDist = centerDist;
best = frame;
bestHitPos = traced.getHitPosition();
bestHitFace = face;
}
if (best == null) {
return null;
}
double x = 0;
double y = 0;
double basex = bestHitPos.getX() - Math.floor(bestHitPos.getX());
double basey = bestHitPos.getY() - Math.floor(bestHitPos.getY());
double basez = bestHitPos.getZ() - Math.floor(bestHitPos.getZ());
switch (bestHitFace) {
case NORTH -> {
x = 128f - (basex * 128f);
y = 128f - (basey * 128f);
}
case SOUTH -> {
x = basex * 128f;
y = 128f - (basey * 128f);
}
case WEST -> {
x = basez * 128f;
y = 128f - (basey * 128f);
}
case EAST -> {
x = 128f - (basez * 128f);
y = 128f - (basey * 128f);
}
case UP -> {
x = basex * 128f;
y = basez * 128f;
}
case DOWN -> {
x = basex * 128f;
y = 128f - (basez * 128f);
}
}
MapMeta map = (MapMeta) best.getItem().getItemMeta();
switch (best.getRotation()) {
case CLOCKWISE_45, FLIPPED_45 -> { // 90 deg
double origX = x;
x = y;
y = 128f - origX;
}
case CLOCKWISE, COUNTER_CLOCKWISE -> { // 180 deg
x = 128f - x;
y = 128f - y;
}
case CLOCKWISE_135, COUNTER_CLOCKWISE_45 -> { // 270 deg
double origX2 = x;
x = 128f - y;
y = origX2;
}
}
MapTag result = new MapTag();
result.putObject("x", new ElementTag(Math.round(x)));
result.putObject("y", new ElementTag(Math.round(y)));
result.putObject("entity", new EntityTag(best));
result.putObject("map", new ElementTag(map.hasMapId() ? map.getMapId() : 0));
return result;
}
public abstract boolean canTrace(World world, Vector start, Vector end);
public Location faceLocation(Location from, Location at) {
Vector direction = from.toVector().subtract(at.toVector()).normalize();
Location newLocation = from.clone();
newLocation.setYaw(180 - (float) Math.toDegrees(Math.atan2(direction.getX(), direction.getZ())));
newLocation.setPitch(90 - (float) Math.toDegrees(Math.acos(direction.getY())));
return newLocation;
}
public boolean internalLook(Player player, Location at) {
return false;
}
/**
* Changes an entity's yaw and pitch to make it face a location.
*
* @param from The Entity whose yaw and pitch you want to change.
* @param at The Location it should be looking at.
*/
public void faceLocation(Entity from, Location at) {
if (from.getWorld() != at.getWorld()) {
return;
}
if (EntityTag.isPlayer(from)) {
if (internalLook((Player) from, at)) {
return;
}
}
Location origin = from instanceof LivingEntity livingEntity ? livingEntity.getEyeLocation()
: new LocationTag(from.getLocation()).getBlockLocation().add(0.5, 0.5, 0.5);
Location rotated = faceLocation(origin, at);
rotate(from, rotated.getYaw(), rotated.getPitch());
}
public boolean isFacingLocation(Location from, Location at, float yawLimitDegrees, float pitchLimitDegrees) {
Vector direction = from.toVector().subtract(at.toVector()).normalize();
float pitch = 90 - (float) Math.toDegrees(Math.acos(direction.getY()));
if (from.getPitch() > pitch + pitchLimitDegrees
|| from.getPitch() < pitch - pitchLimitDegrees) {
return false;
}
return isFacingLocation(from, at, yawLimitDegrees);
}
/**
* Checks if a Location's yaw is facing another Location.
* <p/>
* Note: do not use a player's location as the first argument,
* because player yaws need to modified. Use the method
* below this one instead.
*
* @param from The Location we check.
* @param at The Location we want to know if the first Location's yaw
* is facing
* @param degreeLimit How many degrees can be between the direction the
* first location's yaw is facing and the direction
* we check if it is facing.
* @return Returns a boolean.
*/
public boolean isFacingLocation(Location from, Location at, float degreeLimit) {
double currentYaw = normalizeYaw(from.getYaw());
double requiredYaw = normalizeYaw(getYaw(at.toVector().subtract(
from.toVector()).normalize()));
return (Math.abs(requiredYaw - currentYaw) < degreeLimit ||
Math.abs(requiredYaw + 360 - currentYaw) < degreeLimit ||
Math.abs(currentYaw + 360 - requiredYaw) < degreeLimit);
}
/**
* Checks if an Entity is facing a Location.
*
* @param from The Entity we check.
* @param at The Location we want to know if it is looking at.
* @param degreeLimit How many degrees can be between the direction the
* Entity is facing and the direction we check if it
* is facing.
* @return Returns a boolean.
*/
public boolean isFacingLocation(Entity from, Location at, float degreeLimit) {
return isFacingLocation(from.getLocation(), at, degreeLimit);
}
/**
* Checks if an Entity is facing another Entity.
*
* @param from The Entity we check.
* @param at The Entity we want to know if it is looking at.
* @param degreeLimit How many degrees can be between the direction the
* Entity is facing and the direction we check if it
* is facing.
* @return Returns a boolean.
*/
public boolean isFacingEntity(Entity from, Entity at, float degreeLimit) {
return isFacingLocation(from.getLocation(), at.getLocation(), degreeLimit);
}
/**
* Normalizes Mincraft's yaws (which can be negative or can exceed 360)
* by turning them into proper yaw values that only go from 0 to 359.
*
* @param yaw The original yaw.
* @return The normalized yaw.
*/
public static float normalizeYaw(float yaw) {
yaw = yaw % 360;
if (yaw < 0) {
yaw += 360.0;
}
return yaw;
}
/**
* Converts a vector to a yaw.
* <p/>
* Thanks to bergerkiller.
*
* @param vector The vector you want to get a yaw from.
* @return The yaw.
*/
public float getYaw(Vector vector) {
double dx = vector.getX();
double dz = vector.getZ();
double yaw = 0;
// Set yaw
if (dx != 0) {
// Set yaw start value based on dx
if (dx < 0) {
yaw = 1.5 * Math.PI;
}
else {
yaw = 0.5 * Math.PI;
}
yaw -= Math.atan(dz / dx);
}
else if (dz < 0) {
yaw = Math.PI;
}
return (float) (-yaw * 180 / Math.PI);
}
/**
* Converts a yaw to a cardinal direction name.
*
* @param yaw The yaw you want to get a cardinal direction from.
* @return The name of the cardinal direction as a String.
*/
public String getCardinal(float yaw) {
yaw = normalizeYaw(yaw);
// Compare yaws, return closest direction.
if (0 <= yaw && yaw < 22.5) {
return "south";
}
else if (22.5 <= yaw && yaw < 67.5) {
return "southwest";
}
else if (67.5 <= yaw && yaw < 112.5) {
return "west";
}
else if (112.5 <= yaw && yaw < 157.5) {
return "northwest";
}
else if (157.5 <= yaw && yaw < 202.5) {
return "north";
}
else if (202.5 <= yaw && yaw < 247.5) {
return "northeast";
}
else if (247.5 <= yaw && yaw < 292.5) {
return "east";
}
else if (292.5 <= yaw && yaw < 337.5) {
return "southeast";
}
else if (337.5 <= yaw && yaw < 360.0) {
return "south";
}
else {
return null;
}
}
public abstract void snapPositionTo(Entity entity, Vector vector);
public abstract void move(Entity entity, Vector vector);
public void fakeMove(Entity entity, Vector vector) {
throw new UnsupportedOperationException();
}
public void fakeTeleport(Entity entity, Location location) {
throw new UnsupportedOperationException();
}
public void clientResetLoc(Entity entity) {
throw new UnsupportedOperationException();
}
public abstract void teleport(Entity entity, Location loc);
public abstract void setBoundingBox(Entity entity, BoundingBox box);
public List<Player> getPlayersThatSee(Entity entity) { // TODO: once the minimum supported version is 1.20, remove from NMS
return List.copyOf(entity.getTrackedBy());
}
public void sendAllUpdatePackets(Entity entity) {
throw new UnsupportedOperationException();
}
public abstract void setTicksLived(Entity entity, int ticks);
public abstract void setHeadAngle(LivingEntity entity, float angle);
public void setGhastAttacking(Ghast ghast, boolean attacking) { // TODO: once minimum version is 1.19 or higher, remove from NMS
ghast.setCharging(attacking);
}
public abstract void setEndermanAngry(Enderman enderman, boolean angry);
public static EntityDamageEvent fireFakeDamageEvent(Entity target, EntityTag source, Location sourceLoc, EntityDamageEvent.DamageCause cause, float amount) {
EntityDamageEvent ede;
if (source != null) {
ede = new EntityDamageByEntityEvent(source.getBukkitEntity(), target, cause, amount);
}
else if (sourceLoc != null) {
ede = new EntityDamageByBlockEvent(sourceLoc.getBlock(), target, cause, amount);
}
else {
ede = new EntityDamageEvent(target, cause, amount);
}
Bukkit.getPluginManager().callEvent(ede);
return ede;
}
public abstract void damage(LivingEntity target, float amount, EntityTag source, Location sourceLoc, EntityDamageEvent.DamageCause cause);
public abstract void setLastHurtBy(LivingEntity mob, LivingEntity damager);
public abstract void setFallingBlockType(FallingBlock fallingBlock, BlockData block);
public abstract EntityTag getMobSpawnerDisplayEntity(CreatureSpawner spawner);
public void setFireworkLifetime(Firework firework, int ticks) { // TODO: once minimum version is 1.19, remove from NMS
firework.setMaxLife(ticks);
}
public int getFireworkLifetime(Firework firework) { // TODO: once minimum version is 1.19, remove from NMS
return firework.getMaxLife();
}
public abstract int getInWaterTime(Zombie zombie);
public abstract void setInWaterTime(Zombie zombie, int ticks);
public abstract void setTrackingRange(Entity entity, int range);
public abstract boolean isAggressive(Mob mob);
public abstract void setAggressive(Mob mob, boolean aggressive);
public void setUUID(Entity entity, UUID id) {
throw new UnsupportedOperationException();
}
public float getStepHeight(Entity entity) {
throw new UnsupportedOperationException();
}
public void setStepHeight(Entity entity, float stepHeight) {
throw new UnsupportedOperationException();
}
public List<Object> convertInternalEntityDataValues(Entity entity, MapTag internalData) {
throw new UnsupportedOperationException();
}
public void modifyInternalEntityData(Entity entity, MapTag internalData) {
throw new UnsupportedOperationException();
}
public void startUsingItem(LivingEntity entity, EquipmentSlot hand) {
throw new UnsupportedOperationException();
}
public void stopUsingItem(LivingEntity entity) {
throw new UnsupportedOperationException();
}
public abstract void openHorseInventory(Player player, AbstractHorse horse);
public CompoundTag getRawNBT(Entity entity) {
throw new UnsupportedOperationException();
}
public void modifyRawNBT(Entity entity, CompoundTag tag) {
throw new UnsupportedOperationException();
}
public static class EntityEntersVehicleScriptEventImpl extends EntityEntersVehicleScriptEvent {
@EventHandler
public void onEntityMount(EntityMountEvent event) {
fire(event, event.getMount());
}
}
public Class<? extends EntityEntersVehicleScriptEvent> getEntersVehicleEventImpl() { // TODO: once 1.20 is the minimum supported version, implement in the ScriptEvent class as usual
return EntityEntersVehicleScriptEventImpl.class;
}
public static class EntityExistsVehicleScriptEventImpl extends EntityExitsVehicleScriptEvent {
@EventHandler
public void onEntityMount(EntityDismountEvent event) {
fire(event, event.getDismounted());
}
}
public Class<? extends EntityExitsVehicleScriptEvent> getExistsVehicleEventImpl() { // TODO: once 1.20 is the minimum supported version, implement in the ScriptEvent class as usual
return EntityExistsVehicleScriptEventImpl.class;
}
}