37796803c7
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>
1313 lines
46 KiB
C++
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);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|