RuneHive-Tarnish
Neural OSRS Enhancement Framework
Loading...
Searching...
No Matches
Mob.java
1package com.osroyale.game.world.entity.mob;
2
3import com.osroyale.content.activity.Activity;
4import com.osroyale.content.activity.ActivityType;
5import com.osroyale.content.wintertodt.Wintertodt;
6import com.osroyale.fs.cache.decoder.AnimationDefinition;
7import com.osroyale.fs.cache.decoder.AnimationDefinitionDecoder;
8import com.osroyale.game.Animation;
9import com.osroyale.game.Graphic;
10import com.osroyale.game.action.ActionManager;
11import com.osroyale.game.task.impl.ForceMovementTask;
12import com.osroyale.game.world.Interactable;
13import com.osroyale.game.world.World;
14import com.osroyale.game.world.entity.Entity;
15import com.osroyale.game.world.entity.EntityType;
16import com.osroyale.game.world.entity.combat.Combat;
17import com.osroyale.game.world.entity.combat.CombatUtil;
18import com.osroyale.game.world.entity.combat.PoisonType;
19import com.osroyale.game.world.entity.combat.attack.listener.CombatListener;
20import com.osroyale.game.world.entity.combat.attack.listener.CombatListenerManager;
21import com.osroyale.game.world.entity.combat.effect.CombatEffectType;
22import com.osroyale.game.world.entity.combat.hit.Hit;
23import com.osroyale.game.world.entity.combat.strategy.CombatStrategy;
24import com.osroyale.game.world.entity.combat.strategy.npc.NpcMeleeStrategy;
25import com.osroyale.game.world.entity.mob.data.LockType;
26import com.osroyale.game.world.entity.mob.movement.Movement;
27import com.osroyale.game.world.entity.mob.movement.waypoint.CombatWaypoint;
28import com.osroyale.game.world.entity.mob.movement.waypoint.FollowWaypoint;
29import com.osroyale.game.world.entity.mob.movement.waypoint.WalkToWaypoint;
30import com.osroyale.game.world.entity.mob.movement.waypoint.Waypoint;
31import com.osroyale.game.world.entity.mob.npc.Npc;
32import com.osroyale.game.world.entity.mob.npc.NpcAssistant;
33import com.osroyale.game.world.entity.mob.npc.NpcUtility;
34import com.osroyale.game.world.entity.mob.npc.definition.NpcDefinition;
35import com.osroyale.game.world.entity.mob.player.ForceMovement;
36import com.osroyale.game.world.entity.mob.player.Player;
37import com.osroyale.game.world.entity.mob.player.relations.ChatMessage;
38import com.osroyale.game.world.entity.mob.prayer.PrayerBook;
39import com.osroyale.game.world.entity.skill.Skill;
40import com.osroyale.game.world.entity.skill.SkillManager;
41import com.osroyale.game.world.object.GameObject;
42import com.osroyale.game.world.pathfinding.TraversalMap;
43import com.osroyale.game.world.position.Position;
44import com.osroyale.net.packet.out.SendPoison;
45import com.osroyale.util.MutableNumber;
46import com.osroyale.util.Stopwatch;
47import com.osroyale.util.Utility;
48import com.osroyale.util.generic.BooleanInterface;
49import com.osroyale.util.generic.GenericAttributes;
50
51import java.util.EnumSet;
52import java.util.LinkedList;
53import java.util.List;
54import java.util.Optional;
55import java.util.concurrent.TimeUnit;
56import java.util.function.Function;
57
58import static com.osroyale.game.world.entity.combat.CombatConstants.EMPTY_BONUSES;
59
102
103public abstract class Mob extends Entity {
104
105 public boolean pathfinderProjectiles = false;
106 private int listIndex;
107 public int id = -1;
108 private int transformId;
109 private boolean dead;
110 public boolean regionChange;
111 public boolean positionChange;
112 public boolean forceWalking;
113 public boolean teleporting;
114 public boolean inTeleport;
115 public boolean teleportRegion;
116 public boolean blockFace;
117 public boolean blockInteract;
118 public String forceChat;
119 public Mob interactingWith;
120 public Hit firstHit;
121 public Hit secondHit;
122 public Position lastPosition;
123 public Position teleportTarget;
124 public Position facePosition;
125 public Activity activity;
126 private Optional<Animation> animation = Optional.empty();
127 public transient long nextAnimation;
128 private Optional<Graphic> graphic = Optional.empty();
129 public List<Mob> followers = new LinkedList<>();
130 public ForceMovement forceMovement;
131 public final EnumSet<UpdateFlag> updateFlags = EnumSet.noneOf(UpdateFlag.class);
132 public final GenericAttributes attributes = new GenericAttributes();
133 public final SkillManager skills = new SkillManager(this);
134 public final SkillManager skills_copy = new SkillManager(this);
135 public final Movement movement = new Movement(this);
136 public MobAnimation mobAnimation = new MobAnimation(updateFlags);
137 public ActionManager action = new ActionManager();
138 protected Waypoint cachedWaypoint;
139 public PrayerBook prayer = new PrayerBook();
140 private int[] bonuses = EMPTY_BONUSES;
141 private final MutableNumber poisonDamage = new MutableNumber();
142 private final MutableNumber venomDamage = new MutableNumber();
143 public final Locking locking = new Locking(this);
144 public final Stopwatch freezeImmunity = Stopwatch.start();
145 private PoisonType poisonType;
146 public Stopwatch damageImmunity = Stopwatch.start();
147
151 public Mob(Position position) {
152 super(position);
153 this.lastPosition = position.copy();
154 }
155
156 public Mob(Position position, boolean visible) {
157 super(position, visible);
158 this.lastPosition = position.copy();
159 }
160
164 public void speak(String forceChat) {
165 if (forceChat == null || forceChat.isEmpty() || forceChat.length() > ChatMessage.CHARACTER_LIMIT)
166 return;
167 this.forceChat = forceChat;
168 this.updateFlags.add(UpdateFlag.FORCED_CHAT);
169 }
170
171 public void animate(int animation) {
172 animate(animation, false);
173 }
174
175 public void animate(int animation, boolean override) {
176 animate(new Animation(animation), override);
177 }
178
182 public void animate(Animation animation) {
183 animate(animation, false);
184 }
185
186 public void graphic(int graphic) {
187 graphic(new Graphic(graphic));
188 }
189
193 public void graphic(Graphic graphic) {
194 graphic(graphic, false);
195 }
196
200 public void animate(Animation animation, final boolean override) {
201 final long now = System.currentTimeMillis();
202 if (!override && (nextAnimation > now || updateFlags.contains(UpdateFlag.ANIMATION))) {
203 return;
204 }
205
206 final Optional<Animation> result = Optional.ofNullable(animation);
207 animation = result.orElse(Animation.RESET);
208
209 final AnimationDefinition definition =
210 animation == Animation.RESET
211 ? null
212 : AnimationDefinitionDecoder.definitions.get(animation.getId());
213 if (definition != null) {
214 nextAnimation = now + definition.durationTime + 600;
215 }
216 this.animation = result;
217 this.updateFlags.add(UpdateFlag.ANIMATION);
218 }
219
223 public void graphic(Graphic graphic, boolean override) {
224 Optional<Graphic> result = Optional.ofNullable(graphic);
225 graphic = result.orElse(Graphic.RESET);
226
227 if (!this.graphic.isPresent() || override || this.graphic.get().compareTo(graphic) > 0) {
228 this.graphic = result;
229 this.updateFlags.add(UpdateFlag.GRAPHICS);
230 }
231 }
232
233 public static boolean pathfinderProjectiles(Mob source) {
234 final String name = source.getName().toLowerCase();
235 return "imp".equals(name) || name.contains("impling");
236 }
237
238 public void transform(int transformId) {
239 transform(transformId, false);
240 }
241
245 public void transform(int transformId, boolean reload) {
246 this.transformId = transformId;
247 this.id = transformId;
248 this.updateFlags.add(UpdateFlag.TRANSFORM);
249 this.updateFlags.add(UpdateFlag.APPEARANCE);
250
251 if (isNpc()) {
252 NpcDefinition definition = NpcDefinition.get(id);
253 final Npc npc = getNpc();
254 npc.definition = definition;
255 npc.pathfinderProjectiles = Mob.pathfinderProjectiles(npc);
256 setWidth(definition.getSize());
257 setLength(definition.getSize());
258 setBonuses(definition.getBonuses());
259 mobAnimation.setNpcAnimations(definition);
260
261 if (reload) {
262 Combat<Npc> combat = getNpc().getCombat();
263 CombatListener<Npc> listener = CombatListenerManager.NPC_LISTENERS.get(getNpc().id);
264
265 if (listener != null) {
266 combat.addListener(listener);
267 }
268
269 getNpc().npcAssistant.reloadSkills();
270 getNpc().setStrategy(NpcUtility.STRATEGIES.getOrDefault(getNpc().id, () -> NpcAssistant.loadStrategy(getNpc()).orElse(NpcMeleeStrategy.get())).get());
271 }
272 }
273 }
274
278 public final void reset() {
279 resetAnimation();
280 resetGraphic();
281 }
282
286 public final void resetWaypoint() {
287 if (cachedWaypoint != null && cachedWaypoint.isRunning()) {
288 cachedWaypoint.cancel();
289 }
290 }
291
292 public void forceMove(int animation, int x, int y) {
293 forceMove(0, 0, animation, 0, 0, 0, new Position(x, y), Direction.NORTH);
294 }
295
296 public void forceMove(int delay, int animation, int startSpeed, int endSpeed, Position offset, Direction direction) {
297 forceMove(delay, 0, animation, startSpeed, endSpeed, offset, direction);
298 }
299
300 public void forceMove(int delay, int delay2, int animation, int startSpeed, int endSpeed, Position offset, Direction direction) {
301 forceMove(delay, delay2, animation, 0, startSpeed, endSpeed, offset, direction);
302 }
303
307 public void forceMove(int delay, int delay2, int animation, int animationDelay, int startSpeed, int endSpeed, Position offset, Direction direction) {
308 ForceMovement movement = new ForceMovement(getPosition().copy(), offset, startSpeed, endSpeed, direction);
309 World.schedule(new ForceMovementTask(this, delay, delay2, movement, new Animation(animation, animationDelay)));
310 }
311
315 public void interact(Mob mob) {
316 if (blockInteract) {
317 return;
318 }
319 this.interactingWith = mob;
320 this.updateFlags.add(UpdateFlag.INTERACT);
321 }
322
326 public void face(GameObject object) {
327 if (blockFace)
328 return;
329 if (object == null || object.getPosition().equals(facePosition))
330 return;
331 this.facePosition = object.getPosition();
332 this.updateFlags.add(UpdateFlag.FACE_COORDINATE);
333 }
334
335 public void face(Mob mob) {
336 face(mob.getPosition());
337 }
338
342 public void face(Position position) {
343 if (blockFace)
344 return;
345 if (!position.equals(facePosition)) {
346 this.facePosition = position;
347 this.updateFlags.add(UpdateFlag.FACE_COORDINATE);
348 }
349 }
350
354 public void face(Direction direction) {
355 if (blockFace)
356 return;
357 Position position = getPosition().transform(direction.getFaceLocation());
358 if (!position.equals(facePosition)) {
359 this.facePosition = position;
360 this.updateFlags.add(UpdateFlag.FACE_COORDINATE);
361 }
362 }
363
367 public void resetFace() {
368 if (blockFace || interactingWith == null)
369 return;
370 interactingWith = null;
371 this.updateFlags.add(UpdateFlag.INTERACT);
372 }
373
377 public void move(Position position) {
378 if (regionChange)
379 return;
380 if (isPlayer() && !getPlayer().interfaceManager.isClear())
381 getPlayer().interfaceManager.close(false);
382 setPosition(position);
383 if (Utility.isRegionChange(position, lastPosition)) {
384 regionChange = true;
385 } else {
386 positionChange = true;
387 }
388 teleportRegion = true;
389 getCombat().reset();
390 resetFace();
391 locking.lock(599, TimeUnit.MILLISECONDS, LockType.MASTER);
392 onStep();
393 }
394
395 public void walk(Position position) {
396 walk(position, false);
397 }
398
399 public void walk(Position destination, boolean ignoreClip) {
400 if (ignoreClip) {
401 movement.walk(destination);
402 } else {
403 movement.simplePath(destination);
404 }
405 }
406
407 public void runTo(Position destination) {
408 movement.dijkstraPath(destination);
409 }
410
411 public void walkTo(Position position) {
412 getCombat().reset();
413 walkTo(position, () -> { /* Do nothing on arrival */ });
414 }
415
416 public void walkTo(Position position, Runnable onDestination) {
417 Interactable interactable = Interactable.create(position);
418 walkTo(interactable, onDestination);
419 }
420
421 public void walkExactlyTo(Position position) {
422 walkExactlyTo(position, () -> {
423 });
424 }
425
426 public void walkExactlyTo(Position position, Runnable onDestination) {
427 Interactable interactable = Interactable.create(position, 0, 0);
428 walkTo(interactable, onDestination);
429 }
430
431 public void walkTo(Interactable target, Runnable onDestination) {
432 walkTo(target, true, onDestination);
433 }
434
435 public void walkTo(Interactable target, boolean clearAction, Runnable onDestination) {
436 Waypoint waypoint = new WalkToWaypoint(this, target, onDestination);
437
438 if (cachedWaypoint == null || (!cachedWaypoint.isRunning() || !waypoint.equals(cachedWaypoint))) {
440 getCombat().reset();
441 movement.reset();
442
443 if (clearAction) {
444 action.clearNonWalkableActions();
445 }
446
447 World.schedule(cachedWaypoint = waypoint);
448 }
449 }
450
451 public void follow(Mob target) {
452 Waypoint waypoint = new FollowWaypoint(this, target);
453 if (cachedWaypoint == null || (!cachedWaypoint.isRunning() || !waypoint.equals(cachedWaypoint))) {
455 movement.reset();
456 action.clearNonWalkableActions();
457 World.schedule(cachedWaypoint = waypoint);
458 }
459 }
460
461 public void attack(Mob target) {
462 Waypoint waypoint = new CombatWaypoint(this, target);
463 if (cachedWaypoint == null || (!cachedWaypoint.isRunning() || !waypoint.equals(cachedWaypoint))) {
465 movement.reset();
466 action.clearNonWalkableActions();
467 World.schedule(cachedWaypoint = waypoint);
468 }
469 }
470
471 protected void setWaypoint(Waypoint waypoint) {
472 if (cachedWaypoint == null || (!cachedWaypoint.isRunning() || !waypoint.equals(cachedWaypoint))) {
474 movement.reset();
475 action.clearNonWalkableActions();
476 World.schedule(cachedWaypoint = waypoint);
477 }
478 }
479
480 public void damage(Hit... hits) {
481 for (Hit hit : hits)
482 getCombat().queueDamage(hit);
483 }
484
485 public void writeFakeDamage(Hit hit) {
486 if (!updateFlags.contains(UpdateFlag.FIRST_HIT)) {
487 firstHit = hit;
488 updateFlags.add(UpdateFlag.FIRST_HIT);
489 } else {
490 secondHit = hit;
491 updateFlags.add(UpdateFlag.SECOND_HIT);
492 }
493 }
494
495 public void writeDamage(Hit hit) {
496 if (isDead() || getCurrentHealth() < 1) {
497 return;
498 }
499
500 if (!damageImmunity.finished()) {
501 return;
502 }
503
504 getCombat().onDamage(hit);
505
506 if (!updateFlags.contains(UpdateFlag.FIRST_HIT)) {
507 firstHit = decrementHealth(hit);
508 updateFlags.add(UpdateFlag.FIRST_HIT);
509 } else {
510 secondHit = decrementHealth(hit);
511 updateFlags.add(UpdateFlag.SECOND_HIT);
512 }
513 }
514
515 public Hit decrementHealth(Hit hit) {
516 if (getCurrentHealth() - hit.getDamage() < 0)
517 hit.modifyDamage(damage -> getCurrentHealth());
518 skills.modifyLevel(level -> level - hit.getDamage(), Skill.HITPOINTS, 0, getCurrentHealth());
519 skills.refresh(Skill.HITPOINTS);
520 if (getCurrentHealth() < 1)
521 appendDeath();
522
523 return hit;
524 }
525
526 public void heal(int amount) {
527 int health = getCurrentHealth();
528 if (health >= getMaximumHealth())
529 return;
530 skills.modifyLevel(hp -> health + amount, Skill.HITPOINTS, 0, getMaximumHealth());
531 skills.refresh(Skill.HITPOINTS);
532 }
533
537 public void poison(PoisonType type) {
538 poisonType = type;
539 CombatUtil.effect(this, CombatEffectType.POISON);
540 }
541
545 public void venom() {
547 }
548
549 public void setForceMovement(ForceMovement forceMovement) {
550 this.forceMovement = forceMovement;
551 if (forceMovement != null)
552 this.updateFlags.add(UpdateFlag.FORCE_MOVEMENT);
553 }
554
555 public boolean inActivity() {
556 return activity != null;
557 }
558
559 public boolean inActivity(ActivityType type) {
560 return inActivity() && activity.getType() == type;
561 }
562
563 public void setActivity(Activity activity) {
564 if (this.activity != null) {
565 this.activity.cleanup();
566 }
567 this.activity = activity;
568 }
569
573 public void clearTeleportTarget() {
574 this.teleportTarget = null;
575 }
576
580 public boolean isUpdateRequired() {
581 return !updateFlags.isEmpty();
582 }
583
587 public final boolean isNpc() {
588 return getType() == EntityType.NPC;
589 }
590
594 public final boolean isNpc(BooleanInterface<Npc> condition) {
595 return getType() == EntityType.NPC && condition.activated(getNpc());
596 }
597
601 public final boolean isPlayer() {
602 return getType() == EntityType.PLAYER;
603 }
604
605 public final Npc getNpc() {
606 return (Npc) this;
607 }
608
612 public final boolean isPlayer(Function<Player, Boolean> condition) {
613 return getType() == EntityType.PLAYER && condition.apply(getPlayer());
614 }
615
616 public void takeStep() {
617 Position walkTo = getPosition();
618
620 walkTo = walkTo.west();
621 } else if (TraversalMap.isTraversable(getPosition(), Direction.EAST, width())) {
622 walkTo = walkTo.east();
623 } else if (TraversalMap.isTraversable(getPosition(), Direction.SOUTH, width())) {
624 walkTo = walkTo.north();
625 } else if (TraversalMap.isTraversable(getPosition(), Direction.NORTH, width())) {
626 walkTo = walkTo.south();
627 }
628
629 if (!getPosition().equals(walkTo)) {
630 movement.walkTo(walkTo);
631 }
632 }
633
634 public ForceMovement getForceMovement() {
635 return forceMovement;
636 }
637
638 public void unpoison() {
639 poisonDamage.set(0);
640 poisonType = null;
641
642 if (this instanceof Player) {
643 Player player = (Player) this;
644 player.send(new SendPoison(SendPoison.PoisonType.NO_POISON));
645 }
646 }
647
648 public void unvenom() {
649 venomDamage.set(0);
650
651 if (this instanceof Player) {
652 Player player = (Player) this;
653 player.send(new SendPoison(SendPoison.PoisonType.NO_POISON));
654 }
655 }
656
657 public final boolean isPoisoned() {
658 return poisonDamage.get() > 0;
659 }
660
661 public final boolean isVenomed() {
662 return venomDamage.get() > 0;
663 }
664
665 public final MutableNumber getPoisonDamage() {
666 return poisonDamage;
667 }
668
669 public MutableNumber getVenomDamage() {
670 return venomDamage;
671 }
672
673 public PoisonType getPoisonType() {
674 return poisonType;
675 }
676
677 public boolean isDead() {
678 return dead;
679 }
680
681 public void setDead(boolean dead) {
682 this.dead = dead;
683 }
684
685 public final Player getPlayer() {
686 return (Player) this;
687 }
688
689 public int getCurrentHealth() {
690 if (isNpc()) {
691 Npc npc = (Npc) this;
692 switch (npc.id) {
693 case Wintertodt.PYROMANCER, Wintertodt.INCAPACITATED_PYROMANCER -> {
694 return npc.pyroHealth;
695 }
696 }
697 }
698 return skills.getLevel(Skill.HITPOINTS);
699 }
700
701 public int getMaximumHealth() {
702 if (isNpc()) {
703 Npc npc = (Npc) this;
704 switch (npc.id) {
705 case Wintertodt.PYROMANCER, Wintertodt.INCAPACITATED_PYROMANCER -> {
706 return 24;
707 }
708 }
709 }
710 return skills.getMaxLevel(Skill.HITPOINTS);
711 }
712
713 public int[] getBonuses() {
714 return bonuses;
715 }
716
717 public int getBonus(int index) {
718 return bonuses[index];
719 }
720
721 public void setBonuses(int[] bonuses) {
722 this.bonuses = bonuses;
723 }
724
725 public void appendBonus(int index, int amount) {
726 if (bonuses == EMPTY_BONUSES)
727 bonuses = new int[EMPTY_BONUSES.length];
728 bonuses[index] += amount;
729 }
730
731 public void setBonus(int equipSlot, int bonus) {
732 if (bonuses == EMPTY_BONUSES)
733 bonuses = new int[EMPTY_BONUSES.length];
734 bonuses[equipSlot] = bonus;
735 }
736
737 public int getListIndex() {
738 return listIndex;
739 }
740
741 public void setListIndex(int listIndex) {
742 this.listIndex = listIndex;
743 }
744
745 public Optional<Animation> getAnimation() {
746 return animation;
747 }
748
749 public Optional<Graphic> getGraphic() {
750 return graphic;
751 }
752
753 public void resetAnimation() {
754 this.animation = Optional.empty();
755 }
756
757 public void resetGraphic() {
758 this.graphic = Optional.empty();
759 }
760
764 public abstract void sequence();
765
769 public abstract boolean isAutoRetaliate();
770
774 protected abstract void appendDeath();
775
779 public abstract <T extends Mob> CombatStrategy<? super T> getStrategy();
780
784 public abstract Combat<? extends Mob> getCombat();
785
786 private boolean fixingInside;
787
788 public boolean isFixingInside() {
789 return fixingInside;
790 }
791
792 public void setFixingInside(boolean fixingInside) {
793 this.fixingInside = fixingInside;
794 }
795
796 public int getPriorityIndex() {
797 return getListIndex();
798 }
799
800 public boolean hasPriorityIndex(Mob other) {
801 return getPriorityIndex() < other.getPriorityIndex();
802 }
803
804 public int getId() {
805 return id;
806 }
807
808 public int getTransformId() {
809 return transformId;
810 }
811
812}
static void schedule(Task task)
Definition World.java:284
static boolean effect(Mob mob, CombatEffectType effect)
void move(Position position)
Definition Mob.java:377
void graphic(Graphic graphic, boolean override)
Definition Mob.java:223
void face(Direction direction)
Definition Mob.java:354
abstract< T extends Mob > CombatStrategy<? super T > getStrategy()
void poison(PoisonType type)
Definition Mob.java:537
void face(GameObject object)
Definition Mob.java:326
void speak(String forceChat)
Definition Mob.java:164
final boolean isNpc(BooleanInterface< Npc > condition)
Definition Mob.java:594
void forceMove(int delay, int delay2, int animation, int animationDelay, int startSpeed, int endSpeed, Position offset, Direction direction)
Definition Mob.java:307
void animate(Animation animation)
Definition Mob.java:182
void transform(int transformId, boolean reload)
Definition Mob.java:245
void animate(Animation animation, final boolean override)
Definition Mob.java:200
abstract Combat<? extends Mob > getCombat()
void face(Position position)
Definition Mob.java:342
final boolean isPlayer(Function< Player, Boolean > condition)
Definition Mob.java:612
void graphic(Graphic graphic)
Definition Mob.java:193
static boolean isTraversable(Position from, Direction direction, int size)
Position transform(int diffX, int diffY, int diffZ)