void CAIPetDummy::CheckCurrentAction(uint32 tick) { m_Tick = tick; //uncharm any pets if time is up if(tick > m_PPet->charmTime && m_PPet->isCharmed) { petutils::DespawnPet(m_PPet->PMaster); return; } switch(m_ActionType) { case ACTION_NONE: break; case ACTION_ROAMING: ActionRoaming(); break; case ACTION_DEATH: ActionDeath(); break; case ACTION_SPAWN: ActionSpawn(); break; case ACTION_FALL: ActionFall(); break; case ACTION_ENGAGE: ActionEngage(); break; case ACTION_ATTACK: ActionAttack(); break; case ACTION_SLEEP: ActionSleep(); break; case ACTION_DISENGAGE: ActionDisengage(); break; case ACTION_MOBABILITY_START: ActionAbilityStart(); break; case ACTION_MOBABILITY_USING: ActionAbilityUsing(); break; case ACTION_MOBABILITY_FINISH: ActionAbilityFinish(); break; case ACTION_MOBABILITY_INTERRUPT: ActionAbilityInterrupt(); break; case ACTION_MAGIC_START: ActionMagicStart(); break; case ACTION_MAGIC_CASTING: ActionMagicCasting(); break; case ACTION_MAGIC_FINISH: ActionMagicFinish(); break; default : DSP_DEBUG_BREAK_IF(true); } }
void CAIAutomatonDummy::CheckCurrentAction(uint32 tick) { m_Tick = tick; CBattleEntity* PSelf = m_PPet; switch (m_ActionType) { case ACTION_NONE: break; case ACTION_ROAMING: ActionRoaming(); break; case ACTION_DEATH: ActionDeath(); break; case ACTION_SPAWN: ActionSpawn(); break; case ACTION_FALL: ActionFall(); break; case ACTION_ENGAGE: ActionEngage(); break; case ACTION_ATTACK: ActionAttack(); break; case ACTION_SLEEP: ActionSleep(); break; case ACTION_DISENGAGE: ActionDisengage(); break; case ACTION_MOBABILITY_START: ActionAbilityStart(); break; case ACTION_MOBABILITY_USING: ActionAbilityUsing(); break; case ACTION_MOBABILITY_FINISH: ActionAbilityFinish(); break; case ACTION_MOBABILITY_INTERRUPT: ActionAbilityInterrupt(); break; case ACTION_MAGIC_START: ActionMagicStart(); break; case ACTION_MAGIC_CASTING: ActionMagicCasting(); break; case ACTION_MAGIC_FINISH: ActionMagicFinish(); break; default: DSP_DEBUG_BREAK_IF(true); } //check if this AI was replaced (the new AI will update if this is the case) if (m_PPet && PSelf->PBattleAI == this) { m_PPet->UpdateEntity(); } }
void CAIPetDummy::ActionRoaming() { if( m_PPet->PMaster==NULL || m_PPet->PMaster->isDead()){ m_ActionType = ACTION_FALL; ActionFall(); return; } //wyvern behaviour if(m_PPet->getPetType()==PETTYPE_WYVERN){ if(WyvernIsHealing()){ m_PPathFind->LookAt(m_PPet->PMaster->loc.p); m_PPet->loc.zone->PushPacket(m_PPet, CHAR_INRANGE, new CEntityUpdatePacket(m_PPet, ENTITY_UPDATE)); if(m_PPet->PMaster->objtype == TYPE_PC){ ((CCharEntity*)m_PPet->PMaster)->pushPacket(new CPetSyncPacket((CCharEntity*)m_PPet->PMaster)); } return; } } if(m_PBattleTarget!=NULL){ m_ActionType = ACTION_ENGAGE; ActionEngage(); return; } float currentDistance = distance(m_PPet->loc.p, m_PPet->PMaster->loc.p); // this is broken until pet / mob relationship gets fixed // pets need to extend mob or be a mob because pet has no spell list! if(m_PPet->getPetType() == PETTYPE_AVATAR && m_PPet->m_Family == 104 && m_Tick >= m_LastActionTime + 30000 && currentDistance < PET_ROAM_DISTANCE * 2) { int16 spellID = 108; // define this so action picks it up m_PSpell = spell::GetSpell(spellID); m_PBattleSubTarget = m_PPet->PMaster; m_ActionType = ACTION_MAGIC_START; ActionMagicStart(); return; } if (currentDistance > PET_ROAM_DISTANCE) { if(currentDistance < 35.0f && m_PPathFind->PathAround(m_PPet->PMaster->loc.p, 2.0f, PATHFLAG_RUN | PATHFLAG_WALLHACK)) { m_PPathFind->FollowPath(); } else { m_PPathFind->WarpTo(m_PPet->PMaster->loc.p, PET_ROAM_DISTANCE); } m_PPet->loc.zone->PushPacket(m_PPet, CHAR_INRANGE, new CEntityUpdatePacket(m_PPet, ENTITY_UPDATE)); } }
void CAIPetDummy::ActionRoaming() { if (m_PPet->PMaster == nullptr || m_PPet->PMaster->isDead()){ m_ActionType = ACTION_FALL; ActionFall(); return; } //automaton, wyvern if (m_PPet->getPetType() == PETTYPE_WYVERN || m_PPet->getPetType() == PETTYPE_AUTOMATON){ if (PetIsHealing()){ return; } } if (m_PBattleTarget != nullptr){ m_ActionType = ACTION_ENGAGE; ActionEngage(); return; } float currentDistance = distance(m_PPet->loc.p, m_PPet->PMaster->loc.p); // this is broken until pet / mob relationship gets fixed // pets need to extend mob or be a mob because pet has no spell list! if (m_PPet->getPetType() == PETTYPE_AVATAR && m_PPet->m_Family == 104 && m_Tick >= m_LastActionTime + 30000 && currentDistance < PET_ROAM_DISTANCE * 2) { int16 spellID = 108; // define this so action picks it up SetCurrentSpell(spellID); m_PBattleSubTarget = m_PPet->PMaster; m_ActionType = ACTION_MAGIC_START; ActionMagicStart(); return; } if (currentDistance > PET_ROAM_DISTANCE) { if (currentDistance < 35.0f && m_PPathFind->PathAround(m_PPet->PMaster->loc.p, 2.0f, PATHFLAG_RUN | PATHFLAG_WALLHACK)) { m_PPathFind->FollowPath(); } else if (m_PPet->GetSpeed() > 0) { m_PPathFind->WarpTo(m_PPet->PMaster->loc.p, PET_ROAM_DISTANCE); } } }
void CAIAutomatonDummy::ActionAttack() { if (m_PPet->PMaster == nullptr || m_PPet->PMaster->isDead() || m_PPet->isDead()){ m_ActionType = ACTION_FALL; ActionFall(); return; } //disengage a target that is dead or charmed if ((m_PBattleTarget == nullptr || m_PBattleTarget->isDead() || m_PBattleTarget->animation == ANIMATION_CHOCOBO) || (m_PBattleTarget != nullptr && m_PBattleTarget->objtype == TYPE_MOB && m_PBattleTarget->PMaster != nullptr && m_PBattleTarget->PMaster->objtype == TYPE_PC)) { m_ActionType = ACTION_DISENGAGE; ActionDisengage(); return; } m_PPathFind->LookAt(m_PBattleTarget->loc.p); float currentDistance = distance(m_PPet->loc.p, m_PBattleTarget->loc.p); if (CheckSpellcast()) { m_ActionType = ACTION_MAGIC_START; ActionMagicStart(); return; } else if (CheckTPMove()) { m_ActionType = ACTION_MOBABILITY_START; ActionAbilityStart(); return; } else if (CheckRangedAttack()) { //TODO: set ID to ranged attack m_ActionType = ACTION_MOBABILITY_FINISH; ActionAbilityFinish(); m_LastRangedTime = m_Tick; return; } //go to target if its too far away if (currentDistance > m_PBattleTarget->m_ModelSize && m_PPet->speed != 0) { if (m_PPathFind->PathAround(m_PBattleTarget->loc.p, 2.0f, PATHFLAG_RUN | PATHFLAG_WALLHACK)) { m_PPathFind->FollowPath(); // recalculate currentDistance = distance(m_PPet->loc.p, m_PBattleTarget->loc.p); } } if (currentDistance <= m_PBattleTarget->m_ModelSize) { int32 WeaponDelay = m_PPet->m_Weapons[SLOT_MAIN]->getDelay(); //try to attack if (m_Tick > m_LastActionTime + WeaponDelay){ if (battleutils::IsParalyzed(m_PPet)) { m_PPet->loc.zone->PushPacket(m_PPet, CHAR_INRANGE, new CMessageBasicPacket(m_PPet, m_PBattleTarget, 0, 0, 29)); } else if (battleutils::IsIntimidated(m_PPet, m_PBattleTarget)) { m_PPet->loc.zone->PushPacket(m_PPet, CHAR_INRANGE, new CMessageBasicPacket(m_PPet, m_PBattleTarget, 0, 0, 106)); } else { apAction_t Action; m_PPet->m_ActionList.clear(); Action.ActionTarget = m_PBattleTarget; uint8 numAttacks = battleutils::CheckMultiHits(m_PPet, m_PPet->m_Weapons[SLOT_MAIN]); for (uint8 i = 0; i<numAttacks; i++){ Action.reaction = REACTION_EVADE; Action.speceffect = SPECEFFECT_NONE; Action.animation = 0; Action.param = 0; Action.messageID = 15; Action.knockback = 0; //ShowDebug("pet hp %i and atk %i def %i eva is %i \n",m_PPet->health.hp,m_PPet->ATT(),m_PPet->DEF(),m_PPet->getMod(MOD_EVA)); int32 damage = 0; if (m_PBattleTarget->StatusEffectContainer->HasStatusEffect(EFFECT_PERFECT_DODGE)) { Action.messageID = 32; } else if ((WELL512::GetRandomNumber(100) < battleutils::GetHitRate(m_PPet, m_PBattleTarget)) && !m_PBattleTarget->StatusEffectContainer->HasStatusEffect(EFFECT_ALL_MISS)) { if (battleutils::IsAbsorbByShadow(m_PBattleTarget)) { Action.messageID = 31; Action.param = 1; Action.reaction = REACTION_EVADE; } else { Action.reaction = REACTION_HIT; Action.speceffect = SPECEFFECT_HIT; Action.messageID = 1; bool isCritical = (WELL512::GetRandomNumber(100) < battleutils::GetCritHitRate(m_PPet, m_PBattleTarget, false)); float DamageRatio = battleutils::GetDamageRatio(m_PPet, m_PBattleTarget, isCritical, 0); if (isCritical) { Action.speceffect = SPECEFFECT_CRITICAL_HIT; Action.messageID = 67; } damage = (int32)((m_PPet->GetMainWeaponDmg() + battleutils::GetFSTR(m_PPet, m_PBattleTarget, SLOT_MAIN)) * DamageRatio); } } else { // create enmity even on misses if (m_PBattleTarget->objtype == TYPE_MOB){ CMobEntity* PMob = (CMobEntity*)m_PBattleTarget; PMob->PEnmityContainer->UpdateEnmity(m_PPet, 0, 0); } } if (m_PBattleTarget->objtype == TYPE_PC) { charutils::TrySkillUP((CCharEntity*)m_PBattleTarget, SKILL_EVA, m_PPet->GetMLevel()); } if (m_PPet->PMaster && m_PPet->PMaster->objtype == TYPE_PC) { puppetutils::TrySkillUP((CAutomatonEntity*)m_PPet, SKILL_AME, m_PBattleTarget->GetMLevel()); } bool isBlocked = (WELL512::GetRandomNumber(100) < battleutils::GetBlockRate(m_PPet, m_PBattleTarget)); if (isBlocked){ Action.reaction = REACTION_BLOCK; } Action.param = battleutils::TakePhysicalDamage(m_PPet, m_PBattleTarget, damage, isBlocked, SLOT_MAIN, 1, nullptr, true, true); if (Action.param < 0) { Action.param = -(Action.param); Action.messageID = 373; } // spike effect if (Action.reaction != REACTION_EVADE && Action.reaction != REACTION_PARRY) { battleutils::HandleEnspell(m_PPet, m_PBattleTarget, &Action, i, m_PPet->m_Weapons[SLOT_MAIN], damage); battleutils::HandleSpikesDamage(m_PPet, m_PBattleTarget, &Action, damage); } m_PPet->m_ActionList.push_back(Action); } m_PPet->loc.zone->PushPacket(m_PPet, CHAR_INRANGE, new CActionPacket(m_PPet)); if (m_PPet->PMaster != nullptr && m_PPet->PMaster->objtype == TYPE_PC && m_PPet->PMaster->PPet != nullptr){ ((CCharEntity*)m_PPet->PMaster)->pushPacket(new CPetSyncPacket((CCharEntity*)m_PPet->PMaster)); } } m_LastActionTime = m_Tick; // Update the targets attacker level.. CMobEntity* Monster = (CMobEntity*)m_PBattleTarget; if (Monster->m_HiPCLvl < ((CCharEntity*)m_PPet->PMaster)->GetMLevel()) Monster->m_HiPCLvl = ((CCharEntity*)m_PPet->PMaster)->GetMLevel(); } } }