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
@@ -0,0 +1,9 @@
-- DB update 2022_04_28_00 -> 2022_05_24_00
DROP TABLE IF EXISTS `instance_saved_go_state_data`;
CREATE TABLE IF NOT EXISTS `instance_saved_go_state_data` (
`id` INT UNSIGNED NOT NULL COMMENT 'instance.id',
`guid` INT UNSIGNED NOT NULL COMMENT 'gameobject.guid',
`state` TINYINT UNSIGNED DEFAULT '0' COMMENT 'gameobject.state',
PRIMARY KEY (`id`, `guid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -0,0 +1,5 @@
-- DB update 2022_05_23_05 -> 2022_05_24_00
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 7361) AND (`source_type` = 0) AND (`id` IN (1));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(7361, 0, 1, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 34, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Grubbis - On Just Died - Set Instance Data 0 to 3');
@@ -0,0 +1,11 @@
-- DB update 2022_05_24_00 -> 2022_05_25_00
-- Insert missing Bear Flank drop rate for Rabid Shardtooth
DELETE FROM `creature_loot_template` WHERE `Entry` = 7446 AND `Item` = 35562;
INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Chance`, `Comment`) VALUES (7446, 35562, 65, 'Rabid Shardtooth - Bear Flank');
-- Update Bears from level 47 to 60 with correct Bear Flank drop rates
UPDATE `creature_loot_template` SET `Chance` = 45 WHERE `Item` = 35562 AND `Entry` = 8957;
UPDATE `creature_loot_template` SET `Chance` = 50 WHERE `Item` = 35562 AND `Entry` IN(8956, 1815, 5274);
UPDATE `creature_loot_template` SET `Chance` = 55 WHERE `Item` = 35562 AND `Entry` IN(7444, 7443);
UPDATE `creature_loot_template` SET `Chance` = 60 WHERE `Item` = 35562 AND `Entry` = 8958;
UPDATE `creature_loot_template` SET `Chance` = 65 WHERE `Item` = 35562 AND `Entry` IN(1816, 7446, 7445);
@@ -0,0 +1,8 @@
-- DB update 2022_05_25_00 -> 2022_05_25_01
-- Commander Felstrom, set unlootable if resurrection goes through and despawn body after
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 771;
DELETE FROM `smart_scripts` WHERE `entryorguid` = 771;
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(771, 0, 0, 0, 2, 0, 100, 1, 0, 10, 0, 0, 0, 11, 3488, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Felstrom - Between 0-10% Health - Cast \'Felstrom Resurrection\' (No Repeat)'),
(771, 0, 1, 2, 8, 0, 100, 0, 3488, 0, 0, 0, 0, 94, 128, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Felstrom - On Spellhit \'Felstrom Resurrection\' - Set Dynamic Flags Tapped By Threatlist'),
(771, 0, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 41, 2000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Felstrom - On Spellhit \'Felstrom Resurrection\' - Despawn In 2000 ms');
@@ -0,0 +1,27 @@
-- DB update 2022_05_25_01 -> 2022_05_25_02
--
DROP TABLE IF EXISTS `creaturedisplayinfoextra_dbc`;
CREATE TABLE `creaturedisplayinfoextra_dbc`
( `ID` INT UNSIGNED NOT NULL DEFAULT '0',
`DisplayRaceID` INT UNSIGNED NOT NULL DEFAULT '0',
`DisplaySexID` INT UNSIGNED NOT NULL DEFAULT '0',
`SkinID` INT UNSIGNED NOT NULL DEFAULT '0',
`FaceID` INT UNSIGNED NOT NULL DEFAULT '0',
`HairStyleID` INT UNSIGNED NOT NULL DEFAULT '0',
`HairColorID` INT UNSIGNED NOT NULL DEFAULT '0',
`FacialHairID` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay1` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay2` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay3` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay4` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay5` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay6` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay7` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay8` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay9` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay10` INT UNSIGNED NOT NULL DEFAULT '0',
`NPCItemDisplay11` INT UNSIGNED NOT NULL DEFAULT '0',
`Flags` INT UNSIGNED NOT NULL DEFAULT '0',
`BakeName` VARCHAR(100) NOT NULL,
PRIMARY KEY (`ID`) )
ENGINE=MYISAM CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -0,0 +1,6 @@
-- DB update 2022_05_25_02 -> 2022_05_25_03
-- update `Slaves of the Stormforged` quest Chinese translation
UPDATE `quest_template_locale` SET `ObjectiveText1` = '解救机械侏儒俘虏' WHERE `ID` = 12957 AND `locale` = 'zhCN';
-- update `Waterlogged Recipe` quest Chinese translation
UPDATE `quest_template_locale` SET `CompletedText` = '去达拉然找再来一杯的克莉丝蒂·斯多克顿。' WHERE `ID` = 14203 AND `locale` = 'zhCN';
@@ -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)
@@ -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
};
+2
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");
+1
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;
@@ -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)
@@ -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
@@ -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);
+55
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;
}
+3 -8
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();
+66
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)
+13
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()
+33
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);
+2 -1
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);
@@ -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", " ");
}
@@ -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;
@@ -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)
{
+3
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();
@@ -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>
@@ -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;
}
}
@@ -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
@@ -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];
};
};
@@ -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;
@@ -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,
@@ -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;
}
@@ -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
+35 -4
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
+4 -3
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";