using System; using System.Diagnostics; using ACE.Entity.Enum; namespace ACE.Server.WorldObjects { partial class Creature { protected const double monsterTickInterval = 0.2; public double NextMonsterTickTime; private bool firstUpdate = true; /// /// Primary dispatch for monster think /// public void Monster_Tick(double currentUnixTime) { if (IsChessPiece && this is GamePiece gamePiece) { // faster than vtable? gamePiece.Tick(currentUnixTime); return; } if (IsPassivePet && this is Pet pet) { pet.Tick(currentUnixTime); return; } NextMonsterTickTime = currentUnixTime + monsterTickInterval; if (!IsAwake) { if (MonsterState == State.Return) MonsterState = State.Idle; if (IsFactionMob || HasFoeType) FactionMob_CheckMonsters(); return; } if (IsDead) return; if (EmoteManager.IsBusy) return; HandleFindTarget(); CheckMissHome(); // tickrate? if (AttackTarget == null && MonsterState != State.Return) { Sleep(); return; } if (MonsterState == State.Return) { Movement(); return; } var combatPet = this as CombatPet; var creatureTarget = AttackTarget as Creature; if (creatureTarget != null && (creatureTarget.IsDead || (combatPet == null && !IsVisibleTarget(creatureTarget)))) { FindNextTarget(); return; } if (firstUpdate) { if (CurrentMotionState.Stance == MotionStance.NonCombat) DoAttackStance(); if (IsAnimating) { //PhysicsObj.ShowPendingMotions(); PhysicsObj.update_object(); return; } firstUpdate = false; } // select a new weapon if missile launcher is out of ammo var weapon = GetEquippedWeapon(); /*if (weapon != null && weapon.IsAmmoLauncher) { var ammo = GetEquippedAmmo(); if (ammo == null) SwitchToMeleeAttack(); }*/ if (weapon == null && CurrentAttack != null && CurrentAttack == CombatType.Missile) { EquipInventoryItems(true); DoAttackStance(); CurrentAttack = null; } // decide current type of attack if (CurrentAttack == null) { CurrentAttack = GetNextAttackType(); MaxRange = GetMaxRange(); //if (CurrentAttack == AttackType.Magic) //MaxRange = MaxMeleeRange; // FIXME: server position sync } if (PhysicsObj.IsSticky) UpdatePosition(false); // get distance to target var targetDist = GetDistanceToTarget(); //Console.WriteLine($"{Name} ({Guid}) - Dist: {targetDist}"); if (CurrentAttack != CombatType.Missile) { if (targetDist > MaxRange || (!IsFacing(AttackTarget) && !IsSelfCast())) { // turn / move towards if (!IsTurning && !IsMoving) StartTurn(); else Movement(); } else { // perform attack if (AttackReady()) Attack(); } } else { if (IsTurning || IsMoving) { Movement(); return; } if (!IsFacing(AttackTarget)) { StartTurn(); } else if (targetDist <= MaxRange) { // perform attack if (AttackReady()) Attack(); } else { // monster switches to melee combat immediately, // if target is beyond max range? // should ranged mobs only get CurrentTargets within MaxRange? //Console.WriteLine($"{Name}.MissileAttack({AttackTarget.Name}): targetDist={targetDist}, MaxRange={MaxRange}, switching to melee"); TrySwitchToMeleeAttack(); } } // pets drawing aggro if (combatPet != null) combatPet.PetCheckMonsters(); } } }