Files
felwell-core/src/server/game/Garrison/Garrison.cpp
T
Sylvania 37796803c7 Passe de warnings sur tout le core : 12 bugs reels corriges
Scan de -fsyntax-only avec les vrais flags sur les 1767 fichiers du
serveur (compile_commands.json), warnings de logique actives. 3277
warnings, tries a la main. Le bruit (Wreorder, Wswitch) est ecarte ;
voici ce qui etait un vrai defaut.

Memoire morte et comportement indefini :
- ObjectMgr::GetGarrssionMissionReward rendait l adresse d un vecteur
  local ; Garrison melangeait puis parcourait cet objet detruit.
- BattlePayDataStoreMgr::GetProduct faisait "return{}" sur une fonction
  qui rend une reference : huit appelants lisaient un temporaire mort.
- Garrison::RewardMission reaffectait une initializer_list, ce qui ne
  prolonge pas la duree de vie du tableau sous-jacent.
- WorldSession avait un destructeur non virtuel alors que
  PlayerBotSession en derive : chaque delete d une session de bot ne
  detruisait que la moitie de l objet. Idem FieldActing.
- boss_levantus lisait Waypointspawn[6] dans un tableau de 6 entrees,
  a chaque declenchement de l evenement.
- boss_hyrja_tov modifiait expelLightSwitch deux fois sans point de
  sequence, et replanifiait son evenement toutes les 20 ms au lieu de
  20 secondes (IN_MILLISECONDS manquant).

Code jamais appele :
- cinq hooks OnRemoveTarget d areatrigger (Klaxxi, Blackfuse, Siege
  d Orgrimmar, Ordos) : le hook du core s appelle OnUnitExit, ces
  methodes n etaient rattachees a rien et les auras restaient sur le
  joueur apres sa sortie de zone. Trois d entre elles n avaient meme
  pas de return.
- boss_admiral_svirax declarait un membre bool du meme nom que
  SetDungeonEncounterID, qui masquait la methode de BossAI ; l appel
  avait ete "repare" par une virgule et ne faisait donc rien.
- WorldSession::HasSocket comparait l adresse d un tableau a NULL,
  donc rendait toujours true.

Conditions toujours vraies :
- zone_vault_of_wardens : "== QUEST_STOP_GULDAN_H || QUEST_STOP_GULDAN_A"
  jouait la scene de Gul dan pour n importe quelle quete acceptee.
- boss_vizaduum : "type == POINT_MOTION_TYPE || WAYPOINT_MOTION_TYPE".
- boss_council_of_elders : parenthese fermee au mauvais endroit,
  "HasAura(SPELL_DISCHARGE || HasAura(SPELL_OVERLOAD))".

Valeurs tronquees :
- InstanceScript::m_ScenarioStep etait un uint8 alors qu il recoit des
  ID de ScenarioStep (3195, 3207...), tronques a 135 ou 136.
- PetBattleTrainer passait 3000000000000000 a SetRespawnTime(uint32).

Nettoyage sans effet de bord : memcpy sur PetBattleRequest remplace par
une copie, et suppression d un getThreatList()/empty() sans effet dans
boss_wise_mari.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 12:59:16 +02:00

1313 lines
46 KiB
C++

/*
* This file is part of the DestinyCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Creature.h"
#include "Containers.h"
#include "DatabaseEnv.h"
#include "DB2Stores.h"
#include "Garrison.h"
#include "GarrisonAI.h"
#include "GameObject.h"
#include "GarrisonMgr.h"
#include "Log.h"
#include "Map.h"
#include "MapManager.h"
#include "ObjectMgr.h"
#include "PhasingHandler.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "SpellMgr.h"
#include "SpellInfo.h"
#include "VehicleDefines.h"
Garrison::Garrison(Player* owner) : _garrisonType(), _owner(owner), _siteLevel(nullptr), _followerActivationsRemainingToday(1)
{
_timers[GUPDATE_MISSIONS_DISTRIBUTION].SetInterval(MINUTE * IN_MILLISECONDS);
_timers[GUPDATE_WORKORDERS].SetInterval(10 * IN_MILLISECONDS);
}
bool Garrison::Create(uint32 garrSiteId)
{
GarrSiteLevelEntry const* siteLevel = sGarrisonMgr.GetGarrSiteLevelEntry(garrSiteId, 1);
if (!siteLevel)
return false;
SetSiteLevel(siteLevel);
WorldPackets::Garrison::GarrisonCreateResult garrisonCreateResult;
garrisonCreateResult.GarrSiteLevelID = _siteLevel->ID;
_owner->SendDirectMessage(garrisonCreateResult.Write());
PhasingHandler::OnConditionChange(_owner);
return true;
}
void Garrison::Update(uint32 const diff)
{
// Update the different timers
for (auto& _timer : _timers)
{
if (_timer.GetCurrent() >= 0)
_timer.Update(diff);
else
_timer.SetCurrent(0);
}
if (_timers[GUPDATE_MISSIONS_DISTRIBUTION].Passed())
{
_timers[GUPDATE_MISSIONS_DISTRIBUTION].Reset();
GenerateMissions();
}
if (_timers[GUPDATE_WORKORDERS].Passed())
{
_timers[GUPDATE_WORKORDERS].Reset();
UpdateWorkOrders();
}
}
void Garrison::Delete()
{
WorldPackets::Garrison::GarrisonDeleteResult garrisonDelete;
garrisonDelete.Result = GARRISON_SUCCESS;
garrisonDelete.GarrSiteID = _siteLevel->GarrSiteID;
_owner->SendDirectMessage(garrisonDelete.Write());
}
bool Garrison::LoadFromDB()
{
ObjectGuid::LowType lowGuid = _owner->GetGUID().GetCounter();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON);
stmt->setUInt64(0, lowGuid);
stmt->setUInt8(1, _garrisonType);
PreparedQueryResult garrisonStmt = CharacterDatabase.Query(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON_FOLLOWERS);
stmt->setUInt64(0, lowGuid);
stmt->setUInt8(1, _garrisonType);
PreparedQueryResult followersStmt = CharacterDatabase.Query(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES);
stmt->setUInt64(0, lowGuid);
stmt->setUInt8(1, _garrisonType);
PreparedQueryResult abilitiesStmt = CharacterDatabase.Query(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON_MISSIONS);
stmt->setUInt64(0, lowGuid);
stmt->setUInt8(1, _garrisonType);
PreparedQueryResult missionsStmt = CharacterDatabase.Query(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GARRISON_MISSION_REWARDS);
stmt->setUInt64(0, lowGuid);
stmt->setUInt8(1, _garrisonType);
PreparedQueryResult rewardsStmt = CharacterDatabase.Query(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GARRISON_WORKORDER);
stmt->setUInt64(0, lowGuid);
PreparedQueryResult workordersStmt = CharacterDatabase.Query(stmt);
if (!garrisonStmt)
return false;
Field* fields = garrisonStmt->Fetch();
GarrSiteLevelEntry const* siteLevel = sGarrSiteLevelStore.LookupEntry(fields[0].GetUInt32());
_followerActivationsRemainingToday = fields[1].GetUInt32();
if (!siteLevel)
return false;
SetSiteLevel(siteLevel);
// 0 1 2 3 4 5 6 7 8 9
// SELECT dbId, followerId, quality, level, itemLevelWeapon, itemLevelArmor, xp, currentBuilding, currentMission, status FROM character_garrison_followers WHERE guid = ? AND garrison_type = ?
if (followersStmt)
{
do
{
fields = followersStmt->Fetch();
uint64 dbId = fields[0].GetUInt64();
uint32 followerId = fields[1].GetUInt32();
if (!sGarrFollowerStore.LookupEntry(followerId))
continue;
_followerIds.insert(followerId);
Follower& follower = _followers[dbId];
follower.PacketInfo.DbID = dbId;
follower.PacketInfo.GarrFollowerID = followerId;
follower.PacketInfo.Quality = fields[2].GetUInt32();
follower.PacketInfo.FollowerLevel = fields[3].GetUInt32();
follower.PacketInfo.ItemLevelWeapon = fields[4].GetUInt32();
follower.PacketInfo.ItemLevelArmor = fields[5].GetUInt32();
follower.PacketInfo.Xp = fields[6].GetUInt32();
follower.PacketInfo.CurrentBuildingID = fields[7].GetUInt32();
follower.PacketInfo.CurrentMissionID = fields[8].GetUInt32();
follower.PacketInfo.FollowerStatus = fields[9].GetUInt32();
if (!sGarrBuildingStore.LookupEntry(follower.PacketInfo.CurrentBuildingID))
follower.PacketInfo.CurrentBuildingID = 0;
if (!sGarrMissionStore.LookupEntry(follower.PacketInfo.CurrentMissionID))
follower.PacketInfo.CurrentMissionID = 0;
} while (followersStmt->NextRow());
if (abilitiesStmt)
{
do
{
fields = abilitiesStmt->Fetch();
uint64 dbId = fields[0].GetUInt64();
GarrAbilityEntry const* ability = sGarrAbilityStore.LookupEntry(fields[1].GetUInt32());
if (!ability)
continue;
if (Garrison::Follower* follower = GetFollower(dbId))
follower->PacketInfo.AbilityID.push_back(ability);
} while (abilitiesStmt->NextRow());
}
}
// SELECT dbId, missionId, offerTime, startTime, status
if (missionsStmt)
{
do
{
fields = missionsStmt->Fetch();
uint64 dbId = fields[0].GetUInt64();
uint32 missionId = fields[1].GetUInt32();
GarrMissionEntry const* missionEntry = sGarrMissionStore.LookupEntry(missionId);
if (!missionEntry)
continue;
_missionIds.insert(missionId);
Mission& mission = _missions[dbId];
mission.PacketInfo.DbID = dbId;
mission.PacketInfo.MissionRecID = missionId;
mission.PacketInfo.OfferTime = time_t(fields[2].GetUInt32());
mission.PacketInfo.OfferDuration = missionEntry->OfferTime;
mission.PacketInfo.StartTime = time_t(fields[3].GetUInt32());
mission.PacketInfo.TravelDuration = missionEntry->TravelTime;
mission.PacketInfo.MissionDuration = missionEntry->Duration;
mission.PacketInfo.MissionState = fields[4].GetUInt8();
if (mission.PacketInfo.StartTime == 0)
mission.PacketInfo.StartTime = time_t(2254525440);
mission.PacketInfo.SuccessChance = sGarrisonMgr.GetMissionSuccessChance(this, missionId);
} while (missionsStmt->NextRow());
if (rewardsStmt)
{
do
{
fields = rewardsStmt->Fetch();
uint64 dbId = fields[0].GetUInt64();
uint8 type = fields[1].GetUInt8();
if (Mission* mission = GetMission(dbId))
{
WorldPackets::Garrison::GarrisonMissionReward reward;
reward.ItemID = fields[2].GetInt32();
reward.ItemQuantity = fields[3].GetUInt32();
reward.CurrencyID = fields[4].GetInt32();
reward.CurrencyQuantity = fields[5].GetUInt32();
reward.FollowerXP = fields[6].GetUInt32();
reward.BonusAbilityID = fields[7].GetUInt32();
if (type == GarrisonMission::RewardType::Normal)
mission->Rewards.push_back(reward);
else
mission->BonusRewards.push_back(reward);
}
} while (rewardsStmt->NextRow());
}
}
//SELECT id, plot_instance_id, shipment_id, creation_time, complete_time FROM character_garrison_work_order
if (workordersStmt)
{
do
{
fields = workordersStmt->Fetch();
uint64 dbId = fields[0].GetUInt64();
uint32 plotInstanceId = fields[1].GetUInt32();
//_workorderIds.insert(plotInstanceId);
WorkOrder& workorder = _workorders[dbId];
workorder.DatabaseID = dbId;
workorder.PlotInstanceID = plotInstanceId;
workorder.ShipmentID = fields[2].GetUInt32();
workorder.CreationTime = fields[3].GetUInt32();
workorder.CompleteTime = fields[4].GetUInt32();
workorder.ownerID = fields[5].GetUInt64();
} while (workordersStmt->NextRow());
}
return true;
}
void Garrison::SaveToDB(CharacterDatabaseTransaction& trans)
{
DeleteFromDB(trans);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON);
stmt->setUInt64(0, _owner->GetGUID().GetCounter());
stmt->setUInt8(1, _garrisonType);
stmt->setUInt32(2, _siteLevel->ID);
stmt->setUInt32(3, _followerActivationsRemainingToday);
trans->Append(stmt);
for (auto const& p : _followers)
{
Follower const& follower = p.second;
uint8 index = 0;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON_FOLLOWERS);
stmt->setUInt64(index++, follower.PacketInfo.DbID);
stmt->setUInt64(index++, _owner->GetGUID().GetCounter());
stmt->setUInt8(index++, _garrisonType);
stmt->setUInt32(index++, follower.PacketInfo.GarrFollowerID);
stmt->setUInt32(index++, follower.PacketInfo.Quality);
stmt->setUInt32(index++, follower.PacketInfo.FollowerLevel);
stmt->setUInt32(index++, follower.PacketInfo.ItemLevelWeapon);
stmt->setUInt32(index++, follower.PacketInfo.ItemLevelArmor);
stmt->setUInt32(index++, follower.PacketInfo.Xp);
stmt->setUInt32(index++, follower.PacketInfo.CurrentBuildingID);
stmt->setUInt32(index++, follower.PacketInfo.CurrentMissionID);
stmt->setUInt32(index++, follower.PacketInfo.FollowerStatus);
trans->Append(stmt);
uint8 slot = 0;
for (GarrAbilityEntry const* ability : follower.PacketInfo.AbilityID)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON_FOLLOWER_ABILITIES);
stmt->setUInt64(0, follower.PacketInfo.DbID);
stmt->setUInt32(1, ability->ID);
stmt->setUInt8(2, slot++);
trans->Append(stmt);
}
}
for (auto const& p : _missions)
{
Mission const& mission = p.second;
uint8 index = 0;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON_MISSIONS);
stmt->setUInt64(index++, mission.PacketInfo.DbID);
stmt->setUInt64(index++, _owner->GetGUID().GetCounter());
stmt->setUInt8(index++, _garrisonType);
stmt->setUInt32(index++, mission.PacketInfo.MissionRecID);
stmt->setUInt32(index++, mission.PacketInfo.OfferTime);
stmt->setUInt32(index++, mission.PacketInfo.StartTime != time_t(2254525440) ? mission.PacketInfo.StartTime: 0);
stmt->setUInt32(index++, mission.PacketInfo.MissionState);
trans->Append(stmt);
uint8 rewardType = GarrisonMission::RewardType::Normal;
for (auto rewards : { mission.Rewards, mission.BonusRewards })
{
for (WorldPackets::Garrison::GarrisonMissionReward reward : rewards)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_GARRISON_MISSION_REWARDS);
stmt->setUInt64(0, mission.PacketInfo.DbID);
stmt->setUInt8(1, rewardType++);
stmt->setInt32(2, reward.ItemID);
stmt->setUInt32(3, reward.ItemQuantity);
stmt->setInt32(4, reward.CurrencyID);
stmt->setUInt32(5, reward.CurrencyQuantity);
stmt->setUInt32(6, reward.FollowerXP);
stmt->setUInt32(7, reward.BonusAbilityID);
trans->Append(stmt);
}
}
}
for (auto const& p : _workorders)
{
WorkOrder const& workorder = p.second;
if ((uint32)workorder.DatabaseID == 0)
continue;
//printf("_workorders >>>DatabaseID=%d; PlotInstanceID=%d\n", (uint32)workorder.DatabaseID, workorder.PlotInstanceID);
uint8 index = 0;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GARRISON_WORKORDER);
stmt->setUInt64(index++, workorder.DatabaseID);
stmt->setUInt64(index++, _owner->GetGUID().GetCounter());
stmt->setUInt32(index++, workorder.PlotInstanceID);
stmt->setUInt32(index++, workorder.ShipmentID);
stmt->setUInt32(index++, workorder.CreationTime);
stmt->setUInt32(index++, workorder.CompleteTime);
trans->Append(stmt);
}
}
void Garrison::DeleteFromDB(CharacterDatabaseTransaction& trans)
{
DeleteFromDB(trans, _owner->GetGUID().GetCounter(), GetType());
}
void Garrison::DeleteFromDB(CharacterDatabaseTransaction& trans, ObjectGuid::LowType guid, GarrisonType garrType)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON);
stmt->setUInt64(0, guid);
stmt->setUInt8(1, garrType);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_MISSIONS);
stmt->setUInt64(0, guid);
stmt->setUInt8(1, garrType);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS);
stmt->setUInt64(0, guid);
stmt->setUInt8(1, garrType);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_BLUEPRINTS);
stmt->setUInt64(0, guid);
stmt->setUInt8(1, garrType);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_GARRISON_BUILDINGS);
stmt->setUInt64(0, guid);
stmt->setUInt8(1, garrType);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GARRISON_WORKORDERS);
stmt->setUInt64(0, guid);
trans->Append(stmt);
}
void Garrison::Enter()
{
_owner->SetCurrentGarrison(_garrisonType);
AI()->OnPlayerEnter(_owner);
}
void Garrison::Leave()
{
_owner->SetCurrentGarrison(GARRISON_TYPE_NONE);
AI()->OnPlayerLeave(_owner);
}
uint32 Garrison::GetScriptId() const
{
return sObjectMgr->GetScriptIdForGarrison(_siteLevel->ID);
}
GarrisonFactionIndex Garrison::GetFaction() const
{
return _owner->GetTeam() == HORDE ? GARRISON_FACTION_INDEX_HORDE : GARRISON_FACTION_INDEX_ALLIANCE;
}
void Garrison::SetSiteLevel(GarrSiteLevelEntry const* siteLevel)
{
_siteLevel = siteLevel;
AI_Initialize();
AI()->OnUpgrade(_owner);
}
void Garrison::AI_Initialize()
{
AI_Destroy();
GarrisonAI* ai = sScriptMgr->GetGarrisonAI(this);
if (!ai)
ai = new NullGarrisonAI(this);
_ai.reset(ai);
}
void Garrison::AI_Destroy()
{
_ai.reset();
}
void Garrison::AddFollower(uint32 garrFollowerId)
{
WorldPackets::Garrison::GarrisonAddFollowerResult addFollowerResult;
addFollowerResult.GarrTypeID = _garrisonType;
GarrFollowerEntry const* followerEntry = sGarrFollowerStore.LookupEntry(garrFollowerId);
if (_followerIds.count(garrFollowerId) || !followerEntry)
{
addFollowerResult.Result = GARRISON_ERROR_FOLLOWER_EXISTS;
_owner->SendDirectMessage(addFollowerResult.Write());
return;
}
_followerIds.insert(garrFollowerId);
uint64 dbId = sGarrisonMgr.GenerateFollowerDbId();
Follower& follower = _followers[dbId];
follower.PacketInfo.DbID = dbId;
follower.PacketInfo.GarrFollowerID = garrFollowerId;
follower.PacketInfo.Quality = followerEntry->Quality; // TODO: handle magic upgrades
follower.PacketInfo.FollowerLevel = followerEntry->FollowerLevel;
follower.PacketInfo.ItemLevelWeapon = followerEntry->ItemLevelWeapon;
follower.PacketInfo.ItemLevelArmor = followerEntry->ItemLevelArmor;
follower.PacketInfo.AbilityID = sGarrisonMgr.RollFollowerAbilities(garrFollowerId, followerEntry, follower.PacketInfo.Quality, GetFaction(), true);
addFollowerResult.Follower = follower.PacketInfo;
_owner->SendDirectMessage(addFollowerResult.Write());
_owner->UpdateCriteria(CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER, follower.PacketInfo.DbID);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
SaveToDB(trans);
CharacterDatabase.CommitTransaction(trans);
}
void Garrison::AddShipmentFollower(uint32 garrFollowerId)
{
WorldPackets::Garrison::GarrisonAddFollowerResult addFollowerResult;
addFollowerResult.GarrTypeID = _garrisonType;
GarrFollowerEntry const* followerEntry = sGarrFollowerStore.LookupEntry(garrFollowerId);
if (!followerEntry)
{
addFollowerResult.Result = GARRISON_ERROR_INVALID_FOLLOWER;
_owner->SendDirectMessage(addFollowerResult.Write());
return;
}
uint64 dbId = sGarrisonMgr.GenerateFollowerDbId();
Follower& follower = _followers[dbId];
follower.PacketInfo.DbID = dbId;
follower.PacketInfo.GarrFollowerID = garrFollowerId;
follower.PacketInfo.Quality = followerEntry->Quality; // TODO: handle magic upgrades
follower.PacketInfo.FollowerLevel = followerEntry->FollowerLevel;
follower.PacketInfo.ItemLevelWeapon = followerEntry->ItemLevelWeapon;
follower.PacketInfo.ItemLevelArmor = followerEntry->ItemLevelArmor;
follower.PacketInfo.AbilityID = sGarrisonMgr.RollFollowerAbilities(garrFollowerId, followerEntry, follower.PacketInfo.Quality, GetFaction(), true);
follower.PacketInfo.Durability = 2;
follower.PacketInfo.FollowerStatus = 24;
addFollowerResult.Follower = follower.PacketInfo;
_owner->SendDirectMessage(addFollowerResult.Write());
_owner->UpdateCriteria(CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER, follower.PacketInfo.DbID);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
SaveToDB(trans);
CharacterDatabase.CommitTransaction(trans);
}
Garrison::Follower* Garrison::GetFollower(uint64 dbId)
{
for (auto it = _followers.begin(); it != _followers.end(); ++it)
{
if ((uint32)it->second.PacketInfo.DbID == (uint32)dbId)
{
return &it->second;
}
}
return nullptr;
}
uint32 Garrison::GetActiveFollowersCount() const
{
return std::count_if(_followers.begin(), _followers.end(), [](auto followerItr)
{
return followerItr.second.PacketInfo.FollowerStatus != FOLLOWER_STATUS_INACTIVE;
});
}
uint32 Garrison::GetAverageFollowerILevel() const
{
uint32 followerIlevels = 0;
uint32 activeFollowerCount = 0;
for (auto itr : _followers)
{
if (itr.second.PacketInfo.FollowerStatus != FOLLOWER_STATUS_INACTIVE)
{
followerIlevels += itr.second.GetItemLevel();
++activeFollowerCount;
}
}
return ceil(float(followerIlevels) / float(activeFollowerCount));
}
uint32 Garrison::GetMaxFollowerLevel() const
{
uint32 maxFollowerLevel = 0;
for (auto itr : _followers)
if (itr.second.PacketInfo.FollowerStatus != FOLLOWER_STATUS_INACTIVE)
maxFollowerLevel = std::max(maxFollowerLevel, itr.second.PacketInfo.FollowerLevel);
return maxFollowerLevel;
}
void Garrison::ChangeFollowerActivationState(uint64 followerDBID, bool active)
{
Follower* follower = GetFollower(followerDBID);;
if (active)
{
if (!_owner->HasEnoughMoney((uint64)GarrisonMisc::FollowerActivationCost))
return;
if (GetNumFollowerActivationsRemaining() < 1)
return;
if (follower)
{
if (!follower->IsGarrison())
return;
if (GetActiveFollowersCount() >= GarrisonMisc::MaxActiveFollowerAllowedCount)//+ GetFollowersCountBarracksBonus()
return;
_owner->ModifyMoney(-GarrisonMisc::FollowerActivationCost);
_followerActivationsRemainingToday--;
m_NumFollowerActivationRegenTimestamp = time(0);
follower->PacketInfo.FollowerStatus = follower->PacketInfo.FollowerStatus & ~FOLLOWER_STATUS_INACTIVE;
WorldPacket data(SMSG_GARRISON_NUM_FOLLOWER_ACTIVATIONS_REMAINING, 12);
data << uint32(GetSiteLevel()->GarrSiteID);
data << uint32(GetNumFollowerActivationsRemaining());
_owner->SendDirectMessage(&data);
}
}
else
{
if (!_owner->HasEnoughMoney((uint64)GarrisonMisc::FollowerActivationCost))
return;
if (follower)
{
_owner->ModifyMoney(-GarrisonMisc::FollowerActivationCost);
follower->PacketInfo.FollowerStatus |= FOLLOWER_STATUS_INACTIVE;
WorldPacket data(SMSG_GARRISON_REMOVE_FOLLOWER_FROM_BUILDING_RESULT);
data << uint64(followerDBID);
data << uint32(GarrisonError::GARRISON_SUCCESS);
_owner->SendDirectMessage(&data);
}
}
if (!follower)
return;
follower->SendFollowerUpdate(_owner);
}
uint32 Garrison::GetNumFollowerActivationsRemaining() const
{
return _followerActivationsRemainingToday;
}
void Garrison::AddMission(uint32 garrMissionId)
{
GarrMissionEntry const* missionEntry = sGarrMissionStore.LookupEntry(garrMissionId);
if (_missionIds.count(garrMissionId) || !missionEntry)
return;
_missionIds.insert(garrMissionId);
uint64 dbId = sGarrisonMgr.GenerateMissionDbId();
Mission& mission = _missions[dbId];
mission.PacketInfo.DbID = dbId;
mission.PacketInfo.MissionRecID = garrMissionId;
mission.PacketInfo.OfferTime = time(nullptr);
mission.PacketInfo.OfferDuration = missionEntry->OfferTime;
mission.PacketInfo.MissionState = GarrisonMission::State::Offered;
mission.PacketInfo.Unknown2 = 1;
// TODO : Generate rewards for mission
WorldPackets::Garrison::GarrisonMissionReward reward;
reward.ItemID = 140587;
reward.ItemQuantity = 1;
reward.CurrencyID = 0;
reward.CurrencyQuantity = 0;
reward.FollowerXP = 0;
reward.BonusAbilityID = 0;
reward.Unknown = 1118739;
std::vector<GarrssionMissionReward> fRewards = sObjectMgr->GetGarrssionMissionReward(garrMissionId);
if (!fRewards.empty())
{
Trinity::Containers::RandomShuffle(fRewards);
for (auto _reward : fRewards)
{
// if (_reward.RewardType == GarrisonMission::MissionRewardType::Item)
{
reward.ItemID = _reward.RewardId;
reward.ItemQuantity = _reward.RewardCount;
}
// if (_reward.RewardType == GarrisonMission::MissionRewardType::Currency)
{
reward.CurrencyID = _reward.RewardId;
reward.CurrencyQuantity = _reward.RewardCount;
}
break;
}
}
/*
RewardPackXCurrencyTypeEntry const* rewardCurrencyEntry = sRewardPackXCurrencyTypeStore.LookupEntry(missionEntry->OvermaxRewardPackID);
if (rewardCurrencyEntry)
{
reward.CurrencyID = rewardCurrencyEntry->CurrencyTypeID;
reward.CurrencyQuantity = rewardCurrencyEntry->Quantity;
}
RewardPackXItemEntry const* rewardItemEntry = sRewardPackXItemStore.LookupEntry(missionEntry->OvermaxRewardPackID);
if (rewardCurrencyEntry)
{
reward.ItemID = rewardItemEntry->ItemID;
reward.ItemQuantity = rewardItemEntry->ItemQuantity;
}*/
if (reward.CurrencyID == 0 && reward.ItemID == 0)
{
// if (RewardPackXItemEntry const* rewardItem = sRewardPackXItemStore.LookupEntry(GetRandomRewardId()))
// {
// reward.ItemID = rewardItem->ItemID;
// reward.ItemQuantity = rewardItem->ItemQuantity;
// }
if (reward.ItemID == 0)
{
reward.CurrencyID = 1220;
reward.CurrencyQuantity = 100;
//reward.ItemID = 140587;
//reward.ItemQuantity = 1;
}
}
mission.Rewards.push_back(reward);
WorldPackets::Garrison::GarrisonAddMissionResult garrisonAddMissionResult;
garrisonAddMissionResult.GarrType = GetType();
garrisonAddMissionResult.Result = GarrisonMission::Result::Success;
garrisonAddMissionResult.State = GarrisonMission::State::Offered;
garrisonAddMissionResult.Mission = mission.PacketInfo;
garrisonAddMissionResult.Rewards = mission.Rewards;
garrisonAddMissionResult.BonusRewards = mission.BonusRewards;
garrisonAddMissionResult.Success = true;
_owner->SendDirectMessage(garrisonAddMissionResult.Write());
}
Garrison::Mission* Garrison::GetMission(uint64 dbId)
{
for (auto it = _missions.begin(); it != _missions.end(); ++it)
{
if ((uint32)it->second.PacketInfo.DbID == (uint32)dbId)
{
return &it->second;
}
}
return nullptr;
}
Garrison::Mission* Garrison::GetMissionByID(uint32 ID)
{
auto missionItr = std::find_if(_missions.begin(), _missions.end(), [ID](auto& itr)
{
return itr.second.PacketInfo.MissionRecID == ID;
});
if (missionItr != _missions.end())
return &missionItr->second;
return nullptr;
}
void Garrison::DeleteMission(uint64 dbId)
{
_missions.erase(dbId);
}
std::vector<Garrison::Follower*> Garrison::GetMissionFollowers(uint32 garrMissionId)
{
std::vector<Follower*> missionFollowers;
for (auto& followerItr : _followers)
if (followerItr.second.PacketInfo.CurrentMissionID == garrMissionId)
missionFollowers.push_back(&followerItr.second);
return missionFollowers;
}
bool Garrison::HasMission(uint32 garrMissionId) const
{
return std::count_if(GetMissions().begin(), GetMissions().end(), [garrMissionId](auto missionItr)
{
return missionItr.second.PacketInfo.MissionRecID == garrMissionId;
});
}
std::pair<std::vector<GarrMissionEntry const*>, std::vector<double>> Garrison::GetAvailableMissions() const
{
std::vector<GarrMissionEntry const*> availableMissions;
std::vector<double> weights;
uint32 const maxFollowerlevel = GetMaxFollowerLevel();
uint32 const activeFolowerCount = GetActiveFollowersCount();
//uint32 const averageFollowerILevel = GetAverageFollowerILevel();
for (uint32 i = 0; i < sGarrMissionStore.GetNumRows(); ++i)
{
GarrMissionEntry const* missionEntry = sGarrMissionStore.LookupEntry(i);
if (!missionEntry)
continue;
if (HasMission(missionEntry->ID))
continue;
// Most missions with Duration <= 10 are tests
if (missionEntry->Duration <= 10)
continue;
if (missionEntry->GarrTypeID != GetType())
continue;
if (missionEntry->RequiredLevel > maxFollowerlevel)
continue;
if (missionEntry->RequiredFollowersCount > activeFolowerCount)
continue;
//if (missionEntry->RequiredItemLevel > averageFollowerILevel)
// continue;
uint32 requiredClass = sGarrisonMgr.GetClassByMissionType(missionEntry->MissionType);
if (requiredClass != CLASS_NONE && requiredClass != _owner->getClass())
continue;
uint32 requiredTeam = sGarrisonMgr.GetFactionByMissionType(missionEntry->MissionType);
if (requiredTeam != TEAM_OTHER && requiredTeam != _owner->GetTeamId())
continue;
double weight = 100.0;
if (missionEntry->Flags & GarrisonMission::Flags::isRare)
weight = 25.0;
availableMissions.push_back(missionEntry);
weights.push_back(weight);
}
return std::make_pair(availableMissions, weights);
}
void Garrison::GenerateMissions()
{
uint32 maxMissionCount = ceil(GetActiveFollowersCount() * GARRISON_MISSION_DISTRIB_FOLLOWER_COEFF);
if (GetMissions().size() >= maxMissionCount)
return;
uint32 missionToAddCount = urand(0, maxMissionCount - GetMissions().size());
if (!missionToAddCount)
return;
std::pair<std::vector<GarrMissionEntry const*>, std::vector<double>> availableMissionWithWeights = GetAvailableMissions();
for (uint32 i = 0; i < missionToAddCount; ++i)
{
auto missionItr = Trinity::Containers::SelectRandomWeightedContainerElement(availableMissionWithWeights.first, availableMissionWithWeights.second);
if (missionItr == availableMissionWithWeights.first.end())
return;
AddMission((*missionItr)->ID);
// We will remove mission from available missions, also remove corresponding weight from vector
auto distance = std::distance(availableMissionWithWeights.first.cbegin(), missionItr);
auto weightItr = availableMissionWithWeights.second.begin();
std::advance(weightItr, distance);
availableMissionWithWeights.first.erase(missionItr);
availableMissionWithWeights.second.erase(weightItr);
}
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
SaveToDB(trans);
CharacterDatabase.CommitTransaction(trans);
}
Garrison::WorkOrder* Garrison::GetWorkOrder(uint64 dbId)
{
for (auto it = _workorders.begin(); it != _workorders.end(); ++it)
{
if ((uint32)it->second.DatabaseID == (uint32)dbId)
{
return &it->second;
}
}
return nullptr;
}
uint32 Garrison::GetClassHallPlotId(uint32 creatureID) const
{
uint32 garrPlotInstanceId = 0;
std::vector<GarrisonClassHallPlotGOInfo> plots = sGarrisonMgr.GetPlotClassHallByCreatureId(creatureID);
if (plots.size() > 0)
{
for (auto it = plots.begin(); it != plots.end(); it++)
{
garrPlotInstanceId = (uint32)(*it).PlotId;
return garrPlotInstanceId;
}
}
return 0;
}
uint32 Garrison::GetWorkOrderCount(uint32 plotInstanceID) const
{
return (uint32)std::count_if(_workorders.begin(), _workorders.end(), [plotInstanceID](const std::pair<uint64 /*dbId*/, Garrison::WorkOrder>& p_Order) -> bool
{
return p_Order.second.PlotInstanceID == plotInstanceID;
});
}
std::vector<Garrison::WorkOrder> Garrison::GetBuildingWorkOrders(uint32 plotInstanceID) const
{
std::vector<Garrison::WorkOrder> orders;
for (auto it = _workorders.begin(); it != _workorders.end(); ++it)
if (it->second.PlotInstanceID == plotInstanceID)
orders.push_back(it->second);
return orders;
}
uint64 Garrison::StartWorkOrder(uint32 plotInstanceID, uint32 shipmentID)
{
CharShipmentEntry const* charShipmentEntry = sCharShipmentStore.LookupEntry(shipmentID);
if (!charShipmentEntry)
return 0;
uint32 MaxCompleteTime = time(0);
for (uint32 I = 0; I < _workorders.size(); ++I)
{
if (_workorders[I].PlotInstanceID == plotInstanceID)
MaxCompleteTime = std::max<uint32>(MaxCompleteTime, _workorders[I].CompleteTime);
}
uint64 dbId = sGarrisonMgr.GenerateWorkorderDbId();
WorkOrder& workOrder = _workorders[dbId];;
workOrder.DatabaseID = dbId;
workOrder.PlotInstanceID = plotInstanceID;
workOrder.ShipmentID = shipmentID;
workOrder.CreationTime = MaxCompleteTime;
workOrder.CompleteTime = MaxCompleteTime + charShipmentEntry->Duration;
workOrder.ownerID = _owner->GetGUID().GetCounter();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
uint8 index = 0;
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GARRISON_WORKORDER);
stmt->setUInt64(index++, workOrder.DatabaseID);
stmt->setUInt64(index++, _owner->GetGUID().GetCounter());
stmt->setUInt32(index++, workOrder.PlotInstanceID);
stmt->setUInt32(index++, workOrder.ShipmentID);
stmt->setUInt32(index++, workOrder.CreationTime);
stmt->setUInt32(index++, workOrder.CompleteTime);
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
return workOrder.DatabaseID;
}
void Garrison::DeleteWorkOrder(uint64 dbId)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GARRISON_WORKORDER);
stmt->setUInt64(0, dbId);
CharacterDatabase.AsyncQuery(stmt);
for (auto it = _workorders.begin(); it != _workorders.end(); ++it)
{
if ((uint32)it->second.DatabaseID == (uint32)dbId)
{
_workorders.erase(it);
break;
}
}
}
void Garrison::RewardWorkOrder(uint32 shipmentContainerID)
{
for (uint32 i = 0; i < sCharShipmentStore.GetNumRows(); ++i)
{
if (CharShipmentEntry const* charShipmentData = sCharShipmentStore.LookupEntry(i))
{
if (charShipmentData->ShipmentContainerID == shipmentContainerID)
{
CharShipmentContainerEntry const* charShipmentContainerData = sCharShipmentContainerStore.LookupEntry(charShipmentData->ShipmentContainerID);
const SpellInfo* spell = sSpellMgr->GetSpellInfo(charShipmentData->OnCompleteSpellID);
uint32 CurrentTimeStamp = time(0);
for (auto it = _workorders.begin(); it != _workorders.end(); ++it)
{
if (it->second.CompleteTime <= CurrentTimeStamp && it->second.ShipmentID == charShipmentData->ID)
{
if (spell)
_owner->CastSpell(_owner, spell, TRIGGERED_FULL_MASK);
if (charShipmentData->DummyItemID)
_owner->AddItem(charShipmentData->DummyItemID, 1);
if (charShipmentData->GarrFollowerID)
AddShipmentFollower(charShipmentData->GarrFollowerID);
DeleteWorkOrder(it->second.DatabaseID);
}
}
break;
}
}
}
}
void Garrison::StartMission(uint32 garrMissionId, std::vector<uint64 /*DbID*/> Followers)
{
GarrMissionEntry const* missionEntry = sGarrMissionStore.LookupEntry(garrMissionId);
if (!missionEntry)
return SendStartMissionResult(false);
Mission* mission = GetMissionByID(missionEntry->ID);
if (!mission)
return SendStartMissionResult(false);
if (missionEntry->CurrencyCost && !_owner->HasCurrency(missionEntry->CurrencyID, missionEntry->CurrencyCost))
return SendStartMissionResult(false); // GARRISON_ERROR_NOT_ENOUGH_CURRENCY
mission->PacketInfo.TravelDuration = missionEntry->TravelTime;
mission->PacketInfo.MissionDuration = missionEntry->Duration;
mission->PacketInfo.StartTime = time(nullptr);
mission->PacketInfo.MissionState = GarrisonMission::State::InProgress;
mission->PacketInfo.SuccessChance = sGarrisonMgr.GetMissionSuccessChance(this, mission->PacketInfo.MissionRecID);
for (uint64 followerDbID : Followers)
{
Follower* follower = GetFollower(followerDbID);
if (!follower)
return SendStartMissionResult(false);
if (follower->PacketInfo.CurrentMissionID != 0 || follower->PacketInfo.CurrentBuildingID != 0)
return SendStartMissionResult(false);
if (follower->PacketInfo.FollowerStatus == FOLLOWER_STATUS_INACTIVE)
return SendStartMissionResult(false);
follower->PacketInfo.CurrentMissionID = missionEntry->ID;
}
_owner->ModifyCurrency(missionEntry->CurrencyID, -static_cast<int32>(missionEntry->CurrencyCost));
SendStartMissionResult(true, mission, &Followers);
}
void Garrison::SendStartMissionResult(bool success, Mission* mission /*= nullptr*/, std::vector<uint64 /*DbID*/>* Followers /*= nullptr*/)
{
WorldPackets::Garrison::GarrisonStartMissionResult garrisonStartMissionResult;
if (success)
{
garrisonStartMissionResult.Result = GarrisonMission::Result::Success;
garrisonStartMissionResult.Mission = mission->PacketInfo;
garrisonStartMissionResult.Followers = *Followers;
}
else
{
garrisonStartMissionResult.Result = GarrisonMission::Result::Fail;
}
_owner->SendDirectMessage(garrisonStartMissionResult.Write());
}
void Garrison::CompleteMission(uint32 garrMissionId)
{
GarrMissionEntry const* missionEntry = sGarrMissionStore.LookupEntry(garrMissionId);
if (!missionEntry)
return;
Mission* mission = GetMissionByID(missionEntry->ID);
if (!mission)
return;
bool canComplete = mission->PacketInfo.StartTime + mission->PacketInfo.MissionDuration < time(nullptr);
bool success = false;
if (canComplete)
{
if (GetMissionFollowers(missionEntry->ID).empty())
return;
success = roll_chance_i(mission->PacketInfo.SuccessChance);
mission->PacketInfo.MissionState = success ? GarrisonMission::State::Completed : GarrisonMission::State::Reward2Claimed;
}
WorldPackets::Garrison::GarrisonCompleteMissionResult garrisonCompleteMissionResult;
garrisonCompleteMissionResult.Result = canComplete ? GarrisonMission::Result::Success : GarrisonMission::Result::Fail;
garrisonCompleteMissionResult.MissionData = mission->PacketInfo;
garrisonCompleteMissionResult.Succeeded = success;
_owner->SendDirectMessage(garrisonCompleteMissionResult.Write());
}
void Garrison::CalculateMissonBonusRoll(uint32 garrMissionId)
{
GarrMissionEntry const* missionEntry = sGarrMissionStore.LookupEntry(garrMissionId);
if (!missionEntry)
return;
Mission* mission = GetMissionByID(missionEntry->ID);
if (!mission)
return;
bool withOvermaxReward = false;
if (mission->PacketInfo.SuccessChance > 100)
withOvermaxReward = roll_chance_i(mission->PacketInfo.SuccessChance - 100);
RewardMission(mission, withOvermaxReward);
WorldPackets::Garrison::GarrisonMissionBonusRollResult garrisonMissionBonusRollResult;
garrisonMissionBonusRollResult.Mission = mission->PacketInfo;
garrisonMissionBonusRollResult.Result = 0;
_owner->SendDirectMessage(garrisonMissionBonusRollResult.Write());
DeleteMission(mission->PacketInfo.DbID);
}
void Garrison::RewardMission(Mission* mission, bool withOvermaxReward)
{
// SylvaniaCore : reaffecter une initializer_list ne prolonge pas la duree de
// vie du tableau sous-jacent -- la boucle parcourait un temporaire detruit.
std::vector<decltype(&mission->Rewards)> rewardLists;
rewardLists.push_back(&mission->Rewards);
if (withOvermaxReward)
rewardLists.push_back(&mission->BonusRewards);
for (auto const* rewards : rewardLists)
{
for (WorldPackets::Garrison::GarrisonMissionReward reward : *rewards)
{
if (reward.ItemID)
GetOwner()->AddItem(reward.ItemID, reward.ItemQuantity);
if (reward.CurrencyID)
GetOwner()->ModifyCurrency(reward.CurrencyID, reward.CurrencyQuantity);
if (reward.FollowerXP)
{
std::vector<Follower*> followers = GetMissionFollowers(mission->PacketInfo.MissionRecID);
for (Follower* follower : followers)
{
follower->EarnXP(GetOwner(), reward.FollowerXP);
follower->PacketInfo.CurrentMissionID = 0;
}
}
//if (reward.BonusAbilityID)
// TODO
//if (reward.Unknown)
// TODO
}
}
}
uint32 Garrison::GetRandomRewardId() const
{
uint32 itemid = 0;
uint32 rollid = 0;
for (int i = 0; i < 20; i++)
{
rollid = urand(21, 123);
if (itemid > 0)
break;
}
return rollid;
}
Map* Garrison::FindMap() const
{
return sMapMgr->FindMap(_siteLevel->MapID, _owner->GetGUID().GetCounter());
}
uint32 Garrison::Follower::GetItemLevel() const
{
return (PacketInfo.ItemLevelWeapon + PacketInfo.ItemLevelArmor) / 2;
}
void Garrison::Follower::EarnXP(Player* owner, uint32 xp)
{
GarrFollowerEntry const* followerEntry = sGarrFollowerStore.LookupEntry(PacketInfo.GarrFollowerID);
if (!followerEntry)
return;
WorldPackets::Garrison::GarrisonFollowerChangeXP garrisonFollowerChangeXP;
garrisonFollowerChangeXP.OldFollower = PacketInfo;
garrisonFollowerChangeXP.XP = _EarnXP(xp);
PacketInfo.AbilityID = sGarrisonMgr.RollFollowerAbilities(PacketInfo.GarrFollowerID, followerEntry, PacketInfo.Quality, owner->GetTeam() == HORDE ? GARRISON_FACTION_INDEX_HORDE : GARRISON_FACTION_INDEX_ALLIANCE, false);
garrisonFollowerChangeXP.NewFollower = PacketInfo;
owner->SendDirectMessage(garrisonFollowerChangeXP.Write());
}
const uint32 FollowerMaxLevel[GARRISON_TYPE_MAX] =
{
0,
0,
100, // GARRISON_TYPE_GARRISON
110, // GARRISON_TYPE_CLASS_HALL
};
uint32 Garrison::Follower::_EarnXP(uint32 xp)
{
GarrFollowerEntry const* followerEntry = sGarrFollowerStore.LookupEntry(PacketInfo.GarrFollowerID);
if (!followerEntry)
return 0;
uint32 requiredLevelUpXP = GetRequiredLevelUpXP();
if (!requiredLevelUpXP)
return 0;
if (PacketInfo.Xp + xp < requiredLevelUpXP)
{
PacketInfo.Xp += xp;
return xp;
}
uint32 XPToMax = requiredLevelUpXP - PacketInfo.Xp;
PacketInfo.Xp = 0;
bool canLevelUp = PacketInfo.FollowerLevel < FollowerMaxLevel[followerEntry->GarrTypeID];
if (canLevelUp)
++PacketInfo.FollowerLevel;
else
++PacketInfo.Quality;
return xp + _EarnXP(xp - XPToMax);
}
uint32 Garrison::Follower::GetRequiredLevelUpXP() const
{
GarrFollowerEntry const* followerEntry = sGarrFollowerStore.LookupEntry(PacketInfo.GarrFollowerID);
if (!followerEntry)
return 0;
if (PacketInfo.FollowerLevel < FollowerMaxLevel[followerEntry->GarrTypeID])
{
for (uint32 i = 0; i < sGarrFollowerLevelXPStore.GetNumRows(); ++i)
if (GarrFollowerLevelXPEntry const* currentLevelData = sGarrFollowerLevelXPStore.LookupEntry(i))
if (currentLevelData->FollowerLevel == PacketInfo.FollowerLevel)
return currentLevelData->XpToNextLevel;
}
else
{
for (uint32 i = 0; i < sGarrFollowerQualityStore.GetNumRows(); ++i)
if (GarrFollowerQualityEntry const* garrFollowerQualityEntry = sGarrFollowerQualityStore.LookupEntry(i))
if (garrFollowerQualityEntry->Quality == PacketInfo.Quality)
if (garrFollowerQualityEntry->GarrFollowerTypeId == followerEntry->GarrFollowerTypeID)
return garrFollowerQualityEntry->XpToNextQuality;
}
return 0;
}
bool Garrison::Follower::HasAbility(uint32 garrAbilityId) const
{
return std::find_if(PacketInfo.AbilityID.begin(), PacketInfo.AbilityID.end(), [garrAbilityId](GarrAbilityEntry const* garrAbility)
{
return garrAbility->ID == garrAbilityId;
}) != PacketInfo.AbilityID.end();
}
GarrFollowerEntry const* Garrison::Follower::GetEntry() const
{
return sGarrFollowerStore.LookupEntry(PacketInfo.GarrFollowerID);
}
bool Garrison::Follower::IsShipyard() const
{
GarrFollowerEntry const* entry = GetEntry();
return entry && entry->GarrFollowerTypeID == GarrisonFollowerType::FOLLOWER_TYPE_SHIPYARD;
}
bool Garrison::Follower::IsGarrison() const
{
GarrFollowerEntry const* entry = GetEntry();
return entry && (entry->GarrFollowerTypeID == GarrisonFollowerType::FOLLOWER_TYPE_GARRISON || entry->GarrFollowerTypeID == GarrisonFollowerType::FOLLOWER_TYPE_CLASS_HALL);
}
void Garrison::Follower::SendFollowerUpdate(WorldSession* session) const
{
WorldPackets::Garrison::GarrisonFollowerChangedStatus followerStatus;
followerStatus.resultID = uint32(GarrisonError::GARRISON_SUCCESS);
followerStatus.followers.push_back(PacketInfo);
session->SendPacket(followerStatus.Write());
}
void Garrison::Follower::SendFollowerUpdate(Player* player) const
{
SendFollowerUpdate(player->GetSession());
}
void Garrison::UpdateWorkOrders()
{
if (!GetOwner()->IsInGarrison())
return;
if (_workorders.size() > 0)
{
for (auto const& p : _workorders)
{
WorkOrder const& workorder = p.second;
CharShipmentEntry const* charShipmentEntry = sCharShipmentStore.LookupEntry(workorder.ShipmentID);
if (charShipmentEntry == nullptr)
continue;
CharShipmentContainerEntry const* charShipmentContainerEntry = sCharShipmentContainerStore.LookupEntry(charShipmentEntry->ShipmentContainerID);
if (charShipmentContainerEntry == nullptr)
continue;
bool complete = false;
GarrisonClassHallPlotGOInfo const* GOInfo = sGarrisonMgr.GetPlotClassHallGOInfo(workorder.PlotInstanceID);
uint32 GobDisplayID = GOInfo->workDisplayId;
uint32 CurrentTimeStamp = time(0);
uint32 ShipmentsSize = (uint32)_workorders.size();
GameObject* workOrderGameObject = _owner->FindNearestGameObject(GOInfo->GameObjectId, 30.f);
if (!workOrderGameObject)
workOrderGameObject = _owner->SummonGameObject(GOInfo->GameObjectId, GOInfo->Pos, QuaternionData(), WEEK, true);
if (workorder.CompleteTime <= CurrentTimeStamp)
complete = true;
if (!complete)
{
workOrderGameObject->SetDisplayId(GobDisplayID);
workOrderGameObject->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_FREEZE_ANIMATION);
}
else
{
workOrderGameObject->SetDisplayId(GOInfo->completeDisplayId);
workOrderGameObject->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_FREEZE_ANIMATION);
workOrderGameObject->SetGoState(GO_STATE_READY);
}
}
}
}