diff options
author | Rat <none@none> | 2010-01-19 11:36:05 +0100 |
---|---|---|
committer | Rat <none@none> | 2010-01-19 11:36:05 +0100 |
commit | 0cc053ea4d42ce405a915857f75ee00f0f65666b (patch) | |
tree | 7c25955ee5db618deee963f515ba061fbb1e1e8c /src/game/ScriptedFollowerAI.cpp | |
parent | f5dea61b66a616110cfc82ff640ec448b1efa702 (diff) |
*Integrate Script system to Core
-added ScriptMgr for loading scripts
-removed bindings
-moved script system to src/game
-moved scripts to src/scripts
-VC project files updated
-cmakes updated (not 100% done yet)
NOTE to Devs:
-file locations changed
-precompiled renamed to ScriptedPch
-ecsort_ai renamed to ScriptedEscortAI
-follower_ai renamed to ScriptedFollowerAI
-guard_ai renamed to ScriptedGuardAI
-simple_ai renamed to ScriptedSimpleAI
-sc_creature renamed to ScriptedCreature
-sc_gossip renamed to ScriptedGossip
-sc_instance renamed to ScriptedInstance
*use the new headers in scripts, thank you
NOTE to ALL:
cmake not fully tested, please report any errors with it
could make creashes, incompability
USE AT YOUR OWN RISK before further tests!!
--HG--
branch : trunk
Diffstat (limited to 'src/game/ScriptedFollowerAI.cpp')
-rw-r--r-- | src/game/ScriptedFollowerAI.cpp | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/src/game/ScriptedFollowerAI.cpp b/src/game/ScriptedFollowerAI.cpp new file mode 100644 index 00000000000..b47f534556f --- /dev/null +++ b/src/game/ScriptedFollowerAI.cpp @@ -0,0 +1,387 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +/* ScriptData +SDName: FollowerAI +SD%Complete: 50 +SDComment: This AI is under development +SDCategory: Npc +EndScriptData */ + +#include "ScriptedPch.h" +#include "ScriptedFollowerAI.h" + +const float MAX_PLAYER_DISTANCE = 100.0f; + +enum ePoints +{ + POINT_COMBAT_START = 0xFFFFFF +}; + +FollowerAI::FollowerAI(Creature* pCreature) : ScriptedAI(pCreature), + m_uiLeaderGUID(0), + m_pQuestForFollow(NULL), + m_uiUpdateFollowTimer(2500), + m_uiFollowState(STATE_FOLLOW_NONE) +{} + +void FollowerAI::AttackStart(Unit* pWho) +{ + if (!pWho) + return; + + if (m_creature->Attack(pWho, true)) + { + m_creature->AddThreat(pWho, 0.0f); + m_creature->SetInCombatWith(pWho); + pWho->SetInCombatWith(m_creature); + + if (m_creature->hasUnitState(UNIT_STAT_FOLLOW)) + m_creature->clearUnitState(UNIT_STAT_FOLLOW); + + if (IsCombatMovement()) + m_creature->GetMotionMaster()->MoveChase(pWho); + } +} + +//This part provides assistance to a player that are attacked by pWho, even if out of normal aggro range +//It will cause m_creature to attack pWho that are attacking _any_ player (which has been confirmed may happen also on offi) +//The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate. +bool FollowerAI::AssistPlayerInCombat(Unit* pWho) +{ + if (!pWho || !pWho->getVictim()) + return false; + + //experimental (unknown) flag not present + if (!(m_creature->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_UNK13)) + return false; + + //not a player + if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()) + return false; + + //never attack friendly + if (m_creature->IsFriendlyTo(pWho)) + return false; + + //too far away and no free sight? + if (m_creature->IsWithinDistInMap(pWho, MAX_PLAYER_DISTANCE) && m_creature->IsWithinLOSInMap(pWho)) + { + //already fighting someone? + if (!m_creature->getVictim()) + { + AttackStart(pWho); + return true; + } + else + { + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho, 0.0f); + return true; + } + } + + return false; +} + +void FollowerAI::MoveInLineOfSight(Unit* pWho) +{ + if (!m_creature->hasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(m_creature)) + { + if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(pWho)) + return; + + if (!m_creature->canFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) + return; + + if (m_creature->IsHostileTo(pWho)) + { + float fAttackRadius = m_creature->GetAttackDistance(pWho); + if (m_creature->IsWithinDistInMap(pWho, fAttackRadius) && m_creature->IsWithinLOSInMap(pWho)) + { + if (!m_creature->getVictim()) + { + pWho->RemoveAurasDueToSpell(SPELL_AURA_MOD_STEALTH); + AttackStart(pWho); + } + else if (m_creature->GetMap()->IsDungeon()) + { + pWho->SetInCombatWith(m_creature); + m_creature->AddThreat(pWho, 0.0f); + } + } + } + } +} + +void FollowerAI::JustDied(Unit* pKiller) +{ + if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_uiLeaderGUID || !m_pQuestForFollow) + return; + + //TODO: need a better check for quests with time limit. + if (Player* pPlayer = GetLeaderForFollower()) + { + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + if (Player* pMember = pRef->getSource()) + { + if (pMember->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + pMember->FailQuest(m_pQuestForFollow->GetQuestId()); + } + } + } + else + { + if (pPlayer->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + pPlayer->FailQuest(m_pQuestForFollow->GetQuestId()); + } + } +} + +void FollowerAI::JustRespawned() +{ + m_uiFollowState = STATE_FOLLOW_NONE; + + if (!IsCombatMovement()) + SetCombatMovement(true); + + if (m_creature->getFaction() != m_creature->GetCreatureInfo()->faction_A) + m_creature->setFaction(m_creature->GetCreatureInfo()->faction_A); + + Reset(); +} + +void FollowerAI::EnterEvadeMode() +{ + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(true); + m_creature->SetLootRecipient(NULL); + + if (HasFollowState(STATE_FOLLOW_INPROGRESS)) + { + debug_log("TSCR: FollowerAI left combat, returning to CombatStartPosition."); + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + { + float fPosX, fPosY, fPosZ; + m_creature->GetPosition(fPosX, fPosY, fPosZ); + m_creature->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ); + } + } + else + { + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + m_creature->GetMotionMaster()->MoveTargetedHome(); + } + + Reset(); +} + +void FollowerAI::UpdateAI(const uint32 uiDiff) +{ + if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !m_creature->getVictim()) + { + if (m_uiUpdateFollowTimer <= uiDiff) + { + if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT)) + { + debug_log("TSCR: FollowerAI is set completed, despawns."); + m_creature->ForcedDespawn(); + return; + } + + bool bIsMaxRangeExceeded = true; + + if (Player* pPlayer = GetLeaderForFollower()) + { + if (HasFollowState(STATE_FOLLOW_RETURNING)) + { + debug_log("TSCR: FollowerAI is returning to leader."); + + RemoveFollowState(STATE_FOLLOW_RETURNING); + m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + return; + } + + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + Player* pMember = pRef->getSource(); + + if (pMember && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE)) + { + bIsMaxRangeExceeded = false; + break; + } + } + } + else + { + if (m_creature->IsWithinDistInMap(pPlayer, MAX_PLAYER_DISTANCE)) + bIsMaxRangeExceeded = false; + } + } + + if (bIsMaxRangeExceeded) + { + debug_log("TSCR: FollowerAI failed because player/group was to far away or not found"); + m_creature->ForcedDespawn(); + return; + } + + m_uiUpdateFollowTimer = 1000; + } + else + m_uiUpdateFollowTimer -= uiDiff; + } + + UpdateFollowerAI(uiDiff); +} + +void FollowerAI::UpdateFollowerAI(const uint32 uiDiff) +{ + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); +} + +void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId) +{ + if (uiMotionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS)) + return; + + if (uiPointId == POINT_COMBAT_START) + { + if (GetLeaderForFollower()) + { + if (!HasFollowState(STATE_FOLLOW_PAUSED)) + AddFollowState(STATE_FOLLOW_RETURNING); + } + else + m_creature->ForcedDespawn(); + } +} + +void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const Quest* pQuest) +{ + if (m_creature->getVictim()) + { + debug_log("TSCR: FollowerAI attempt to StartFollow while in combat."); + return; + } + + if (HasFollowState(STATE_FOLLOW_INPROGRESS)) + { + error_log("TSCR: FollowerAI attempt to StartFollow while already following."); + return; + } + + //set variables + m_uiLeaderGUID = pLeader->GetGUID(); + + if (uiFactionForFollower) + m_creature->setFaction(uiFactionForFollower); + + m_pQuestForFollow = pQuest; + + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + debug_log("TSCR: FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle."); + } + + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + + AddFollowState(STATE_FOLLOW_INPROGRESS); + + m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + + debug_log("TSCR: FollowerAI start follow %s (GUID %u)", pLeader->GetName(), m_uiLeaderGUID); +} + +Player* FollowerAI::GetLeaderForFollower() +{ + if (Player* pLeader = Unit::GetPlayer(m_uiLeaderGUID)) + { + if (pLeader->isAlive()) + return pLeader; + else + { + if (Group* pGroup = pLeader->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + Player* pMember = pRef->getSource(); + + if (pMember && pMember->isAlive() && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE)) + { + debug_log("TSCR: FollowerAI GetLeader changed and returned new leader."); + m_uiLeaderGUID = pMember->GetGUID(); + return pMember; + break; + } + } + } + } + } + + debug_log("TSCR: FollowerAI GetLeader can not find suitable leader."); + return NULL; +} + +void FollowerAI::SetFollowComplete(bool bWithEndEvent) +{ + if (m_creature->hasUnitState(UNIT_STAT_FOLLOW)) + { + m_creature->clearUnitState(UNIT_STAT_FOLLOW); + + m_creature->StopMoving(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + + if (bWithEndEvent) + AddFollowState(STATE_FOLLOW_POSTEVENT); + else + { + if (HasFollowState(STATE_FOLLOW_POSTEVENT)) + RemoveFollowState(STATE_FOLLOW_POSTEVENT); + } + + AddFollowState(STATE_FOLLOW_COMPLETE); +} + +void FollowerAI::SetFollowPaused(bool bPaused) +{ + if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE)) + return; + + if (bPaused) + { + AddFollowState(STATE_FOLLOW_PAUSED); + + if (m_creature->hasUnitState(UNIT_STAT_FOLLOW)) + { + m_creature->clearUnitState(UNIT_STAT_FOLLOW); + + m_creature->StopMoving(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + } + else + { + RemoveFollowState(STATE_FOLLOW_PAUSED); + + if (Player* pLeader = GetLeaderForFollower()) + m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } +} |