RuneHive-Game
Loading...
Searching...
No Matches
CombatUtil.java
Go to the documentation of this file.
1package com.runehive.game.world.entity.combat;
2
3import com.runehive.content.activity.Activity;
4import com.runehive.content.activity.ActivityType;
5import com.runehive.content.lms.LMSGame;
6import com.runehive.content.skill.impl.slayer.SlayerTask;
7import com.runehive.game.Animation;
8import com.runehive.game.Projectile;
9import com.runehive.game.UpdatePriority;
10import com.runehive.game.world.World;
11import com.runehive.game.world.entity.combat.effect.AntifireDetails;
12import com.runehive.game.world.entity.combat.effect.CombatEffect;
13import com.runehive.game.world.entity.combat.effect.CombatEffectType;
14import com.runehive.game.world.entity.combat.hit.CombatHit;
15import com.runehive.game.world.entity.combat.hit.Hit;
16import com.runehive.game.world.entity.combat.hit.HitIcon;
17import com.runehive.game.world.entity.combat.hit.Hitsplat;
18import com.runehive.game.world.entity.combat.projectile.CombatProjectile;
19import com.runehive.game.world.entity.combat.strategy.CombatStrategy;
20import com.runehive.game.world.entity.mob.Mob;
21import com.runehive.game.world.entity.mob.data.PacketType;
22import com.runehive.game.world.entity.mob.npc.Npc;
23import com.runehive.game.world.entity.mob.player.Player;
24import com.runehive.game.world.entity.mob.prayer.Prayer;
25import com.runehive.game.world.items.Item;
26import com.runehive.game.world.items.containers.equipment.Equipment;
27import com.runehive.game.world.position.Area;
28import com.runehive.net.packet.out.SendMessage;
29import com.runehive.util.RandomUtils;
30import com.runehive.util.Utility;
31
32import java.util.List;
33import java.util.function.Consumer;
34
35/**
36 * A collection of util methods and constants related to combat.
37 *
38 * @author lare96 <http://github.com/lare96>
39 */
40public final class CombatUtil {
41
42 /**
43 * The default constructor.
44 *
45 * @throws UnsupportedOperationException if this class is instantiated.
46 */
47 private CombatUtil() {
48 throw new UnsupportedOperationException("This class cannot be instantiated!");
49 }
50
51 /**
52 * Executes an action for mobs within a 3x3 square, including the source
53 * {@code mob}.
54 *
55 * @param mob the mob to generate an area for
56 * @param action the action to apply to all mobs in the area
57 */
58 public static void areaAction(Mob mob, Consumer<Mob> action) {
59 action.accept(mob);
60 areaAction(mob, 3 * 3, 1, action);
61 }
62
63 /**
64 * Sends an action to {@link Mob} instance which is within a {@code
65 * distance}.
66 *
67 * @param action action consumer.
68 */
69 public static void areaAction(Mob mob, int max, int distance, Consumer<Mob> action) {
70 if (!Area.inMulti(mob)) {
71 return;
72 }
73
74 int added = 0;
75 List<Player> players = World.getRegions().getLocalPlayers(mob);
76 players.sort((first, second) -> {
77 int firstD = Utility.getDistance(first, mob);
78 int secondD = Utility.getDistance(second, mob);
79 return firstD - secondD;
80 });
81
82 for (Player other : players) {
83 if (other == null) continue;
84 if (other.instance != mob.instance) continue;
85 if (!Utility.withinViewingDistance(other, mob, distance)) continue;
86 if (other.equals(mob)) continue;
87 if (other.getCurrentHealth() <= 0 || other.isDead()) continue;
88 if (!Area.inMulti(other)) continue;
89 if (mob.isPlayer() && other.isPlayer() && (!Area.inWilderness(mob) || !Area.inWilderness(other)))
90 continue;
91 action.accept(other);
92 if (++added == max) return;
93 }
94
95 List<Npc> npcs = World.getRegions().getLocalNpcs(mob);
96 npcs.sort((first, second) -> {
97 int firstD = Utility.getDistance(first, mob);
98 int secondD = Utility.getDistance(second, mob);
99 return firstD - secondD;
100 });
101
102 for (Npc other : npcs) {
103 if (other == null) continue;
104 if (other.instance != mob.instance) continue;
105 if (!Utility.withinViewingDistance(other, mob, distance)) continue;
106 if (other.equals(mob)) continue;
107 if (other.getCurrentHealth() <= 0 || other.isDead()) continue;
108 if (!Area.inMulti(other)) continue;
109 if (!other.definition.isAttackable()) continue;
110 action.accept(other);
111 if (++added == max) return;
112 }
113 }
114
115 /**
116 * Gets the hit delay for the specified {@code type}.
117 *
118 * @param attacker the character doing the hit
119 * @param defender the victim being hit
120 * @param type the combat type of this hit
121 * @return the delay for the combat type
122 */
123 public static int getHitDelay(final Mob attacker, final Mob defender, final CombatType type) {
124 if (CombatType.MELEE.equals(type)) return 0;
125
127 if (strategy != null) {
128 final CombatProjectile combatProjectile = strategy.getCombatProjectile();
129 if (combatProjectile != null) {
130 final int serverTicks = combatProjectile.getServerTicks();
131 if (serverTicks > 0) {
132 return serverTicks;
133 }
134 }
135 }
136 return getOldHitDelay(attacker, defender, type);
137 }
138
139 private static int getOldHitDelay(final Mob attacker, final Mob defender, final CombatType type) {
140 final int distance = Math.min(10, Utility.getDistance(attacker, defender));
141 return switch (type) {
142 default -> 0;
143 case RANGED -> Projectile.RANGED_DELAYS[distance] - 1;
144 case MAGIC -> Projectile.MAGIC_DELAYS[distance] - 1;
145 };
146 }
147
148 /**
149 * Gets the hitsplat delay for the specified {@code type}.
150 *
151 * @param type the combat type of this hit
152 * @return the delay for the combat type
153 */
154 public static int getHitsplatDelay(CombatType type) {
155 return type.getHitsplatDelay();
156 }
157
158 static boolean validateMobs(Mob attacker, Mob defender) {
159 if (!validate(attacker) || !validate(defender)) {
160 attacker.getCombat().reset();
161 return false;
162 }
163
164 if (attacker.instance != defender.instance) {
165 attacker.getCombat().reset();
166 return false;
167 }
168
169 if (!canAttack(attacker, defender)) {
170 attacker.getCombat().reset();
171 return false;
172 }
173 return true;
174 }
175
176 static boolean validate(Mob attacker, Mob defender) {
177 if (!validate(attacker) || !validate(defender)) {
178 attacker.getCombat().reset();
179 return false;
180 }
181
182 if (attacker.instance != defender.instance) {
183 attacker.getCombat().reset();
184 return false;
185 }
186
187 if (!canBasicAttack(attacker, defender)) {
188 attacker.getCombat().reset();
189 return false;
190 }
191 return true;
192 }
193
194 /**
195 * Applies the {@code effect} in any context.
196 *
197 * @param effect the effect that must be applied
198 * @return {@code true} if it was successfully applied
199 */
200 public static boolean effect(Mob mob, CombatEffectType effect) {
201 return CombatEffect.EFFECTS.get(effect).start(mob);
202 }
203
204 /**
205 * Cancels the {@code effect} in any context.
206 *
207 * @param effect the effect that must be applied
208 * @return {@code true} if it was successfully applied
209 */
210 public static boolean cancelEffect(Mob mob, CombatEffectType effect) {
211 return CombatEffect.EFFECTS.get(effect).removeOn(mob);
212 }
213
214 public static boolean canAttack(Mob attacker, Mob defender) {
215 if (attacker.isPlayer())
216 return canAttack(attacker.getPlayer(), defender);
217 return canAttack(attacker.getNpc(), defender);
218 }
219
220 private static boolean canAttack(Player attacker, Mob defender) {
221
222
223 if (defender.isNpc() && !SlayerTask.canAttack(attacker, defender.getName())) {
224 attacker.send(new SendMessage("You do not meet the slayer requirements to attack this npc!"));
225 return false;
226 }
227 if (attacker.equals(defender)) {
228 //attacker.send(new SendMessage("You can't attack yourself!"));
229 return false;
230 }
231 if (defender.isNpc() && defender.getNpc().owner != null && !attacker.equals(defender.getNpc().owner)) {
232 attacker.send(new SendMessage("You can't attack this npc!"));
233 return false;
234 }
235
236 if (attacker.getCombat().isUnderAttack() && !attacker.getCombat().isUnderAttackBy(defender)) {
237 if (!Area.inMulti(attacker) || !Area.inMulti(defender)) {
238 attacker.send(new SendMessage("You are already under attack!"));
239 return false;
240 }
241 }
242
243 if (defender.getCombat().isUnderAttack() && !defender.getCombat().isUnderAttackBy(attacker)) {
244 if (!Area.inMulti(attacker) || !Area.inMulti(defender)) {
245 if (defender.isPlayer()) {
246 attacker.send(new SendMessage(defender.getName() + " is currently in combat and can not be attacked."));
247 } else {
248 attacker.send(new SendMessage("This monster is already under attack!"));
249 }
250 return false;
251 }
252 }
253 if (defender.isPlayer()) {
254 if (attacker.locking.locked(PacketType.COMBAT)) {
255 return false;
256 }
257
258 if (LMSGame.inGameArea(attacker) && LMSGame.canAttack(attacker, defender.getPlayer())) {
259 return true;
260 }
261
262 if (Activity.evaluate(attacker, activity -> defender.activity == activity) && attacker.inActivity(ActivityType.DUEL_ARENA)) {
263 return true;
264 }
265
266/*
267 if (Area.inEventArena(attacker) && Area.inEventArena(defender)) {
268 return true;
269 }
270*/
271
272 int difference = Math.abs(attacker.skills.getCombatLevel() - defender.skills.getCombatLevel());
273
274 if (attacker.pvpInstance) {
275 if (!defender.getPlayer().pvpInstance) {
276 attacker.message(defender.getName() + " is not in a PvP instance.");
277 return false;
278 }
279 if (!Area.inPvP(attacker) && !attacker.hasPvPTimer) {
280 attacker.message("You must be in a PvP zone to attack " + defender.getName() + "!");
281 return false;
282 }
283
284 if (!Area.inPvP(defender) && !defender.getPlayer().hasPvPTimer) {
285 attacker.message(defender.getName() + " must be in a PvP zone for you to attack!");
286 return false;
287 }
288
289 if (difference > 10) {
290 if (!Area.inDuelObsticleArena(attacker)) {
291 attacker.message("Your combat level difference is too great!");
292 return false;
293 }
294 }
295 return true;
296 }
297
298 if (difference > attacker.wilderness) {
299 if (!Area.inDuelObsticleArena(attacker)) {
300 attacker.send(new SendMessage("Your combat level difference is too great!"));
301 return false;
302 }
303 }
304
305 if (!Area.inWilderness(attacker)) {
306 attacker.send(new SendMessage("You need to be in the wilderness to attack " + Utility.formatName(defender.getName()) + "."));
307 return false;
308 }
309
310 if (!Area.inWilderness(defender)) {
311 attacker.send(new SendMessage(Utility.formatName(defender.getName()) + " must be in the wilderness for you to attack."));
312 return false;
313 }
314 }
315 return true;
316 }
317
318 private static boolean canAttack(Npc attacker, Mob defender) {
319 if (attacker.equals(defender)) {
320 return false;
321 }
322 if (defender.isNpc() && defender.getNpc().owner != null && !defender.getNpc().owner.equals(attacker)) {
323 return false;
324 }
325 if (attacker.getCombat().isUnderAttack() && !attacker.getCombat().isUnderAttackBy(defender)) {
326 if (!Area.inMulti(attacker) || !Area.inMulti(defender)) {
327 return false;
328 }
329 }
330 if (defender.getCombat().isUnderAttack() && !defender.getCombat().isUnderAttackBy(attacker)) {
331 return Area.inMulti(attacker) && Area.inMulti(defender);
332 }
333 return true;
334 }
335
336 public static boolean canBasicAttack(Mob attacker, Mob defender) {
337 if (attacker.equals(defender)) {
338 return false;
339 }
340 if (defender.isNpc() && defender.getNpc().owner != null && !attacker.equals(defender.getNpc().owner)) {
341 return false;
342 }
343 if (Activity.evaluate(attacker, activity -> defender.activity == activity) && attacker.inActivity(ActivityType.DUEL_ARENA)) {
344 return true;
345 }
346 if (attacker.getCombat().isUnderAttack() && !attacker.getCombat().isUnderAttackBy(defender)) {
347 if (!Area.inMulti(attacker) || !Area.inMulti(defender)) {
348 return false;
349 }
350 }
351 if (defender.getCombat().isUnderAttack() && !defender.getCombat().isUnderAttackBy(attacker)) {
352 if (!Area.inMulti(attacker) || !Area.inMulti(defender)) {
353 return false;
354 }
355 }
356 if (attacker.isPlayer() && defender.isPlayer()) {
357 int difference = Math.abs(attacker.skills.getCombatLevel() - defender.skills.getCombatLevel());
358
359 if (attacker.getPlayer().pvpInstance || defender.getPlayer().pvpInstance) {
360 if (!defender.getPlayer().pvpInstance) {
361 return false;
362 }
363 if (!attacker.getPlayer().pvpInstance) {
364 return false;
365 }
366 if (!Area.inPvP(attacker)) {
367 return false;
368 }
369 if (!Area.inPvP(defender)) {
370 return false;
371 }
372 return difference <= 10;
373 }
374
375
376 if (difference > attacker.getPlayer().wilderness) {
377 return false;
378 }
379 if (!Area.inWilderness(attacker)) {
380 return false;
381 }
382 return Area.inWilderness(defender);
383 }
384 return true;
385 }
386
387 private static boolean validate(Mob mob) {
388 return mob != null && !mob.isDead() && mob.isVisible() && mob.isValid() && !mob.teleporting && !mob.inTeleport;
389 }
390
392 int animation = 404;
393 if (mob.isPlayer()) {
394 final var shieldOverride = mob.getPlayer().overrides.hasOverride(Equipment.SHIELD_SLOT);
395 if (mob.getPlayer().equipment.hasShield() || shieldOverride) {
396 Item shield = shieldOverride ? mob.getPlayer().overrides.get(Equipment.SHIELD_SLOT) : mob.getPlayer().equipment.getShield();
397 animation = shield.getBlockAnimation().orElse(424);
398 } else if (mob.isPlayer()) {
399 final var weaponOverride = mob.getPlayer().overrides.hasOverride(Equipment.WEAPON_SLOT);
400 if (mob.getPlayer().equipment.hasWeapon() || weaponOverride) {
401 Item weapon = weaponOverride ? mob.getPlayer().overrides.get(Equipment.WEAPON_SLOT) : mob.getPlayer().equipment.getWeapon();
402 animation = weapon.getBlockAnimation().orElse(424);
403 } else {
404 animation = 404;//TODO
405 }
406 }
407 } else {
408 Npc npc = mob.getNpc();
409 animation = npc.definition.getBlockAnimation();
410 }
411 int delay = (int) mob.getCombat().lastBlocked.elapsedTime();
412 if (delay < 600)
413 return new Animation(animation, delay / 50, UpdatePriority.LOW);
414 return new Animation(animation, UpdatePriority.LOW);
415 }
416
417 public static CombatHit generateDragonfire(Mob attacker, Mob defender, int max, boolean prayer) {
418 int hitDelay = getHitDelay(attacker, defender, CombatType.MAGIC);
419 int hitsplatDelay = 1;
420 return generateDragonfire(attacker, defender, max, hitDelay, hitsplatDelay, prayer);
421 }
422
423 public static CombatHit generateDragonfire(Mob attacker, Mob defender, int max, int hitDelay, int hitsplatDelay, boolean prayer) {
424 int damage;
425
426 if (defender.isPlayer()) {
427 Player player = defender.getPlayer();
428
429 if (Equipment.isWearingDFS(player) && player.dragonfireCharges < 50) {
430 if (player.equipment.getShield().getId() == 11284) {
431 player.equipment.set(Equipment.SHIELD_SLOT, new Item(11283), true);
432 player.equipment.refresh();
433 }
434 player.animate(6695);
435 player.graphic(1164);
436 player.dragonfireCharges++;
437 player.getCombat().setCooldown(5);
438 player.send(new SendMessage("Your dragonfire Shield Absorbs the Dragon breath."));
439 player.face(attacker);
440 damage = 0;
441 } else {
442 prayer &= player.prayer.isActive(Prayer.PROTECT_FROM_MAGIC);
443 boolean shield = player.equipment.containsAny(1540, 11283, 21633);
444 boolean potion = player.getAntifireDetails().isPresent();
445
446 if (shield && potion) {
447 max -= 65;
448 } else if (potion) {
449 AntifireDetails.AntifireType type = player.getAntifireDetails().get().getType();
450 max -= type.getReduction();
451 if (max <= 0) {
452 max = 0;
453 }
454 } else if (shield) {
455 max -= 50;
456 } else if (prayer) {
457 max -= 45;
458 }
459
460 if (max > 0) {
461 damage = RandomUtils.inclusive(max);
462 } else {
463 damage = 0;
464 }
465
466 if (damage >= 15) {
467 player.send(new SendMessage("You are horribly burned by the dragonfire!", true));
468 } else if (!shield && !potion && !prayer && damage < 9 && damage > 0) {
469 player.send(new SendMessage("You manage to resist some of the dragonfire.", true));
470 }
471 }
472 } else {
473 damage = max == 0 ? 0 : RandomUtils.inclusive(max);
474 }
475
476 Hit hit = new Hit(damage, Hitsplat.NORMAL, HitIcon.NONE, true);
477 return new CombatHit(hit, hitDelay, hitsplatDelay);
478 }
479
481 return RandomUtils.random(strategies);
482 }
483
484 @SafeVarargs
486 return strategies;
487 }
488
489 @SafeVarargs
491 CombatStrategy<Npc>... moreStrategies) {
492 final CombatStrategy<Npc>[] array = new CombatStrategy[strategies.length + moreStrategies.length];
493 System.arraycopy(strategies, 0, array, 0, strategies.length);
494 System.arraycopy(moreStrategies, 0, array, strategies.length, moreStrategies.length);
495 return array;
496 }
497
498}
A Activity object constructs an in-game activity and sequences it through the start() and finish() me...
Definition Activity.java:31
static boolean evaluate(Mob mob, Predicate< Activity > predicate)
Definition Activity.java:74
static boolean canAttack
Checks if the players are allowed to attack within the game.
Definition LMSGame.java:53
static boolean inGameArea(Player player)
Handles checking if the player is in the LMS game area.
Definition LMSGame.java:142
Class that models a single animation used by an entity.
Represents the game world.
Definition World.java:46
static RegionManager getRegions()
Definition World.java:552
abstract String getName()
Gets the name of this entity.
abstract boolean equals(Object obj)
static boolean cancelEffect(Mob mob, CombatEffectType effect)
Cancels the effect in any context.
static void areaAction(Mob mob, int max, int distance, Consumer< Mob > action)
Sends an action to Mob instance which is within a distance.
static CombatStrategy< Npc >[] createStrategyArray(CombatStrategy< Npc >[] strategies, CombatStrategy< Npc >... moreStrategies)
static CombatStrategy< Npc >[] createStrategyArray(CombatStrategy< Npc >... strategies)
static boolean validateMobs(Mob attacker, Mob defender)
static int getHitDelay(final Mob attacker, final Mob defender, final CombatType type)
Gets the hit delay for the specified type.
static boolean effect(Mob mob, CombatEffectType effect)
Applies the effect in any context.
static boolean validate(Mob attacker, Mob defender)
static CombatHit generateDragonfire(Mob attacker, Mob defender, int max, int hitDelay, int hitsplatDelay, boolean prayer)
static int getOldHitDelay(final Mob attacker, final Mob defender, final CombatType type)
static boolean canAttack(Npc attacker, Mob defender)
static boolean canBasicAttack(Mob attacker, Mob defender)
static boolean canAttack(Mob attacker, Mob defender)
static CombatHit generateDragonfire(Mob attacker, Mob defender, int max, boolean prayer)
static boolean canAttack(Player attacker, Mob defender)
static CombatStrategy< Npc > randomStrategy(CombatStrategy< Npc >[] strategies)
static int getHitsplatDelay(CombatType type)
Gets the hitsplat delay for the specified type.
static void areaAction(Mob mob, Consumer< Mob > action)
Executes an action for mobs within a 3x3 square, including the source mob.
Some sort of temporary effect applied to a Mob during combat.
static final Map< CombatEffectType, CombatEffect > EFFECTS
The map of all of the combat effect types mapped to their respective listeners.
A wrapper for a Hit object, adding additional variables for hit and hitsplat delays.
A Hit object holds the damage amount and hitsplat data.
Definition Hit.java:10
boolean locked()
Checks if the mob is locked.
Definition Locking.java:52
Handles the mob class.
Definition Mob.java:66
abstract< T extends Mob > CombatStrategy<? super T > getStrategy()
The combat strategy of the mob.
abstract Combat<? extends Mob > getCombat()
The combat of the mob.
void face(GameObject object)
Sets the client update flag to face a certain direction.
Definition Mob.java:289
final boolean isNpc()
Check if an entity is an npc.
Definition Mob.java:550
final boolean isPlayer()
Check if an entity is a player.
Definition Mob.java:564
Optional< Graphic > graphic
Definition Mob.java:91
Represents a non-player character in the in-game world.
Definition Npc.java:29
Combat< Npc > getCombat()
The combat of the mob.
Definition Npc.java:156
This class represents a character controlled by a player.
Definition Player.java:125
Optional< AntifireDetails > getAntifireDetails()
Definition Player.java:897
Combat< Player > getCombat()
The combat of the mob.
Definition Player.java:759
boolean isActive(Prayer... prayers)
Checks if all given prayers are active.
int getCombatLevel()
Gets the mob's combat level.
The container class that represents an item that can be interacted with.
Definition Item.java:21
final int getId()
Gets the identification of this item.
Definition Item.java:324
final boolean containsAny(int... identifiers)
Determines if this container contains any identifiers.
The container that manages the equipment for a player.
void refresh()
Forces a refresh of Equipment items to the EQUIPMENT_DISPLAY_ID widget.
Handles checking if mobs are in a certain area.
Definition Area.java:13
static boolean inMulti(Entity entity)
Definition Area.java:263
static boolean inWilderness(Position position)
Definition Area.java:272
static boolean inDuelObsticleArena(Interactable entity)
Definition Area.java:302
static boolean inPvP(Interactable entity)
Definition Area.java:355
List< Player > getLocalPlayers(Mob mob)
Gets the local players around an entity.
List< Npc > getLocalNpcs(Mob mob)
Gets the local npcs around an entity.
The OutgoingPacket that sends a message to a Players chatbox in the client.
A static-util class that provides additional functionality for generating pseudo-random numbers.
static int inclusive(int min, int max)
Returns a pseudo-random int value between inclusive min and inclusive max.
static< T > T random(T[] array)
Pseudo-randomly retrieves a element from array.
Handles miscellaneous methods.
Definition Utility.java:27
static String formatName(final String input)
Definition Utility.java:645
static int getDistance(Interactable source, Position target)
Definition Utility.java:363
static boolean withinViewingDistance(Interactable source, Interactable target, int radius)
Definition Utility.java:755
Holds all activity types that are timed.
static boolean canAttack(Player player, String npc)
Checks if a player can attack a slayer monster.
Represents different priorities for updating.
The enumerated type whose values represent the collection of different combat effect types.
The enumerated type whose elements represent the hit icon of a Hit.
Definition HitIcon.java:8