diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index 9d4c6d758..93b48e842 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -59,6 +59,8 @@ GameObject::GameObject() : WorldObject(), m_spellId = 0; m_cooldownTime = 0; + m_captureTimer = 0; + m_packedRotation = 0; m_groupLootTimer = 0; m_groupLootId = 0; @@ -155,6 +157,10 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMa SetGoArtKit(0); // unknown what this is SetGoAnimProgress(animprogress); + // set initial data and activate non visual-only capture points + if (goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT && goinfo->capturePoint.radius) + SetCapturePointSlider(CAPTURE_SLIDER_NEUTRAL); + //Notify the map's instance data. //Only works if you create the object in it, not if it is moves to that map. //Normally non-players do not teleport to other maps. @@ -164,7 +170,7 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMa return true; } -void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) +void GameObject::Update(uint32 update_diff, uint32 p_time) { if (GetObjectGuid().IsMOTransport()) { @@ -374,6 +380,14 @@ void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) m_cooldownTime = 0; } break; + case GAMEOBJECT_TYPE_CAPTURE_POINT: + m_captureTimer += p_time; + if (m_captureTimer >= 5000) + { + TickCapturePoint(); + m_captureTimer -= 5000; + } + break; default: break; } @@ -381,25 +395,36 @@ void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) } case GO_JUST_DEACTIVATED: { - // if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed - if (GetGoType() == GAMEOBJECT_TYPE_GOOBER) + switch (GetGoType()) { - uint32 spellId = GetGOInfo()->goober.spellId; + case GAMEOBJECT_TYPE_GOOBER: + // if gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed + if (uint32 spellId = GetGOInfo()->goober.spellId) + { + for (GuidSet::const_iterator itr = m_UniqueUsers.begin(); itr != m_UniqueUsers.end(); ++itr) + { + if (Player* owner = GetMap()->GetPlayer(*itr)) + owner->CastSpell(owner, spellId, false, NULL, NULL, GetObjectGuid()); + } - if (spellId) - { + ClearAllUsesData(); + } + + SetGoState(GO_STATE_READY); + + //any return here in case battleground traps + break; + case GAMEOBJECT_TYPE_CAPTURE_POINT: + // remove capturing players because slider wont be displayed if capture point is being locked for (GuidSet::const_iterator itr = m_UniqueUsers.begin(); itr != m_UniqueUsers.end(); ++itr) { if (Player* owner = GetMap()->GetPlayer(*itr)) - owner->CastSpell(owner, spellId, false, NULL, NULL, GetObjectGuid()); + owner->SendUpdateWorldState(GetGOInfo()->capturePoint.worldState1, WORLD_STATE_REMOVE); } - ClearAllUsesData(); - } - - SetGoState(GO_STATE_READY); - - //any return here in case battleground traps + m_UniqueUsers.clear(); + SetLootState(GO_READY); + return; // SetLootState and return because go is treated as "burning flag" due to GetGoAnimProgress() being 100 and would be removed on the client } if (!HasStaticDBSpawnData()) // Remove wild summoned after use @@ -1530,79 +1555,6 @@ void GameObject::Use(Unit* user) } break; } - case GAMEOBJECT_TYPE_CAPTURE_POINT: // 29 - { - // Code here is not even halfway complete, and only added for further development. - // Computer may very well blow up after stealing your bank accounts and wreck your car. - // Use() object at own risk. - - GameObjectInfo const* info = GetGOInfo(); - - if (!info) - return; - - // Can we expect that only player object are able to trigger a capture point or - // could dummy creatures be involved? - //if (user->GetTypeId() != TYPEID_PLAYER) - //return; - - //Player* player = (Player*)user; - - // ID1 vs ID2 are possibly related to team. The world states should probably - // control which event to be used. For this to work, we need a far better system for - // sWorldStateMgr (system to store and keep track of states) so that we at all times - // know the state of every part of the world. - - // Call every event, which is obviously wrong, but can help in further development. For - // the time being script side can process events and determine which one to use. It - // require of course that some object call go->Use() - if (info->capturePoint.winEventID1) - { - if (!sScriptMgr.OnProcessEvent(info->capturePoint.winEventID1, user, this, true)) - GetMap()->ScriptsStart(sEventScripts, info->capturePoint.winEventID1, user, this); - } - if (info->capturePoint.winEventID2) - { - if (!sScriptMgr.OnProcessEvent(info->capturePoint.winEventID2, user, this, true)) - GetMap()->ScriptsStart(sEventScripts, info->capturePoint.winEventID2, user, this); - } - - if (info->capturePoint.contestedEventID1) - { - if (!sScriptMgr.OnProcessEvent(info->capturePoint.contestedEventID1, user, this, true)) - GetMap()->ScriptsStart(sEventScripts, info->capturePoint.contestedEventID1, user, this); - } - if (info->capturePoint.contestedEventID2) - { - if (!sScriptMgr.OnProcessEvent(info->capturePoint.contestedEventID2, user, this, true)) - GetMap()->ScriptsStart(sEventScripts, info->capturePoint.contestedEventID2, user, this); - } - - if (info->capturePoint.progressEventID1) - { - if (!sScriptMgr.OnProcessEvent(info->capturePoint.progressEventID1, user, this, true)) - GetMap()->ScriptsStart(sEventScripts, info->capturePoint.progressEventID1, user, this); - } - if (info->capturePoint.progressEventID2) - { - if (!sScriptMgr.OnProcessEvent(info->capturePoint.progressEventID2, user, this, true)) - GetMap()->ScriptsStart(sEventScripts, info->capturePoint.progressEventID2, user, this); - } - - if (info->capturePoint.neutralEventID1) - { - if (!sScriptMgr.OnProcessEvent(info->capturePoint.neutralEventID1, user, this, true)) - GetMap()->ScriptsStart(sEventScripts, info->capturePoint.neutralEventID1, user, this); - } - if (info->capturePoint.neutralEventID2) - { - if (!sScriptMgr.OnProcessEvent(info->capturePoint.neutralEventID2, user, this, true)) - GetMap()->ScriptsStart(sEventScripts, info->capturePoint.neutralEventID2, user, this); - } - - // Some has spell, need to process those further. - return; - } case GAMEOBJECT_TYPE_BARBER_CHAIR: // 32 { GameObjectInfo const* info = GetGOInfo(); @@ -1978,3 +1930,202 @@ bool GameObject::HasStaticDBSpawnData() const { return sObjectMgr.GetGOData(GetGUIDLow()) != NULL; } + +void GameObject::SetCapturePointSlider(int8 value) +{ + GameObjectInfo const* info = GetGOInfo(); + + switch (value) + { + case CAPTURE_SLIDER_ALLIANCE_LOCKED: + m_captureSlider = CAPTURE_SLIDER_ALLIANCE; + break; + case CAPTURE_SLIDER_HORDE_LOCKED: + m_captureSlider = CAPTURE_SLIDER_HORDE; + break; + default: + m_captureSlider = value; + SetLootState(GO_ACTIVATED); + break; + } + + // set the state of the capture point based on the slider value + if (m_captureSlider == CAPTURE_SLIDER_ALLIANCE) + m_captureState = CAPTURE_STATE_WIN_ALLIANCE; + else if (m_captureSlider == CAPTURE_SLIDER_HORDE) + m_captureState = CAPTURE_STATE_WIN_HORDE; + else if (m_captureSlider > CAPTURE_SLIDER_NEUTRAL + info->capturePoint.neutralPercent * 0.5f) + m_captureState = CAPTURE_STATE_PROGRESS_ALLIANCE; + else if (m_captureSlider < CAPTURE_SLIDER_NEUTRAL - info->capturePoint.neutralPercent * 0.5f) + m_captureState = CAPTURE_STATE_PROGRESS_HORDE; + else + m_captureState = CAPTURE_STATE_NEUTRAL; +} + +void GameObject::TickCapturePoint() +{ + // TODO: On retail: Ticks every 5.2 seconds. slider value increase when new player enters on tick + + GameObjectInfo const* info = GetGOInfo(); + float radius = info->capturePoint.radius; + + // search for players in radius + std::list capturingPlayers; + MaNGOS::AnyPlayerInObjectRangeWithOutdoorPvPCheck u_check(this, radius); + MaNGOS::PlayerListSearcher checker(capturingPlayers, u_check); + Cell::VisitWorldObjects(this, checker, radius); + + GuidSet tempUsers(m_UniqueUsers); + uint32 neutralPercent = info->capturePoint.neutralPercent; + uint32 oldValue = m_captureSlider; + int rangePlayers = 0; + + for (std::list::iterator itr = capturingPlayers.begin(); itr != capturingPlayers.end(); ++itr) + { + if ((*itr)->GetTeam() == ALLIANCE) + ++rangePlayers; + else + --rangePlayers; + + ObjectGuid guid = (*itr)->GetObjectGuid(); + if (!tempUsers.erase(guid)) + { + // new player entered capture point zone + m_UniqueUsers.insert(guid); + + // send capture point enter packets + (*itr)->SendUpdateWorldState(info->capturePoint.worldState3, neutralPercent); + (*itr)->SendUpdateWorldState(info->capturePoint.worldState2, oldValue); + (*itr)->SendUpdateWorldState(info->capturePoint.worldState1, WORLD_STATE_ADD); + (*itr)->SendUpdateWorldState(info->capturePoint.worldState2, oldValue); // also redundantly sent on retail to prevent displaying the initial capture direction on client capture slider incorrectly + } + } + + for (GuidSet::iterator itr = tempUsers.begin(); itr != tempUsers.end(); ++itr) + { + // send capture point leave packet + if (Player* owner = GetMap()->GetPlayer(*itr)) + owner->SendUpdateWorldState(info->capturePoint.worldState1, WORLD_STATE_REMOVE); + + // player left capture point zone + m_UniqueUsers.erase((*itr)); + } + + // return if there are not enough players capturing the point (works because minSuperiority is always 1) + if (rangePlayers == 0) + return; + + // cap speed + int maxSuperiority = info->capturePoint.maxSuperiority; + if (rangePlayers > maxSuperiority) + rangePlayers = maxSuperiority; + else if (rangePlayers < -maxSuperiority) + rangePlayers = -maxSuperiority; + + // time to capture from 0% to 100% is maxTime for minSuperiority amount of players and minTime for maxSuperiority amount of players (linear function: y = dy/dx*x+d) + float deltaSlider = info->capturePoint.minTime; + + if (int deltaSuperiority = maxSuperiority - info->capturePoint.minSuperiority) + deltaSlider += (float)(maxSuperiority - abs(rangePlayers)) / deltaSuperiority * (info->capturePoint.maxTime - info->capturePoint.minTime); + + // calculate changed slider value for a duration of 5 seconds (5 * 100%) + deltaSlider = 500.0f / deltaSlider; + + Team progressFaction; + if (rangePlayers > 0) + { + progressFaction = ALLIANCE; + m_captureSlider += deltaSlider; + if (m_captureSlider > CAPTURE_SLIDER_ALLIANCE) + m_captureSlider = CAPTURE_SLIDER_ALLIANCE; + } + else + { + progressFaction = HORDE; + m_captureSlider -= deltaSlider; + if (m_captureSlider < CAPTURE_SLIDER_HORDE) + m_captureSlider = CAPTURE_SLIDER_HORDE; + } + + // return if slider did not move a whole percent + if ((uint32)m_captureSlider == oldValue) + return; + + // on retail this is also sent to newly added players even though they already received a slider value + for (std::list::iterator itr = capturingPlayers.begin(); itr != capturingPlayers.end(); ++itr) + (*itr)->SendUpdateWorldState(info->capturePoint.worldState2, (uint32)m_captureSlider); + + // send capture point events + uint32 eventId = 0; + + /* WIN EVENTS */ + // alliance wins tower with max points + if (m_captureState != CAPTURE_STATE_WIN_ALLIANCE && (uint32)m_captureSlider == CAPTURE_SLIDER_ALLIANCE) + { + eventId = info->capturePoint.winEventID1; + m_captureState = CAPTURE_STATE_WIN_ALLIANCE; + } + // horde wins tower with max points + else if (m_captureState != CAPTURE_STATE_WIN_HORDE && (uint32)m_captureSlider == CAPTURE_SLIDER_HORDE) + { + eventId = info->capturePoint.winEventID2; + m_captureState = CAPTURE_STATE_WIN_HORDE; + } + + /* PROGRESS EVENTS */ + // alliance takes the tower from neutral or contested to alliance + else if ((m_captureState == CAPTURE_STATE_NEUTRAL && m_captureSlider > CAPTURE_SLIDER_NEUTRAL + neutralPercent * 0.5f) || (m_captureState == CAPTURE_STATE_CONTEST_ALLIANCE && progressFaction == ALLIANCE)) + { + eventId = info->capturePoint.progressEventID1; + + // TODO handle objective complete + + // set capture state to alliance + m_captureState = CAPTURE_STATE_PROGRESS_ALLIANCE; + } + // horde takes the tower from neutral or contested to horde + else if ((m_captureState == CAPTURE_STATE_NEUTRAL && m_captureSlider < CAPTURE_SLIDER_NEUTRAL - neutralPercent * 0.5f) || (m_captureState == CAPTURE_STATE_CONTEST_HORDE && progressFaction == HORDE)) + { + eventId = info->capturePoint.progressEventID2; + + // TODO handle objective complete + + // set capture state to horde + m_captureState = CAPTURE_STATE_PROGRESS_HORDE; + } + + /* NEUTRAL EVENTS */ + // alliance takes the tower from horde to neutral + else if (m_captureState != CAPTURE_STATE_NEUTRAL && m_captureSlider >= CAPTURE_SLIDER_NEUTRAL - neutralPercent * 0.5f && m_captureSlider <= CAPTURE_SLIDER_NEUTRAL + neutralPercent * 0.5f && progressFaction == ALLIANCE) + { + eventId = info->capturePoint.neutralEventID1; + m_captureState = CAPTURE_STATE_NEUTRAL; + } + // horde takes the tower from alliance to neutral + else if (m_captureState != CAPTURE_STATE_NEUTRAL && m_captureSlider >= CAPTURE_SLIDER_NEUTRAL - neutralPercent * 0.5f && m_captureSlider <= CAPTURE_SLIDER_NEUTRAL + neutralPercent * 0.5f && progressFaction == HORDE) + { + eventId = info->capturePoint.neutralEventID2; + m_captureState = CAPTURE_STATE_NEUTRAL; + } + + /* CONTESTED EVENTS */ + // alliance attacks tower which is in control or progress by horde (except if alliance also gains control in that case) + else if ((m_captureState == CAPTURE_STATE_WIN_HORDE || m_captureState == CAPTURE_STATE_PROGRESS_HORDE) && progressFaction == ALLIANCE) + { + eventId = info->capturePoint.contestedEventID1; + m_captureState = CAPTURE_STATE_CONTEST_HORDE; + } + // horde attacks tower which is in control or progress by alliance (except if horde also gains control in that case) + else if ((m_captureState == CAPTURE_STATE_WIN_ALLIANCE || m_captureState == CAPTURE_STATE_PROGRESS_ALLIANCE) && progressFaction == HORDE) + { + eventId = info->capturePoint.contestedEventID2; + m_captureState = CAPTURE_STATE_CONTEST_ALLIANCE; + } + + if (eventId) + { + // Send script event to SD2 and database as well - this can be used for summoning creatures, casting specific spells or spawning GOs + if (!sScriptMgr.OnProcessEvent(eventId, this, this, true)) + GetMap()->ScriptsStart(sEventScripts, eventId, this, this); + } +} diff --git a/src/game/GameObject.h b/src/game/GameObject.h index 802f2e82b..245e72f0e 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -310,7 +310,7 @@ struct GameObjectInfo uint32 radius; //0 uint32 spell; //1 uint32 worldState1; //2 - uint32 worldstate2; //3 + uint32 worldState2; //3 uint32 winEventID1; //4 uint32 winEventID2; //5 uint32 contestedEventID1; //6 @@ -320,7 +320,7 @@ struct GameObjectInfo uint32 neutralEventID1; //10 uint32 neutralEventID2; //11 uint32 neutralPercent; //12 - uint32 worldstate3; //13 + uint32 worldState3; //13 uint32 minSuperiority; //14 uint32 maxSuperiority; //15 uint32 minTime; //16 @@ -595,6 +595,34 @@ enum LootState GO_JUST_DEACTIVATED }; +// TODO: Move this somewhere else +enum WorldStateType +{ + WORLD_STATE_REMOVE = 0, + WORLD_STATE_ADD = 1 +}; + +enum CapturePointState +{ + CAPTURE_STATE_NEUTRAL = 0, + CAPTURE_STATE_PROGRESS_ALLIANCE, + CAPTURE_STATE_PROGRESS_HORDE, + CAPTURE_STATE_CONTEST_ALLIANCE, + CAPTURE_STATE_CONTEST_HORDE, + CAPTURE_STATE_WIN_ALLIANCE, + CAPTURE_STATE_WIN_HORDE +}; + +enum CapturePointSlider +{ + CAPTURE_SLIDER_ALLIANCE = 100, // full alliance + CAPTURE_SLIDER_HORDE = 0, // full horde + CAPTURE_SLIDER_NEUTRAL = 50, // middle + + CAPTURE_SLIDER_ALLIANCE_LOCKED = -1, // used to store additional information + CAPTURE_SLIDER_HORDE_LOCKED = -2 +}; + class Unit; struct GameObjectDisplayInfoEntry; @@ -749,6 +777,8 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject GameObject* LookupFishingHoleAround(float range); + void SetCapturePointSlider(int8 value); + GridReference &GetGridRef() { return m_gridRef; } protected: @@ -760,6 +790,10 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction). // For traps/goober this: spell casting cooldown, for doors/buttons: reset time. + uint32 m_captureTimer; // (msecs) timer used for capture points + float m_captureSlider; + CapturePointState m_captureState; + GuidSet m_SkillupSet; // players that already have skill-up at GO use uint32 m_useTimes; // amount uses/charges triggered @@ -782,6 +816,7 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject private: void SwitchDoorOrButton(bool activate, bool alternative = false); + void TickCapturePoint(); GridReference m_gridRef; }; diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 238442e3c..c57542a73 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "12059" + #define REVISION_NR "12060" #endif // __REVISION_NR_H__