Merge branch 'master' into Playerbot

# Conflicts:
#	src/server/game/Entities/Unit/Unit.cpp
This commit is contained in:
郑佩茹
2022-05-25 08:51:01 -06:00
33 changed files with 656 additions and 28 deletions

View File

@@ -601,6 +601,13 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_REP_CHAR_SETTINGS, "REPLACE INTO character_settings (guid, source, data) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
// Instance saved data. Stores the states of gameobjects in instances to be loaded on server start
PrepareStatement(CHAR_SELECT_INSTANCE_SAVED_DATA, "SELECT id, guid, state FROM instance_saved_go_state_data", CONNECTION_SYNCH);
PrepareStatement(CHAR_UPDATE_INSTANCE_SAVED_DATA, "UPDATE instance_saved_go_state_data SET state = ? WHERE guid = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INSERT_INSTANCE_SAVED_DATA, "INSERT INTO instance_saved_go_state_data (id, guid, state) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DELETE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SANITIZE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id NOT IN (SELECT instance.id FROM instance)", CONNECTION_ASYNC);
}
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)

View File

@@ -515,6 +515,12 @@ enum CharacterDatabaseStatements : uint32
CHAR_REP_CHAR_SETTINGS,
CHAR_DEL_CHAR_SETTINGS,
CHAR_SELECT_INSTANCE_SAVED_DATA,
CHAR_UPDATE_INSTANCE_SAVED_DATA,
CHAR_INSERT_INSTANCE_SAVED_DATA,
CHAR_DELETE_INSTANCE_SAVED_DATA,
CHAR_SANITIZE_INSTANCE_SAVED_DATA,
MAX_CHARACTERDATABASE_STATEMENTS
};

View File

@@ -62,6 +62,7 @@ DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt);
DBCStorage <CinematicCameraEntry> sCinematicCameraStore(CinematicCameraEntryfmt);
DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore(CinematicSequencesEntryfmt);
DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt);
DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore(CreatureDisplayInfoExtrafmt);
DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore(CreatureFamilyfmt);
DBCStorage <CreatureModelDataEntry> sCreatureModelDataStore(CreatureModelDatafmt);
DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore(CreatureSpellDatafmt);
@@ -290,6 +291,7 @@ void LoadDBCStores(const std::string& dataPath)
LOAD_DBC(sCinematicCameraStore, "CinematicCamera.dbc", "cinematiccamera_dbc");
LOAD_DBC(sCinematicSequencesStore, "CinematicSequences.dbc", "cinematicsequences_dbc");
LOAD_DBC(sCreatureDisplayInfoStore, "CreatureDisplayInfo.dbc", "creaturedisplayinfo_dbc");
LOAD_DBC(sCreatureDisplayInfoExtraStore, "CreatureDisplayInfoExtra.dbc", "creaturedisplayinfoextra_dbc");
LOAD_DBC(sCreatureFamilyStore, "CreatureFamily.dbc", "creaturefamily_dbc");
LOAD_DBC(sCreatureModelDataStore, "CreatureModelData.dbc", "creaturemodeldata_dbc");
LOAD_DBC(sCreatureSpellDataStore, "CreatureSpellData.dbc", "creaturespelldata_dbc");

View File

@@ -95,6 +95,7 @@ extern DBCStorage <ChrRacesEntry> sChrRacesStore;
extern DBCStorage <CinematicCameraEntry> sCinematicCameraStore;
extern DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore;
extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore;
extern DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore;
extern DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore;
extern DBCStorage <CreatureModelDataEntry> sCreatureModelDataStore;
extern DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore;

View File

@@ -329,7 +329,31 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
// GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
SetGoType(GameobjectTypes(goinfo->type));
SetGoState(go_state);
if (IsInstanceGameobject())
{
switch (GetStateSavedOnInstance())
{
case 0:
SetGoState(GO_STATE_READY);
SwitchDoorOrButton(true);
break;
case 1:
SetGoState(GO_STATE_READY);
break;
case 2:
SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
break;
default:
SetGoState(go_state);
break;
}
}
else
{
SetGoState(go_state);
}
SetGoArtKit(artKit);
SetDisplayId(goinfo->displayId);
@@ -2432,6 +2456,146 @@ void GameObject::SetGoState(GOState state)
else if (state == GO_STATE_READY)
EnableCollision(!startOpen);*/
}
/* Whenever a gameobject inside an instance changes
* save it's state on the database to be loaded properly
* on server restart or crash.
*/
if (IsInstanceGameobject() && IsAbleToSaveOnDb())
{
// Save the gameobject state on the Database
if (!FindStateSavedOnInstance())
{
SaveInstanceData(GameobjectStateToInt(&state));
}
else
{
UpdateInstanceData(GameobjectStateToInt(&state));
}
}
}
bool GameObject::IsInstanceGameobject()
{
// Avoid checking for unecessary gameobjects whose
// states don't matter for the dungeon progression
if (!ValidateGameobjectType())
{
return false;
}
if (auto* map = FindMap())
{
if (map->IsDungeon() || map->IsRaid())
{
return true;
}
}
return false;
}
bool GameObject::ValidateGameobjectType()
{
switch (m_goInfo->type)
{
case GAMEOBJECT_TYPE_DOOR:
case GAMEOBJECT_TYPE_BUTTON:
case GAMEOBJECT_TYPE_TRAP:
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING:
case GAMEOBJECT_TYPE_TRAPDOOR:
return true;
default:
return false;
}
}
uint8 GameObject::GameobjectStateToInt(GOState* state)
{
uint8 m_state = 3;
if (state)
{
switch (*state)
{
case GO_STATE_ACTIVE:
m_state = 0;
return m_state;
case GO_STATE_READY:
m_state = 1;
return m_state;
case GO_STATE_ACTIVE_ALTERNATIVE:
m_state = 2;
return m_state;
}
}
// Returning any value that is not one of the specified ones
// Which will default into the invalid part of the switch
return m_state;
}
bool GameObject::IsAbleToSaveOnDb()
{
return m_saveStateOnDb;
}
void GameObject::UpdateSaveToDb(bool enable)
{
m_saveStateOnDb = enable;
if (enable)
{
SavingStateOnDB();
}
}
void GameObject::SavingStateOnDB()
{
if (IsInstanceGameobject())
{
GOState param = GetGoState();
if (!FindStateSavedOnInstance())
{
SaveInstanceData(GameobjectStateToInt(&param));
}
}
}
void GameObject::SaveInstanceData(uint8 state)
{
uint32 id = GetInstanceId();
uint32 guid = GetSpawnId();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INSERT_INSTANCE_SAVED_DATA);
stmt->SetData(0, id);
stmt->SetData(1, guid);
stmt->SetData(2, state);
CharacterDatabase.Execute(stmt);
sObjectMgr->NewInstanceSavedGameobjectState(id, guid, state);
}
void GameObject::UpdateInstanceData(uint8 state)
{
uint32 id = GetInstanceId();
uint32 guid = GetSpawnId();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPDATE_INSTANCE_SAVED_DATA);
stmt->SetData(0, state);
stmt->SetData(1, guid);
stmt->SetData(2, id);
CharacterDatabase.Execute(stmt);
sObjectMgr->SetInstanceSavedGameobjectState(id, guid, state);
}
uint8 GameObject::GetStateSavedOnInstance()
{
return sObjectMgr->GetInstanceSavedGameobjectState(GetInstanceId(), GetSpawnId());
}
bool GameObject::FindStateSavedOnInstance()
{
return sObjectMgr->FindInstanceSavedGameobjectState(GetInstanceId(), GetSpawnId());
}
void GameObject::SetDisplayId(uint32 displayid)

View File

@@ -1011,6 +1011,25 @@ public:
static std::unordered_map<int, goEventFlag> gameObjectToEventFlag; // Gameobject -> event flag
void SaveInstanceData(uint8 state);
void UpdateInstanceData(uint8 state);
bool FindStateSavedOnInstance();
bool ValidateGameobjectType();
uint8 GetStateSavedOnInstance();
bool IsInstanceGameobject();
uint8 GameobjectStateToInt(GOState* state);
/* A check to verify if this object is available to be saved on the DB when
* a state change occurs
*/
bool IsAbleToSaveOnDb();
/* Enable or Disable the ability to save on the database this gameobject's state
* whenever it changes
*/
void UpdateSaveToDb(bool enable);
void SavingStateOnDB();
protected:
bool AIM_Initialize();
GameObjectModel* CreateModel();
@@ -1067,5 +1086,7 @@ private:
return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare);
}
GameObjectAI* m_AI;
bool m_saveStateOnDb = false;
};
#endif

View File

@@ -181,6 +181,16 @@ void Player::SendResetFailedNotify(uint32 mapid)
GetSession()->SendPacket(&data);
}
void DeleteInstanceSavedData(uint32 instanceId)
{
if (instanceId)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DELETE_INSTANCE_SAVED_DATA);
stmt->SetData(0, instanceId);
CharacterDatabase.Execute(stmt);
}
}
/// Reset all solo instances and optionally send a message on success for each
void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
{
@@ -198,7 +208,9 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
InstanceSave* instanceSave = itr->second.save;
MapEntry const* entry = sMapStore.LookupEntry(itr->first);
if (!entry || entry->IsRaid() || !instanceSave->CanReset())
{
continue;
}
Map* map = sMapMgr->FindMap(instanceSave->GetMapId(), instanceSave->GetInstanceId());
if (!map || map->ToInstanceMap()->Reset(method))
@@ -207,10 +219,16 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
toUnbind.push_back(instanceSave);
}
else
{
p->SendResetInstanceFailed(0, instanceSave->GetMapId());
}
DeleteInstanceSavedData(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
{
sInstanceSaveMgr->UnbindAllFor(*itr);
}
}
break;
case INSTANCE_RESET_CHANGE_DIFFICULTY:
@@ -225,7 +243,9 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
InstanceSave* instanceSave = itr->second.save;
MapEntry const* entry = sMapStore.LookupEntry(itr->first);
if (!entry || entry->IsRaid() != isRaid || !instanceSave->CanReset())
{
continue;
}
Map* map = sMapMgr->FindMap(instanceSave->GetMapId(), instanceSave->GetInstanceId());
if (!map || map->ToInstanceMap()->Reset(method))
@@ -234,7 +254,11 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
toUnbind.push_back(instanceSave);
}
else
{
p->SendResetInstanceFailed(0, instanceSave->GetMapId());
}
DeleteInstanceSavedData(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
sInstanceSaveMgr->UnbindAllFor(*itr);
@@ -262,6 +286,8 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
}
//else
// p->SendResetInstanceFailed(0, instanceSave->GetMapId());
DeleteInstanceSavedData(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
sInstanceSaveMgr->PlayerUnbindInstance(p->GetGUID(), (*itr)->GetMapId(), (*itr)->GetDifficulty(), true, p);

View File

@@ -20713,3 +20713,58 @@ bool Unit::SetCannotReachTarget(bool cannotReach, bool /*isChase = true*/)
return true;
}
bool Unit::IsInDisallowedMountForm() const
{
if (SpellInfo const* transformSpellInfo = sSpellMgr->GetSpellInfo(getTransForm()))
{
if (transformSpellInfo->HasAttribute(SPELL_ATTR0_ALLOW_WHILE_MOUNTED))
{
return false;
}
}
if (ShapeshiftForm form = GetShapeshiftForm())
{
SpellShapeshiftEntry const* shapeshift = sSpellShapeshiftStore.LookupEntry(form);
if (!shapeshift)
{
return true;
}
if (!(shapeshift->flags1 & 0x1))
{
return true;
}
}
if (GetDisplayId() == GetNativeDisplayId())
{
return false;
}
CreatureDisplayInfoEntry const* display = sCreatureDisplayInfoStore.LookupEntry(GetDisplayId());
if (!display)
{
return true;
}
CreatureDisplayInfoExtraEntry const* displayExtra = sCreatureDisplayInfoExtraStore.LookupEntry(display->ExtendedDisplayInfoID);
if (!displayExtra)
{
return true;
}
CreatureModelDataEntry const* model = sCreatureModelDataStore.LookupEntry(display->ModelId);
ChrRacesEntry const* race = sChrRacesStore.LookupEntry(displayExtra->DisplayRaceID);
if (model && !(model->HasFlag(CREATURE_MODEL_DATA_FLAGS_CAN_MOUNT)))
{
if (race && !(race->HasFlag(CHRRACES_FLAGS_CAN_MOUNT)))
{
return true;
}
}
return false;
}

View File

@@ -2036,18 +2036,13 @@ public:
SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
}
[[nodiscard]] inline bool IsInFeralForm() const
[[nodiscard]] bool IsInFeralForm() const
{
ShapeshiftForm form = GetShapeshiftForm();
return form == FORM_CAT || form == FORM_BEAR || form == FORM_DIREBEAR || form == FORM_GHOSTWOLF; // Xinef: added shamans Ghost Wolf, should behave exactly like druid forms
}
[[nodiscard]] inline bool IsInDisallowedMountForm() const
{
ShapeshiftForm form = GetShapeshiftForm();
return form != FORM_NONE && form != FORM_BATTLESTANCE && form != FORM_BERSERKERSTANCE && form != FORM_DEFENSIVESTANCE &&
form != FORM_SHADOW && form != FORM_STEALTH && form != FORM_UNDEAD;
}
[[nodiscard]] bool IsInDisallowedMountForm() const;
float m_modMeleeHitChance;
float m_modRangedHitChance;
@@ -2129,7 +2124,7 @@ public:
void AddInterruptMask(uint32 mask) { m_interruptMask |= mask; }
void UpdateInterruptMask();
uint32 GetDisplayId() { return GetUInt32Value(UNIT_FIELD_DISPLAYID); }
[[nodiscard]] uint32 GetDisplayId() const { return GetUInt32Value(UNIT_FIELD_DISPLAYID); }
virtual void SetDisplayId(uint32 modelId);
[[nodiscard]] uint32 GetNativeDisplayId() const { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); }
void RestoreDisplayId();

View File

@@ -9762,6 +9762,72 @@ uint32 ObjectMgr::GetQuestMoneyReward(uint8 level, uint32 questMoneyDifficulty)
return 0;
}
void ObjectMgr::LoadInstanceSavedGameobjectStateData()
{
uint32 oldMSTime = getMSTime();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SELECT_INSTANCE_SAVED_DATA);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
{
// There's no gameobject with this GUID saved on the DB
LOG_INFO("sql.sql", ">> Loaded 0 Instance saved gameobject state data. DB table `instance_saved_go_state_data` is empty.");
return;
}
Field* fields;
uint32 count = 0;
do
{
fields = result->Fetch();
GameobjectInstanceSavedStateList.push_back({ fields[0].Get<uint32>(), fields[1].Get<uint32>(), fields[2].Get<unsigned short>() });
count++;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} instance saved gameobject state data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
uint8 ObjectMgr::GetInstanceSavedGameobjectState(uint32 id, uint32 guid)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
return it->m_state;
}
}
return 3; // Any state higher than 2 to get the default state
}
bool ObjectMgr::FindInstanceSavedGameobjectState(uint32 id, uint32 guid)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
return true;
}
}
return false;
}
void ObjectMgr::SetInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
it->m_state = state;
}
}
}
void ObjectMgr::NewInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state)
{
GameobjectInstanceSavedStateList.push_back({ id, guid, state });
}
void ObjectMgr::SendServerMail(Player* player, uint32 id, uint32 reqLevel, uint32 reqPlayTime, uint32 rewardMoneyA, uint32 rewardMoneyH, uint32 rewardItemA, uint32 rewardItemCountA, uint32 rewardItemH, uint32 rewardItemCountH, std::string subject, std::string body, uint8 active) const
{
if (active)

View File

@@ -1411,6 +1411,11 @@ public:
[[nodiscard]] uint32 GetQuestMoneyReward(uint8 level, uint32 questMoneyDifficulty) const;
void SendServerMail(Player* player, uint32 id, uint32 reqLevel, uint32 reqPlayTime, uint32 rewardMoneyA, uint32 rewardMoneyH, uint32 rewardItemA, uint32 rewardItemCountA, uint32 rewardItemH, uint32 rewardItemCountH, std::string subject, std::string body, uint8 active) const;
void LoadInstanceSavedGameobjectStateData();
bool FindInstanceSavedGameobjectState(uint32 id, uint32 guid);
uint8 GetInstanceSavedGameobjectState(uint32 id, uint32 guid);
void SetInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state);
void NewInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state);
private:
// first free id for selected id type
uint32 _auctionId; // pussywizard: accessed by a single thread
@@ -1580,6 +1585,14 @@ private:
std::set<uint32> _transportMaps; // Helper container storing map ids that are for transports only, loaded from gameobject_template
QuestMoneyRewardStore _questMoneyRewards;
struct GameobjectInstanceSavedState
{
uint32 m_instance;
uint32 m_guid;
unsigned short m_state;
};
std::vector<GameobjectInstanceSavedState> GameobjectInstanceSavedStateList;
};
#define sObjectMgr ObjectMgr::instance()

View File

@@ -744,6 +744,8 @@ void Group::Disband(bool hideDestroy /* = false */)
sScriptMgr->OnGroupDisband(this);
Player* player;
uint32 instanceId = 0;
for (member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
{
if (!isBGGroup() && !isBFGroup())
@@ -753,6 +755,11 @@ void Group::Disband(bool hideDestroy /* = false */)
player = ObjectAccessor::FindConnectedPlayer(citr->guid);
if (player && !instanceId && !isBGGroup() && !isBFGroup())
{
instanceId = player->GetInstanceId();
}
_homebindIfInstance(player);
if (!isBGGroup() && !isBFGroup())
Player::ResetInstances(citr->guid, INSTANCE_RESET_GROUP_LEAVE, false);
@@ -821,6 +828,14 @@ void Group::Disband(bool hideDestroy /* = false */)
CharacterDatabase.Execute(stmt);
}
// Cleaning up instance saved data for gameobjects when a group is disbanded
if (instanceId)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DELETE_INSTANCE_SAVED_DATA);
stmt->SetData(0, instanceId);
CharacterDatabase.Execute(stmt);
}
sGroupMgr->RemoveGroup(this);
delete this;
}
@@ -2022,6 +2037,16 @@ void Group::SetRaidDifficulty(Difficulty difficulty)
}
}
void Group::ResetInstanceSavedGameobjects(uint32 instanceId)
{
if (instanceId)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DELETE_INSTANCE_SAVED_DATA);
stmt->SetData(0, instanceId);
CharacterDatabase.Execute(stmt);
}
}
void Group::ResetInstances(uint8 method, bool isRaid, Player* leader)
{
if (isBGGroup() || isBFGroup() || isLFGGroup())
@@ -2049,7 +2074,11 @@ void Group::ResetInstances(uint8 method, bool isRaid, Player* leader)
toUnbind.push_back(instanceSave);
}
else
{
leader->SendResetInstanceFailed(0, instanceSave->GetMapId());
}
ResetInstanceSavedGameobjects(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
sInstanceSaveMgr->UnbindAllFor(*itr);
@@ -2073,7 +2102,11 @@ void Group::ResetInstances(uint8 method, bool isRaid, Player* leader)
toUnbind.push_back(instanceSave);
}
else
{
leader->SendResetInstanceFailed(0, instanceSave->GetMapId());
}
ResetInstanceSavedGameobjects(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
sInstanceSaveMgr->UnbindAllFor(*itr);

View File

@@ -318,9 +318,10 @@ public:
uint32 GetDifficultyChangePreventionTime() const;
DifficultyPreventionChangeType GetDifficultyChangePreventionReason() const { return _difficultyChangePreventionType; }
void SetDifficultyChangePrevention(DifficultyPreventionChangeType type);
void DoForAllMembers(std::function<void(Player*)> const& worker);
// Reset Instance Gameobjects
void ResetInstanceSavedGameobjects(uint32 instanceId);
protected:
void _homebindIfInstance(Player* player);
void _cancelHomebindIfInstance(Player* player);

View File

@@ -238,6 +238,12 @@ bool InstanceSave::RemovePlayer(ObjectGuid guid, InstanceSaveMgr* ism)
return deleteSave;
}
void InstanceSaveMgr::SanitizeInstanceSavedData()
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SANITIZE_INSTANCE_SAVED_DATA);
CharacterDatabase.Execute(stmt);
}
void InstanceSaveMgr::LoadInstances()
{
uint32 oldMSTime = getMSTime();
@@ -270,6 +276,9 @@ void InstanceSaveMgr::LoadInstances()
LoadInstanceSaves();
LoadCharacterBinds();
// Sanitize pending rows on Instance_saved_data for data that wasn't deleted properly
SanitizeInstanceSavedData();
LOG_INFO("server.loading", ">> Loaded instances and binds in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}

View File

@@ -182,6 +182,7 @@ public:
void CopyBinds(ObjectGuid from, ObjectGuid to, Player* toPlr = nullptr);
void UnbindAllFor(InstanceSave* save);
void SanitizeInstanceSavedData();
protected:
static uint16 ResetTimeDelay[];
static PlayerBindStorage playerBindStorage;

View File

@@ -1200,13 +1200,6 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER_AREA_PARTY);
});
// Shadow Weaving
ApplySpellFix({ 15257, 15331, 15332 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER);
spellInfo->Effects[EFFECT_0].ApplyAuraName = SPELL_AURA_PROC_TRIGGER_SPELL;
});
// Hymn of Hope
ApplySpellFix({ 64904 }, [](SpellInfo* spellInfo)
{

View File

@@ -1588,6 +1588,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Instance Template...");
sObjectMgr->LoadInstanceTemplate();
LOG_INFO("server.loading", "Loading Instance Saved Gameobject State Data...");
sObjectMgr->LoadInstanceSavedGameobjectStateData();
LOG_INFO("server.loading", "Load Character Cache...");
sCharacterCache->LoadCharacterCacheStorage();

View File

@@ -32,7 +32,13 @@ enum DataTypes
enum GameObjects
{
GO_FACTORY_DOOR = 13965,
GO_IRON_CLAD_DOOR = 16397
GO_HEAVY_DOOR_1 = 17153,
GO_HEAVY_DOOR_2 = 17154,
GO_IRON_CLAD_DOOR = 16397,
GO_DOOR_LEVER_1 = 101831,
GO_DOOR_LEVER_2 = 101833,
GO_DOOR_LEVER_3 = 101834,
GO_CANNON = 16398,
};
template <class AI, class T>

View File

@@ -1,4 +1,4 @@
/*
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
@@ -39,13 +39,25 @@ public:
{
switch (gameobject->GetEntry())
{
case GO_HEAVY_DOOR_1:
case GO_HEAVY_DOOR_2:
case GO_DOOR_LEVER_1:
case GO_DOOR_LEVER_2:
case GO_DOOR_LEVER_3:
case GO_CANNON:
gameobject->UpdateSaveToDb(true);
break;
case GO_FACTORY_DOOR:
gameobject->UpdateSaveToDb(true);
if (_encounters[TYPE_RHAHK_ZOR] == DONE)
gameobject->SetGoState(GO_STATE_ACTIVE);
break;
case GO_IRON_CLAD_DOOR:
if (_encounters[TYPE_CANNON] == DONE)
HandleGameObject(ObjectGuid::Empty, true, gameobject);
gameobject->UpdateSaveToDb(true);
if (gameobject->GetStateSavedOnInstance() == GO_STATE_ACTIVE)
{
gameobject->DespawnOrUnsummon();
}
break;
}
}

View File

@@ -28,4 +28,23 @@ inline AI* GetGnomereganAI(T* obj)
return GetInstanceAI<AI>(obj, GnomereganScriptName);
}
enum DataTypes
{
TYPE_GRUBBIS = 0,
MAX_ENCOUNTERS = 1
};
enum GameObjects
{
GO_CAVE_IN_1 = 146085,
GO_CAVE_IN_2 = 146086,
GO_WORKSHOP_DOOR = 90858,
GO_FINAL_CHAMBER_DOOR = 142207,
};
enum NPCs
{
NPC_EMI_SHORTFUSE = 7998
};
#endif

View File

@@ -37,6 +37,74 @@ public:
instance_gnomeregan_InstanceMapScript(Map* map) : InstanceScript(map)
{
}
void OnCreatureCreate(Creature* creature) override
{
switch (creature->GetEntry())
{
case NPC_EMI_SHORTFUSE:
if (_encounters[TYPE_GRUBBIS] == DONE)
{
creature->DespawnOrUnsummon();
}
break;
}
}
void OnGameObjectCreate(GameObject* gameobject) override
{
switch (gameobject->GetEntry())
{
case GO_CAVE_IN_1:
case GO_CAVE_IN_2:
case GO_WORKSHOP_DOOR:
case GO_FINAL_CHAMBER_DOOR:
gameobject->UpdateSaveToDb(true);
break;
}
}
void SetData(uint32 type, uint32 data) override
{
switch (type)
{
case TYPE_GRUBBIS:
_encounters[type] = data;
break;
}
if (data == DONE)
SaveToDB();
}
std::string GetSaveData() override
{
std::ostringstream saveStream;
saveStream << "D E " << _encounters[0];
return saveStream.str();
}
void Load(const char* in) override
{
if (!in)
return;
char dataHead1, dataHead2;
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2;
if (dataHead1 == 'D' && dataHead2 == 'E')
{
for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i)
{
loadStream >> _encounters[i];
if (_encounters[i] == IN_PROGRESS)
_encounters[i] = NOT_STARTED;
}
}
}
private:
uint32 _encounters[MAX_ENCOUNTERS];
};
};

View File

@@ -62,6 +62,9 @@ public:
case GO_GATE_KIRTONOS:
GateKirtonosGUID = go->GetGUID();
break;
case GO_DOOR_OPENED_WITH_KEY:
go->UpdateSaveToDb(true);
break;
case GO_GATE_GANDLING_DOWN_NORTH:
GandlingGatesGUID[0] = go->GetGUID();
break;

View File

@@ -52,6 +52,8 @@ enum GameobjectIds
GO_BRAZIER_KIRTONOS = 175564,
GO_GATE_KIRTONOS = 175570,
GO_DOOR_OPENED_WITH_KEY = 175167,
GO_GATE_GANDLING_ENTRANCE = 177374,
GO_GATE_GANDLING_DOWN_NORTH = 177371,

View File

@@ -171,55 +171,74 @@ public:
{
switch (go->GetEntry())
{
case GO_CRUSADER_SQUARE_DOOR:
case GO_HOARD_DOOR:
case GO_HALL_OF_HIGH_COMMAND:
case GO_GAUNTLET_DOOR_1:
case GO_GAUNTLET_DOOR_2:
go->UpdateSaveToDb(true);
break;
case GO_ZIGGURAT_DOORS1:
go->UpdateSaveToDb(true);
_zigguratDoorsGUID1 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT1) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS2:
go->UpdateSaveToDb(true);
_zigguratDoorsGUID2 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT2) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS3:
go->UpdateSaveToDb(true);
_zigguratDoorsGUID3 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT3) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_GAUNTLET_GATE:
go->UpdateSaveToDb(true);
_gauntletGateGUID = go->GetGUID();
if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SLAUGTHER_GATE:
go->UpdateSaveToDb(true);
_slaughterGateGUID = go->GetGUID();
if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS4:
go->UpdateSaveToDb(true);
_zigguratDoorsGUID4 = go->GetGUID();
if (_slaughterProgress == 4)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS5:
go->UpdateSaveToDb(true);
_zigguratDoorsGUID5 = go->GetGUID();
if (_slaughterProgress == 4)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SLAUGHTER_GATE_SIDE:
go->UpdateSaveToDb(true);
if (_slaughterProgress >= 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_PORT_TRAP_GATE_1:
go->UpdateSaveToDb(true);
_trapGatesGUIDs[0] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_2:
go->UpdateSaveToDb(true);
_trapGatesGUIDs[1] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_3:
go->UpdateSaveToDb(true);
_trapGatesGUIDs[2] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_4:
go->UpdateSaveToDb(true);
_trapGatesGUIDs[3] = go->GetGUID();
break;
}

View File

@@ -57,6 +57,11 @@ enum CreatureIds
enum GameobjectIds
{
GO_CRUSADER_SQUARE_DOOR = 175967,
GO_HOARD_DOOR = 175968,
GO_HALL_OF_HIGH_COMMAND = 176194,
GO_GAUNTLET_DOOR_1 = 175357,
GO_GAUNTLET_DOOR_2 = 175356,
GO_ZIGGURAT_DOORS1 = 175380, // baroness
GO_ZIGGURAT_DOORS2 = 175379, // nerub'enkan
GO_ZIGGURAT_DOORS3 = 175381, // maleki

View File

@@ -694,10 +694,17 @@ struct ChrClassesEntry
uint32 expansion; // 59 (0 - original race, 1 - tbc addon, ...)
};
enum ChrRacesFlags
{
CHRRACES_FLAGS_NOT_PLAYABLE = 0x01,
CHRRACES_FLAGS_BARE_FEET = 0x02,
CHRRACES_FLAGS_CAN_MOUNT = 0x04
};
struct ChrRacesEntry
{
uint32 RaceID; // 0
// 1 unused
uint32 Flags; // 1
uint32 FactionID; // 2 facton template id
// 3 unused
uint32 model_m; // 4
@@ -715,6 +722,8 @@ struct ChrRacesEntry
// 64 string flags, unused
// 65-67 unused
uint32 expansion; // 68 (0 - original race, 1 - tbc addon, ...)
inline bool HasFlag(ChrRacesFlags flag) const { return (Flags & flag) != 0; }
};
struct CinematicCameraEntry
@@ -739,7 +748,7 @@ struct CreatureDisplayInfoEntry
uint32 Displayid; // 0 m_ID
uint32 ModelId; // 1 m_modelID
// 2 m_soundID
// 3 m_extendedDisplayInfoID
uint32 ExtendedDisplayInfoID; // 3
float scale; // 4 m_creatureModelScale
// 5 m_creatureModelAlpha
// 6-8 m_textureVariation[3]
@@ -752,6 +761,21 @@ struct CreatureDisplayInfoEntry
// 15 m_objectEffectPackageID
};
struct CreatureDisplayInfoExtraEntry
{
//uint32 ID; // 0
uint32 DisplayRaceID; // 1
uint32 DisplaySexID; // 2
//uint32 SkinID; // 3
//uint32 FaceID; // 4
//uint32 HairStyleID; // 5
//uint32 HairColorID; // 6
//uint32 FacialHairID; // 7
//uint32 NPCItemDisplay[11]; // 8-18
//uint32 Flags; // 19
//char const* BakeName; // 20
};
struct CreatureFamilyEntry
{
uint32 ID; // 0 m_ID
@@ -768,11 +792,16 @@ struct CreatureFamilyEntry
// 27 m_iconFile
};
enum CreatureModelDataFlags
{
CREATURE_MODEL_DATA_FLAGS_CAN_MOUNT = 0x00000080
};
struct CreatureModelDataEntry
{
uint32 Id;
//uint32 Flags;
//char const* ModelPath[16]
uint32 Flags;
//char const* ModelPath
//uint32 Unk1;
float Scale; // Used in calculation of unit collision data
//int32 Unk2
@@ -788,6 +817,8 @@ struct CreatureModelDataEntry
float CollisionHeight;
float MountHeight; // Used in calculation of unit collision data when mounted
//float Unks[11]
inline bool HasFlag(CreatureModelDataFlags flag) const { return (Flags & flag) != 0; }
};
#define MAX_CREATURE_SPELL_DATA_SLOT 4

View File

@@ -33,12 +33,13 @@ char constexpr CharSectionsEntryfmt[] = "diiixxxiii";
char constexpr CharTitlesEntryfmt[] = "nxssssssssssssssssxssssssssssssssssxi";
char constexpr ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; // ChatChannelsEntryfmt, index not used (more compact store)
char constexpr ChrClassesEntryfmt[] = "nxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii";
char constexpr ChrRacesEntryfmt[] = "nxixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
char constexpr ChrRacesEntryfmt[] = "niixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
char constexpr CinematicCameraEntryfmt[] = "nsiffff";
char constexpr CinematicSequencesEntryfmt[] = "nxixxxxxxx";
char constexpr CreatureDisplayInfofmt[] = "nixxfxxxxxxxxxxx";
char constexpr CreatureDisplayInfofmt[] = "nixifxxxxxxxxxxx";
char constexpr CreatureDisplayInfoExtrafmt[] = "diixxxxxxxxxxxxxxxxxx";
char constexpr CreatureFamilyfmt[] = "nfifiiiiixssssssssssssssssxx";
char constexpr CreatureModelDatafmt[] = "nxxxfxxxxxxxxxfffxxxxxxxxxxx";
char constexpr CreatureModelDatafmt[] = "nixxfxxxxxxxxxfffxxxxxxxxxxx";
char constexpr CreatureSpellDatafmt[] = "niiiixxxx";
char constexpr CreatureTypefmt[] = "nxxxxxxxxxxxxxxxxxx";
char constexpr CurrencyTypesfmt[] = "xnxi";