1+ package net .darkhax .bookshelf .api .util ;
2+
3+ import java .util .HashMap ;
4+ import java .util .HashSet ;
5+ import java .util .List ;
6+ import java .util .Map ;
7+ import java .util .Set ;
8+
9+ import net .minecraft .core .BlockPos ;
10+ import net .minecraft .core .Direction ;
11+ import net .minecraft .util .Tuple ;
12+ import net .minecraft .world .effect .MobEffect ;
13+ import net .minecraft .world .effect .MobEffectInstance ;
14+ import net .minecraft .world .effect .MobEffects ;
15+ import net .minecraft .world .entity .Entity ;
16+ import net .minecraft .world .entity .EntityType ;
17+ import net .minecraft .world .entity .EquipmentSlot ;
18+ import net .minecraft .world .entity .LivingEntity ;
19+ import net .minecraft .world .entity .Mob ;
20+ import net .minecraft .world .item .Item ;
21+ import net .minecraft .world .item .ItemStack ;
22+ import net .minecraft .world .item .SpawnEggItem ;
23+ import net .minecraft .world .level .ClipContext ;
24+ import net .minecraft .world .level .ClipContext .Block ;
25+ import net .minecraft .world .level .ClipContext .Fluid ;
26+ import net .minecraft .world .level .Level ;
27+ import net .minecraft .world .phys .AABB ;
28+ import net .minecraft .world .phys .HitResult ;
29+ import net .minecraft .world .phys .Vec3 ;
30+
31+ public final class EntityUtils {
32+
33+ /**
34+ * A cache of spawn egg colors mapped to the entity type. Populated by
35+ * {@link #getEggColors(EntityType)}.
36+ */
37+ private static Map <EntityType <?>, Tuple <Integer , Integer >> eggColorCache = new HashMap <>();
38+
39+ /**
40+ * Calculates the distance between two entities.
41+ *
42+ * @param firstEntity The first entity to use.
43+ * @param secondEntity The second entity to use.
44+ * @return double The distance between the two entities passed.
45+ */
46+ public static double getDistanceFromEntity (Entity firstEntity , Entity secondEntity ) {
47+
48+ return MathsUtils .getDistanceBetweenPoints (firstEntity .position (), secondEntity .position ());
49+ }
50+
51+ /**
52+ * Calculates the distance between an entity and a BlockPos.
53+ *
54+ * @param entity The Entity to use for the first position.
55+ * @param pos The BlockPos to use for the second position.
56+ * @return double The distance between the Entity and the BlockPos.
57+ */
58+ public static double getDistaceFromPos (Entity entity , BlockPos pos ) {
59+
60+ return MathsUtils .getDistanceBetweenPoints (entity .position (), Vec3 .atCenterOf (pos ));
61+ }
62+
63+ /**
64+ * Pushes an entity towards a specific direction.
65+ *
66+ * @param entityToMove The entity that you want to push.
67+ * @param direction The direction to push the entity.
68+ * @param force The amount of force to push the entity with.
69+ */
70+ public static void pushTowards (Entity entityToMove , Direction direction , double force ) {
71+
72+ pushTowards (entityToMove , entityToMove .blockPosition ().relative (direction .getOpposite (), 1 ), force );
73+ }
74+
75+ /**
76+ * Pushes an Entity towards a BlockPos.
77+ *
78+ * @param entityToMove The entity that you want to push.
79+ * @param pos The BlockPos to push the entity towards.
80+ * @param force The amount of force to push the entity with.
81+ */
82+ public static void pushTowards (Entity entityToMove , BlockPos pos , double force ) {
83+
84+ final BlockPos entityPos = entityToMove .blockPosition ();
85+ final double distanceX = (double ) pos .getX () - entityPos .getX ();
86+ final double distanceY = (double ) pos .getY () - entityPos .getY ();
87+ final double distanceZ = (double ) pos .getZ () - entityPos .getZ ();
88+ final double distance = Math .sqrt (distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ );
89+
90+ if (distance > 0 ) {
91+ entityToMove .setDeltaMovement (new Vec3 (distanceX / distance * force , distanceY / distance * force , distanceZ / distance * force ));
92+ }
93+ }
94+
95+ /**
96+ * Pushes an entity towards another one.
97+ *
98+ * @param entityToMove The entity that should be pushed towards the other entity.
99+ * @param destination The destination entity, that the entity to move should be pushed
100+ * towards.
101+ * @param force The amount of force to push the entityToMove with.
102+ */
103+ public static void pushTowards (Entity entityToMove , Entity destination , double force ) {
104+
105+ final double distanceX = destination .getX () - entityToMove .getX ();
106+ final double distanceY = destination .getY () - entityToMove .getY ();
107+ final double distanceZ = destination .getZ () - entityToMove .getZ ();
108+ final double distance = Math .sqrt (distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ );
109+
110+ if (distance > 0 ) {
111+ entityToMove .setDeltaMovement (new Vec3 (distanceX / distance * force , distanceY / distance * force , distanceZ / distance * force ));
112+ }
113+ }
114+
115+ /**
116+ * Creates a Vector3d that represents the additional motion that would be needed to push an
117+ * entity towards a destination.
118+ *
119+ * @param entityToMove The entity to push.
120+ * @param direction The direction to push the entity.
121+ * @param force The amount of force to use.
122+ *
123+ */
124+ public static void pushTowardsDirection (Entity entityToMove , Direction direction , double force ) {
125+
126+ final BlockPos entityPos = entityToMove .blockPosition ();
127+ final BlockPos destination = entityToMove .blockPosition ().relative (direction .getOpposite (), 1 );
128+
129+ final double distanceX = (double ) destination .getX () - entityPos .getX ();
130+ final double distanceY = (double ) destination .getY () - entityPos .getY ();
131+ final double distanceZ = (double ) destination .getZ () - entityPos .getZ ();
132+ final double distance = Math .sqrt (distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ );
133+
134+ if (distance > 0 ) {
135+ entityToMove .setDeltaMovement (new Vec3 (distanceX / distance * force , distanceY / distance * force , distanceZ / distance * force ));
136+ }
137+ }
138+
139+ /**
140+ * Checks if two entities are close enough together.
141+ *
142+ * @param firstEntity The first entity to check.
143+ * @param secondEntity The second entity to check.
144+ * @param maxDistance The maximum distance that the entities can be apart.
145+ * @return boolean True if the distance between the entities are within range of the
146+ * maxDistance.
147+ */
148+ public static boolean areEntitiesCloseEnough (Entity firstEntity , Entity secondEntity , double maxDistance ) {
149+
150+ return getDistanceFromEntity (firstEntity , secondEntity ) < maxDistance * maxDistance ;
151+ }
152+
153+ /**
154+ * Gets a List of entities that are within the provided area.
155+ *
156+ * @param <T> The type of entities to look for.
157+ * @param entityClass The type of entity you are looking for.
158+ * @param world The world to search in.
159+ * @param pos The position to start the search around.
160+ * @param range The range of the search.
161+ * @return A List containing all entities of the specified type that are within the range.
162+ */
163+ public static <T extends Entity > List <T > getEntitiesInArea (Class <T > entityClass , Level world , BlockPos pos , int range ) {
164+
165+ return getEntitiesInArea (entityClass , world , pos , (float ) range );
166+ }
167+
168+ /**
169+ * Gets a List of entities that are within the provided area.
170+ *
171+ * @param <T> The type of entities to look for.
172+ * @param entityClass The type of entity you are looking for.
173+ * @param world The world to search in.
174+ * @param pos The position to start the search around.
175+ * @param range The range of the search.
176+ * @return A List containing all entities of the specified type that are within the range.
177+ */
178+ public static <T extends Entity > List <T > getEntitiesInArea (Class <T > entityClass , Level world , BlockPos pos , float range ) {
179+
180+ return world .getEntitiesOfClass (entityClass , new AABB (pos .offset (-range , -range , -range ), pos .offset (range + 1 , range + 1 , range + 1 )));
181+ }
182+
183+ /**
184+ * A check to see if an entity is wearing a full suit of the armor. This check is based on
185+ * the class names of armor.
186+ *
187+ * @param living: The living entity to check the armor of.
188+ * @param armorClass: The class of the armor to check against.
189+ * @return boolean: True if every piece of armor the entity is wearing are the same class
190+ * as the provied armor class.
191+ */
192+ public static boolean isWearingFullSet (Mob living , Class <Item > armorClass ) {
193+
194+ for (final EquipmentSlot slot : EquipmentSlot .values ()) {
195+ if (slot .getType ().equals (EquipmentSlot .Type .ARMOR )) {
196+
197+ final ItemStack armor = living .getItemBySlot (slot );
198+
199+ if (armor .isEmpty () || !armor .getItem ().getClass ().equals (armorClass )) {
200+ return false ;
201+ }
202+ }
203+ }
204+
205+ return true ;
206+ }
207+
208+ /**
209+ * Performs a ray trace for the look vector of an entity.
210+ *
211+ * @param entity The entity to perform a ray trace on.
212+ * @param length The distance to cast the rays.
213+ * @param blockMode The mode used when detecting blocks.
214+ * @param fluidMode The mode used when detecting fluids.
215+ * @return An object containing the results of the ray trace.
216+ */
217+ public static HitResult rayTrace (LivingEntity entity , double length , Block blockMode , Fluid fluidMode ) {
218+
219+ final Vec3 startingPosition = new Vec3 (entity .getX (), entity .getY () + entity .getEyeHeight (), entity .getZ ());
220+ final Vec3 lookVector = entity .getLookAngle ();
221+ final Vec3 endingPosition = startingPosition .add (lookVector .x * length , lookVector .y * length , lookVector .z * length );
222+ return entity .level .clip (new ClipContext (startingPosition , endingPosition , blockMode , fluidMode , entity ));
223+ }
224+
225+ /**
226+ * Checks if an entity can be affected by fire. While fire immune entities can already
227+ * negate the effects of fire doing prechecks using this method can be used to avoid
228+ * rendering flickers or filter out these types of entities.
229+ *
230+ * @param toCheck The entity to check.
231+ * @return Whether or not this entity can be affected by fire.
232+ */
233+ public static boolean isAffectedByFire (LivingEntity toCheck ) {
234+
235+ return !toCheck .fireImmune () && !toCheck .hasEffect (MobEffects .FIRE_RESISTANCE );
236+ }
237+
238+ /**
239+ * Clears potion effect from an entity based on whether or not the effects are positive or
240+ * negative.
241+ *
242+ * @param entity The entity to remove effects from.
243+ * @param removePositive Should positive effects be cleared?
244+ * @param removeNegative Should negative effects be cleared?
245+ */
246+ public static void clearEffects (LivingEntity entity , boolean removePositive , boolean removeNegative ) {
247+
248+ final Set <MobEffect > toClear = new HashSet <>();
249+
250+ for (final MobEffectInstance effect : entity .getActiveEffects ()) {
251+
252+ final boolean isGood = effect .getEffect ().isBeneficial ();
253+
254+ if (isGood && removePositive || !isGood && removeNegative ) {
255+
256+ toClear .add (effect .getEffect ());
257+ }
258+ }
259+
260+ for (final MobEffect effect : toClear ) {
261+
262+ entity .removeEffect (effect );
263+ }
264+ }
265+
266+ /**
267+ * Get the egg color associated with an entity type. If the entity does not have an egg
268+ * type this will be 0 for both values.
269+ *
270+ * @param type The entity type to get a color for.
271+ * @return A Tuple containing the primary and secondary egg colors.
272+ */
273+ public static Tuple <Integer , Integer > getEggColors (EntityType <?> type ) {
274+
275+ return eggColorCache .computeIfAbsent (type , key -> {
276+
277+ SpawnEggItem item = SpawnEggItem .byId (key );
278+
279+ if (item != null ) {
280+
281+ return new Tuple <>(item .getColor (0 ), item .getColor (1 ));
282+ }
283+
284+ return new Tuple <>(0 , 0 );
285+ });
286+ }
287+ }
0 commit comments