Core/Misc: Implemented spawn tracking

Port From (https://github.com/TrinityCore/TrinityCore/commit/c9099c87567ead635811ef71b990ae7f19103509)
This commit is contained in:
Hondacrx
2025-05-25 20:13:24 -04:00
parent 84642c2ccf
commit 2043ac37f9
17 changed files with 953 additions and 44 deletions
+8
View File
@@ -621,4 +621,12 @@ namespace Framework.Constants
FirstCompletionBonus = 0x1,
RepeatCompletionBonus = 0x2
}
public enum SpawnTrackingState
{
None = 0,
Active = 1,
Complete = 2,
Max
}
}
@@ -92,6 +92,7 @@ namespace Framework.Database
PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES, "SELECT quest, objective, data FROM character_queststatus_objectives WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA, "SELECT questObjectiveId FROM character_queststatus_objectives_criteria WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, "SELECT criteriaId, counter, date FROM character_queststatus_objectives_criteria_progress WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING, "SELECT quest, objective, spawnTrackingId FROM character_queststatus_objectives_spawn_tracking WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_DAILY, "SELECT quest, time FROM character_queststatus_daily WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_WEEKLY, "SELECT quest FROM character_queststatus_weekly WHERE guid = ?");
@@ -566,6 +567,7 @@ namespace Framework.Database
PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA, "DELETE FROM character_queststatus_objectives_criteria WHERE guid = ?");
PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, "DELETE FROM character_queststatus_objectives_criteria_progress WHERE guid = ?");
PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS_BY_CRITERIA, "DELETE FROM character_queststatus_objectives_criteria_progress WHERE guid = ? AND criteriaId = ?");
PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING, "DELETE FROM character_queststatus_objectives_spawn_tracking WHERE guid = ?");
PrepareStatement(CharStatements.DEL_CHAR_SOCIAL_BY_GUID, "DELETE FROM character_social WHERE guid = ?");
PrepareStatement(CharStatements.DEL_CHAR_SOCIAL_BY_FRIEND, "DELETE FROM character_social WHERE friend = ?");
PrepareStatement(CharStatements.DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT, "DELETE FROM character_achievement WHERE achievement = ? AND guid = ?");
@@ -616,6 +618,8 @@ namespace Framework.Database
PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_BY_QUEST, "DELETE FROM character_queststatus_objectives WHERE guid = ? AND quest = ?");
PrepareStatement(CharStatements.INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA, "INSERT INTO character_queststatus_objectives_criteria (guid, questObjectiveId) VALUES (?, ?)");
PrepareStatement(CharStatements.INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS, "INSERT INTO character_queststatus_objectives_criteria_progress (guid, criteriaId, counter, date) VALUES (?, ?, ?, ?)");
PrepareStatement(CharStatements.REP_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING, "REPLACE INTO character_queststatus_objectives_spawn_tracking (guid, quest, objective, spawnTrackingId) VALUES (?, ?, ?, ?)");
PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING_BY_QUEST, "DELETE FROM character_queststatus_objectives_spawn_tracking WHERE guid = ? AND quest = ?");
PrepareStatement(CharStatements.INS_CHAR_QUESTSTATUS_REWARDED, "INSERT IGNORE INTO character_queststatus_rewarded (guid, quest, active) VALUES (?, ?, 1)");
PrepareStatement(CharStatements.DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST, "DELETE FROM character_queststatus_rewarded WHERE guid = ? AND quest = ?");
PrepareStatement(CharStatements.UPD_CHAR_QUESTSTATUS_REWARDED_FACTION_CHANGE, "UPDATE character_queststatus_rewarded SET quest = ? WHERE quest = ? AND guid = ?");
@@ -813,6 +817,7 @@ namespace Framework.Database
SEL_CHARACTER_QUESTSTATUS_OBJECTIVES,
SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA,
SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS,
SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING,
SEL_CHARACTER_QUESTSTATUS_DAILY,
SEL_CHARACTER_QUESTSTATUS_WEEKLY,
SEL_CHARACTER_QUESTSTATUS_MONTHLY,
@@ -1204,6 +1209,7 @@ namespace Framework.Database
DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA,
DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS,
DEL_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS_BY_CRITERIA,
DEL_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING,
DEL_CHAR_SOCIAL_BY_GUID,
DEL_CHAR_SOCIAL_BY_FRIEND,
DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT,
@@ -1254,6 +1260,8 @@ namespace Framework.Database
DEL_CHAR_QUESTSTATUS_OBJECTIVES_BY_QUEST,
INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA,
INS_CHAR_QUESTSTATUS_OBJECTIVES_CRITERIA_PROGRESS,
REP_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING,
DEL_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING_BY_QUEST,
INS_CHAR_QUESTSTATUS_REWARDED,
DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST,
UPD_CHAR_QUESTSTATUS_REWARDED_FACTION_CHANGE,
+19 -1
View File
@@ -338,7 +338,7 @@ namespace Game.Entities
ReplaceAllDynamicFlags(UnitDynFlags.None);
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.StateAnimID), Global.DB2Mgr.GetEmptyAnimStateID());
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.StateWorldEffectsQuestObjectiveID), data != null ? data.spawnTrackingQuestObjectiveId : 0);
SetCanDualWield(cInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.UseOffhandAttack));
@@ -3025,6 +3025,24 @@ namespace Game.Entities
public string GetStringId(StringIdType type) { return m_stringIds[(int)type]; }
public override SpawnTrackingStateData GetSpawnTrackingStateDataForPlayer(Player player)
{
if (player == null)
return null;
SpawnMetadata data = Global.ObjectMgr.GetSpawnMetadata(SpawnObjectType.Creature, GetSpawnId());
if (data != null)
{
if (data.spawnTrackingQuestObjectiveId != 0 && data.spawnTrackingData != null)
{
SpawnTrackingState state = player.GetSpawnTrackingStateByObjective(data.spawnTrackingData.SpawnTrackingId, data.spawnTrackingQuestObjectiveId);
return data.spawnTrackingStates[(int)state];
}
}
return null;
}
public VendorItemData GetVendorItems()
{
return Global.ObjectMgr.GetNpcVendorItemList(GetEntry());
+20 -2
View File
@@ -283,8 +283,6 @@ namespace Game.Entities
SetGoState(goState);
SetGoArtKit(artKit);
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.SpawnTrackingStateAnimID), Global.DB2Mgr.GetEmptyAnimStateID());
switch (goInfo.type)
{
case GameObjectTypes.FishingHole:
@@ -1128,6 +1126,8 @@ namespace Game.Entities
PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.PhaseUseFlags, data.PhaseId, data.PhaseGroup);
PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap);
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.StateWorldEffectsQuestObjectiveID), data != null ? data.spawnTrackingQuestObjectiveId : 0);
if (data.spawntimesecs >= 0)
{
m_spawnedByDefault = true;
@@ -2730,6 +2730,24 @@ namespace Game.Entities
public string GetStringId(StringIdType type) { return m_stringIds[(int)type]; }
public override SpawnTrackingStateData GetSpawnTrackingStateDataForPlayer(Player player)
{
if (player == null)
return null;
SpawnMetadata data = Global.ObjectMgr.GetSpawnMetadata(SpawnObjectType.GameObject, GetSpawnId());
if (data != null)
{
if (data.spawnTrackingQuestObjectiveId != 0 && data.spawnTrackingData != null)
{
SpawnTrackingState state = player.GetSpawnTrackingStateByObjective(data.spawnTrackingData.SpawnTrackingId, data.spawnTrackingQuestObjectiveId);
return data.spawnTrackingStates[(int)state];
}
}
return null;
}
public override string GetName(Locale locale = Locale.enUS)
{
if (locale != Locale.enUS)
@@ -160,6 +160,11 @@ namespace Game.Entities
if (!gameObject.MeetsInteractCondition(receiver))
dynFlags |= GameObjectDynamicLowFlags.NoInterract;
var data = Global.ObjectMgr.GetSpawnMetadata(SpawnObjectType.GameObject, gameObject.GetSpawnId());
if (data != null && data.spawnTrackingQuestObjectiveId != 0 && data.spawnTrackingData != null)
if (receiver.GetSpawnTrackingStateByObjective(data.spawnTrackingData.SpawnTrackingId, data.spawnTrackingQuestObjectiveId) != SpawnTrackingState.Active)
dynFlags &= ~GameObjectDynamicLowFlags.Activate;
}
unitDynFlags = (uint)dynFlags;
@@ -1278,14 +1283,15 @@ namespace Game.Entities
data.WriteUInt32(GetViewerDependentNpcFlags(this, owner, receiver));
data.WriteUInt32(GetViewerDependentNpcFlags2(this, owner, receiver));
data.WriteUInt32(StateSpellVisualID);
data.WriteUInt32(StateAnimID);
data.WriteUInt32(StateAnimKitID);
data.WriteInt32(StateWorldEffectIDs.GetValue().Count);
data.WriteUInt32(GetViewerDependentStateSpellVisualID(this, owner, receiver));
data.WriteUInt32(GetViewerDependentStateAnimID(this, owner, receiver));
data.WriteUInt32(GetViewerDependentStateAnimKitID(this, owner, receiver));
var stateWorldEffects = GetViewerDependentStateWorldEffectIDs(this, owner, receiver);
data.WriteInt32(stateWorldEffects.Count);
data.WriteUInt32(StateWorldEffectsQuestObjectiveID);
data.WriteInt32(SpellOverrideNameID);
for (int i = 0; i < StateWorldEffectIDs.GetValue().Count; ++i)
data.WriteUInt32(StateWorldEffectIDs.GetValue()[i]);
for (int i = 0; i < stateWorldEffects.Count; ++i)
data.WriteUInt32(stateWorldEffects[i]);
data.WritePackedGuid(Charm);
data.WritePackedGuid(Summon);
@@ -1514,11 +1520,11 @@ namespace Game.Entities
}
if (changesMask[2])
{
var list = StateWorldEffectIDs.GetValue();
data.WriteBits(list.Count, 32);
for (int i = 0; i < list.Count; ++i)
var stateWorldEffects = GetViewerDependentStateWorldEffectIDs(this, owner, receiver);
data.WriteBits(stateWorldEffects.Count, 32);
for (int i = 0; i < stateWorldEffects.Count; ++i)
{
data.WriteUInt32(list[i]);
data.WriteUInt32(stateWorldEffects[i]);
}
}
}
@@ -1594,15 +1600,15 @@ namespace Game.Entities
}
if (changesMask[9])
{
data.WriteUInt32(StateSpellVisualID);
data.WriteUInt32(GetViewerDependentStateSpellVisualID(this, owner, receiver));
}
if (changesMask[10])
{
data.WriteUInt32(StateAnimID);
data.WriteUInt32(GetViewerDependentStateAnimID(this, owner, receiver));
}
if (changesMask[11])
{
data.WriteUInt32(StateAnimKitID);
data.WriteUInt32(GetViewerDependentStateAnimKitID(this, owner, receiver));
}
if (changesMask[12])
{
@@ -2477,6 +2483,63 @@ namespace Game.Entities
}
return interactSpellId;
}
List<uint> GetViewerDependentStateWorldEffectIDs(UnitData unitData, Unit unit, Player receiver)
{
List<uint> stateWorldEffects = unitData.StateWorldEffectIDs;
if (unit.IsCreature())
{
var spawnTrackingStateData = unit.GetSpawnTrackingStateDataForPlayer(receiver);
if (spawnTrackingStateData != null)
stateWorldEffects = spawnTrackingStateData.StateWorldEffects;
}
return stateWorldEffects;
}
uint GetViewerDependentStateSpellVisualID(UnitData unitData, Unit unit, Player receiver)
{
uint stateSpellVisual = unitData.StateSpellVisualID;
if (unit.IsCreature())
{
var spawnTrackingStateData = unit.GetSpawnTrackingStateDataForPlayer(receiver);
if (spawnTrackingStateData != null)
stateSpellVisual = spawnTrackingStateData.StateSpellVisualId.GetValueOrDefault(0);
}
return stateSpellVisual;
}
uint GetViewerDependentStateAnimID(UnitData unitData, Unit unit, Player receiver)
{
uint stateAnimId = Global.DB2Mgr.GetEmptyAnimStateID();
if (unit.IsCreature())
{
var spawnTrackingStateData = unit.GetSpawnTrackingStateDataForPlayer(receiver);
if (spawnTrackingStateData != null)
stateAnimId = spawnTrackingStateData.StateAnimId.GetValueOrDefault((ushort)stateAnimId);
}
return stateAnimId;
}
uint GetViewerDependentStateAnimKitID(UnitData unitData, Unit unit, Player receiver)
{
uint stateAnimKitId = unitData.StateAnimKitID;
if (unit.IsCreature())
{
var spawnTrackingStateData = unit.GetSpawnTrackingStateDataForPlayer(receiver);
if (spawnTrackingStateData != null)
stateAnimKitId = spawnTrackingStateData.StateAnimKitId.GetValueOrDefault(0);
}
return stateAnimKitId;
}
}
public class ChrCustomizationChoice : IComparable<ChrCustomizationChoice>
@@ -7505,25 +7568,26 @@ namespace Game.Entities
{
data.WriteUInt32(DisplayID);
data.WriteUInt32(SpellVisualID);
data.WriteUInt32(StateSpellVisualID);
data.WriteUInt32(SpawnTrackingStateAnimID);
data.WriteUInt32(SpawnTrackingStateAnimKitID);
data.WriteInt32(((List<uint>)StateWorldEffectIDs).Count);
data.WriteUInt32(GetViewerDependentStateSpellVisualID(this, owner, receiver));
data.WriteUInt32(GetViewerDependentStateAnimID(this, owner, receiver));
data.WriteUInt32(GetViewerDependentStateAnimKitID(this, owner, receiver));
var stateWorldEffects = GetViewerDependentStateWorldEffectIDs(this, owner, receiver);
data.WriteInt32(stateWorldEffects.Count);
data.WriteUInt32(StateWorldEffectsQuestObjectiveID);
for (int i = 0; i < ((List<uint>)StateWorldEffectIDs).Count; ++i)
for (int i = 0; i < stateWorldEffects.Count; ++i)
{
data.WriteUInt32(((List<uint>)StateWorldEffectIDs)[i]);
data.WriteUInt32(stateWorldEffects[i]);
}
data.WritePackedGuid(CreatedBy);
data.WritePackedGuid(GuildGUID);
data.WriteUInt32(GetViewerGameObjectFlags(this, owner, receiver));
data.WriteUInt32(GetViewerDependentGameObjectFlags(this, owner, receiver));
Quaternion rotation = ParentRotation;
data.WriteFloat(rotation.X);
data.WriteFloat(rotation.Y);
data.WriteFloat(rotation.Z);
data.WriteFloat(rotation.W);
data.WriteUInt32(FactionTemplate);
data.WriteInt8(GetViewerGameObjectState(this, owner, receiver));
data.WriteInt8(GetViewerDependentGameObjectState(this, owner, receiver));
data.WriteInt8(TypeID);
data.WriteUInt8(PercentHealth);
data.WriteUInt32(ArtKit);
@@ -7558,10 +7622,11 @@ namespace Game.Entities
{
if (changesMask[1])
{
data.WriteBits(((List<uint>)StateWorldEffectIDs).Count, 32);
for (int i = 0; i < ((List<uint>)StateWorldEffectIDs).Count; ++i)
var stateWorldEffects = GetViewerDependentStateWorldEffectIDs(this, owner, receiver);
data.WriteBits(stateWorldEffects.Count, 32);
for (int i = 0; i < stateWorldEffects.Count; ++i)
{
data.WriteUInt32(((List<uint>)StateWorldEffectIDs)[i]);
data.WriteUInt32(stateWorldEffects[i]);
}
}
}
@@ -7616,15 +7681,15 @@ namespace Game.Entities
}
if (changesMask[6])
{
data.WriteUInt32(StateSpellVisualID);
data.WriteUInt32(GetViewerDependentStateSpellVisualID(this, owner, receiver));
}
if (changesMask[7])
{
data.WriteUInt32(SpawnTrackingStateAnimID);
data.WriteUInt32(GetViewerDependentStateAnimID(this, owner, receiver));
}
if (changesMask[8])
{
data.WriteUInt32(SpawnTrackingStateAnimKitID);
data.WriteUInt32(GetViewerDependentStateAnimKitID(this, owner, receiver));
}
if (changesMask[9])
{
@@ -7640,7 +7705,7 @@ namespace Game.Entities
}
if (changesMask[12])
{
data.WriteUInt32(GetViewerGameObjectFlags(this, owner, receiver));
data.WriteUInt32(GetViewerDependentGameObjectFlags(this, owner, receiver));
}
if (changesMask[13])
{
@@ -7655,7 +7720,7 @@ namespace Game.Entities
}
if (changesMask[15])
{
data.WriteInt8(GetViewerGameObjectState(this, owner, receiver));
data.WriteInt8(GetViewerDependentGameObjectState(this, owner, receiver));
}
if (changesMask[16])
{
@@ -7725,7 +7790,7 @@ namespace Game.Entities
_changesMask.ResetAll();
}
uint GetViewerGameObjectFlags(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
uint GetViewerDependentGameObjectFlags(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
{
uint flags = gameObjectData.Flags;
if (gameObject.GetGoType() == GameObjectTypes.Chest)
@@ -7735,10 +7800,55 @@ namespace Game.Entities
return flags;
}
sbyte GetViewerGameObjectState(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
sbyte GetViewerDependentGameObjectState(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
{
return (sbyte)gameObject.GetGoStateFor(receiver.GetGUID());
}
List<uint> GetViewerDependentStateWorldEffectIDs(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
{
List<uint> stateWorldEffects = gameObjectData.StateWorldEffectIDs;
var spawnTrackingStateData = gameObject.GetSpawnTrackingStateDataForPlayer(receiver);
if (spawnTrackingStateData != null)
stateWorldEffects = spawnTrackingStateData.StateWorldEffects;
return stateWorldEffects;
}
uint GetViewerDependentStateSpellVisualID(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
{
uint stateSpellVisual = gameObjectData.StateSpellVisualID;
var spawnTrackingStateData = gameObject.GetSpawnTrackingStateDataForPlayer(receiver);
if (spawnTrackingStateData != null)
stateSpellVisual = spawnTrackingStateData.StateSpellVisualId.GetValueOrDefault(0);
return stateSpellVisual;
}
uint GetViewerDependentStateAnimID(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
{
uint stateAnimId = Global.DB2Mgr.GetEmptyAnimStateID();
var spawnTrackingStateData = gameObject.GetSpawnTrackingStateDataForPlayer(receiver);
if (spawnTrackingStateData != null)
stateAnimId = spawnTrackingStateData.StateAnimId.GetValueOrDefault((ushort)stateAnimId);
return stateAnimId;
}
uint GetViewerDependentStateAnimKitID(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
{
uint stateAnimKitId = gameObjectData.SpawnTrackingStateAnimKitID;
var spawnTrackingStateData = gameObject.GetSpawnTrackingStateDataForPlayer(receiver);
if (spawnTrackingStateData != null)
stateAnimKitId = spawnTrackingStateData.StateAnimKitId.GetValueOrDefault(0);
return stateAnimKitId;
}
}
public class DynamicObjectData : HasChangesMask
+11 -1
View File
@@ -867,6 +867,8 @@ namespace Game.Entities
public virtual Loot GetLootForPlayer(Player player) { return null; }
public virtual SpawnTrackingStateData GetSpawnTrackingStateDataForPlayer(Player player) { return null; }
public virtual void BuildValuesCreate(WorldPacket data, UpdateFieldFlag flags, Player target) { }
public virtual void BuildValuesUpdate(WorldPacket data, UpdateFieldFlag flags, Player target) { }
@@ -1272,6 +1274,15 @@ namespace Game.Entities
if (!obj.IsPrivateObject() && !Global.ConditionMgr.IsObjectMeetingVisibilityByObjectIdConditions((uint)obj.GetTypeId(), obj.GetEntry(), this))
return false;
// Spawn tracking
Player player = ToPlayer();
if (player != null)
{
SpawnTrackingStateData spawnTrackingStateData = obj.GetSpawnTrackingStateDataForPlayer(player);
if (spawnTrackingStateData != null && !spawnTrackingStateData.Visible)
return false;
}
bool corpseVisibility = false;
if (distanceCheck)
{
@@ -1304,7 +1315,6 @@ namespace Game.Entities
}
WorldObject viewpoint = this;
Player player = ToPlayer();
if (player != null)
viewpoint = player.GetViewpoint();
+65 -1
View File
@@ -821,9 +821,11 @@ namespace Game.Entities
uint questID = result.Read<uint>(0);
Quest quest = Global.ObjectMgr.GetQuestTemplate(questID);
if (quest == null)
continue;
var questStatusData = m_QuestStatus.LookupByKey(questID);
if (questStatusData != null && questStatusData.Slot < SharedConst.MaxQuestLogSize && quest != null)
if (questStatusData != null && questStatusData.Slot < SharedConst.MaxQuestLogSize)
{
byte storageIndex = result.Read<byte>(1);
@@ -845,6 +847,42 @@ namespace Game.Entities
while (result.NextRow());
}
}
void _LoadQuestStatusObjectiveSpawnTrackings(SQLResult result)
{
if (!result.IsEmpty())
{
do
{
uint questID = result.Read<uint>(0);
Quest quest = Global.ObjectMgr.GetQuestTemplate(questID);
if (quest == null)
continue;
var itr = m_QuestStatus.LookupByKey(questID);
if (itr != null && itr.Slot < SharedConst.MaxQuestLogSize)
{
QuestStatusData questStatusData = itr;
sbyte storageIndex = result.Read<sbyte>(1);
var objectiveItr = quest.Objectives.Find(objective => objective.StorageIndex == storageIndex);
if (objectiveItr != null)
{
uint spawnTrackingId = result.Read<uint>(2);
if (Global.ObjectMgr.IsQuestObjectiveForSpawnTracking(spawnTrackingId, objectiveItr.Id))
questStatusData.SpawnTrackingList.Add((storageIndex, spawnTrackingId));
else
Log.outError(LogFilter.Player, $"Player::_LoadQuestStatusObjectiveSpawnTrackings: Player '{GetName()}' ({GetGUID()}) has objective {objectiveItr.Id} (quest {questID}) with unrelated spawn tracking {spawnTrackingId}.");
}
else
Log.outError(LogFilter.Player, $"Player::_LoadQuestStatusObjectiveSpawnTrackings: Player '{GetName()}' ({GetGUID()}) has quest {questID} out of range objective index {storageIndex}.");
}
else
Log.outError(LogFilter.Player, $"Player::_LoadQuestStatusObjectiveSpawnTrackings: Player {GetName()} ({GetGUID()}) does not have quest {questID} but has objective spawn trackings for it.");
} while (result.NextRow());
}
}
void _LoadQuestStatusRewarded(SQLResult result)
{
if (!result.IsEmpty())
@@ -2189,6 +2227,22 @@ namespace Game.Entities
stmt.AddValue(3, GetQuestSlotObjectiveData(data.Slot, obj));
trans.Append(stmt);
}
// Save spawn trackings
stmt = CharacterDatabase.GetPreparedStatement( CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING_BY_QUEST);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, save.Key);
trans.Append(stmt);
foreach (var (questObjectiveStorageIndex, spawnTrackingId) in data.SpawnTrackingList)
{
stmt = CharacterDatabase.GetPreparedStatement(CharStatements.REP_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, save.Key);
stmt.AddValue(2, questObjectiveStorageIndex);
stmt.AddValue(3, spawnTrackingId);
trans.Append(stmt);
}
}
}
else
@@ -2203,6 +2257,11 @@ namespace Game.Entities
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, save.Key);
trans.Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING_BY_QUEST);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, save.Key);
trans.Append(stmt);
}
}
@@ -3427,6 +3486,7 @@ namespace Game.Entities
// after spell load, learn rewarded spell if need also
_LoadQuestStatus(holder.GetResult(PlayerLoginQueryLoad.QuestStatus));
_LoadQuestStatusObjectives(holder.GetResult(PlayerLoginQueryLoad.QuestStatusObjectives));
_LoadQuestStatusObjectiveSpawnTrackings(holder.GetResult(PlayerLoginQueryLoad.QuestStatusSpawnTracking));
_LoadQuestStatusRewarded(holder.GetResult(PlayerLoginQueryLoad.QuestStatusRew));
_LoadDailyQuestStatus(holder.GetResult(PlayerLoginQueryLoad.DailyQuestStatus));
_LoadWeeklyQuestStatus(holder.GetResult(PlayerLoginQueryLoad.WeeklyQuestStatus));
@@ -4407,6 +4467,10 @@ namespace Game.Entities
stmt.AddValue(0, guid);
trans.Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING);
stmt.AddValue(0, guid);
trans.Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_REWARDED);
stmt.AddValue(0, guid);
trans.Append(stmt);
+133 -2
View File
@@ -753,6 +753,8 @@ namespace Game.Entities
QuestStatusData questStatusData = m_QuestStatus.LookupByKey(questId);
QuestStatus oldStatus = questStatusData.Status;
SendForceSpawnTrackingUpdate(questId);
// check for repeatable quests status reset
SetQuestSlot(logSlot, questId);
questStatusData.Slot = logSlot;
@@ -836,6 +838,8 @@ namespace Game.Entities
{
if (quest_id != 0)
{
SendForceSpawnTrackingUpdate(quest_id);
SetQuestStatus(quest_id, QuestStatus.Complete);
QuestStatusData questStatus = m_QuestStatus.LookupByKey(quest_id);
@@ -2610,6 +2614,32 @@ namespace Game.Entities
bool updatePhaseShift = false;
bool updateZoneAuras = false;
bool anyObjectiveChangedSpawnTrackingState = false;
bool isObjectiveTypeForSpawnTracking = false;
if (objectiveType == QuestObjectiveType.Monster ||
objectiveType == QuestObjectiveType.GameObject ||
objectiveType == QuestObjectiveType.TalkTo ||
objectiveType == QuestObjectiveType.WinPetBattleAgainstNpc)
isObjectiveTypeForSpawnTracking = true;
SpawnMetadata data = null;
if (isObjectiveTypeForSpawnTracking)
{
if (victimGuid.IsGameObject())
{
GameObject gameObject = ObjectAccessor.GetGameObject(this, victimGuid);
if (gameObject != null)
data = Global.ObjectMgr.GetSpawnMetadata(SpawnObjectType.GameObject, gameObject.GetSpawnId());
}
else if (victimGuid.IsCreatureOrVehicle())
{
Creature creature = ObjectAccessor.GetCreatureOrPetOrVehicle(this, victimGuid);
if (creature != null)
data = Global.ObjectMgr.GetSpawnMetadata(SpawnObjectType.Creature, creature.GetSpawnId());
}
}
foreach (var objectiveStatusData in m_questObjectiveStatus.LookupByKey((objectiveType, objectId)))
{
uint questId = objectiveStatusData.QuestStatusPair.QuestID;
@@ -2719,6 +2749,41 @@ namespace Game.Entities
}
}
if (data != null && data.spawnTrackingQuestObjectiveId != 0 && data.spawnTrackingData != null)
{
if (objective.Id == data.spawnTrackingQuestObjectiveId)
{
// Store spawn tracking to return correct state in Player::GetSpawnTrackingStateByObjective
QuestStatusData questStatus = objectiveStatusData.QuestStatusPair.Status;
questStatus.SpawnTrackingList.Add((objective.StorageIndex, data.spawnTrackingData.SpawnTrackingId));
// Send QuestPOIUpdateResponse for every spawn linked to same SpawnTrackingId
foreach (var spawnMetaData in Global.ObjectMgr.GetSpawnMetadataForSpawnTracking(data.spawnTrackingData.SpawnTrackingId))
{
SpawnData spawnData = spawnMetaData.ToSpawnData();
if (spawnData == null)
continue;
QuestPOIUpdateResponse response = new();
SpawnTrackingResponseInfo responseInfo = new();
responseInfo.SpawnTrackingID = data.spawnTrackingData.SpawnTrackingId;
responseInfo.ObjectID = (int)spawnData.Id;
responseInfo.PhaseID = (int)spawnData.PhaseId;
responseInfo.PhaseGroupID = (int)spawnData.PhaseGroup;
responseInfo.PhaseUseFlags = (int)spawnData.PhaseUseFlags;
SpawnTrackingState state = GetSpawnTrackingStateByObjective(data.spawnTrackingData.SpawnTrackingId, objective.Id);
responseInfo.Visible = data.spawnTrackingStates[(int)state].Visible;
response.SpawnTrackingResponses.Add(responseInfo);
SendPacket(response);
}
anyObjectiveChangedSpawnTrackingState = true;
}
}
if (objectiveWasComplete != objectiveIsNowComplete)
anyObjectiveChangedCompletionState = true;
@@ -2752,7 +2817,7 @@ namespace Game.Entities
}
if (anyObjectiveChangedCompletionState)
if (anyObjectiveChangedCompletionState || anyObjectiveChangedSpawnTrackingState)
UpdateVisibleObjectInteractions(true, false, false, true);
if (updatePhaseShift)
@@ -3138,6 +3203,8 @@ namespace Game.Entities
moneyReward = (uint)(GetQuestMoneyReward(quest) + (int)(quest.GetRewMoneyMaxLevel() * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney)));
}
SendForceSpawnTrackingUpdate(questId);
if (quest.HasFlag(QuestFlags.TrackingEvent))
return;
@@ -3348,6 +3415,51 @@ namespace Game.Entities
return false;
}
public void SendForceSpawnTrackingUpdate(uint questId)
{
if (questId != 0)
{
ForceSpawnTrackingUpdate data = new();
data.QuestID = questId;
SendPacket(data);
}
}
public QuestObjective GetActiveQuestObjectiveForForSpawnTracking(uint spawnTrackingId)
{
var questObjectiveList = Global.ObjectMgr.GetSpawnTrackingQuestObjectiveList(spawnTrackingId);
if (questObjectiveList != null)
foreach (QuestObjective questObjective in questObjectiveList)
if (FindQuestSlot(questObjective.QuestID) < SharedConst.MaxQuestLogSize)
return questObjective;
return null;
}
public SpawnTrackingState GetSpawnTrackingStateByObjective(uint spawnTrackingId, uint questObjectiveId)
{
if (spawnTrackingId != 0 && questObjectiveId != 0 && Global.ObjectMgr.IsQuestObjectiveForSpawnTracking(spawnTrackingId, questObjectiveId))
{
QuestObjective questObjective = Global.ObjectMgr.GetQuestObjective(questObjectiveId);
if (questObjective != null)
{
if (IsQuestRewarded(questObjective.QuestID) || IsQuestObjectiveComplete(questObjective.QuestID, questObjective.Id))
return SpawnTrackingState.Complete;
else if (GetQuestStatus(questObjective.QuestID) != QuestStatus.None)
{
var itr = m_QuestStatus.LookupByKey(questObjective.QuestID);
if (itr != null && itr.Slot < SharedConst.MaxQuestLogSize)
if (itr.SpawnTrackingList.Contains((questObjective.StorageIndex, spawnTrackingId)))
return SpawnTrackingState.Complete;
return SpawnTrackingState.Active;
}
}
}
return SpawnTrackingState.None;
}
public bool HasQuestForGO(int GOId)
{
foreach (var objectiveStatusData in m_questObjectiveStatus.LookupByKey((QuestObjectiveType.GameObject, GOId)))
@@ -3389,7 +3501,16 @@ namespace Game.Entities
ObjectFieldData objMask = new();
GameObjectFieldData goMask = new();
if (m_questObjectiveStatus.ContainsKey((QuestObjectiveType.GameObject, (int)gameObject.GetEntry())) || gameObject.GetGoInfo().GetConditionID1() != 0)
SpawnMetadata data = Global.ObjectMgr.GetSpawnMetadata(SpawnObjectType.GameObject, gameObject.GetSpawnId());
if (data != null && data.spawnTrackingQuestObjectiveId != 0 && data.spawnTrackingData != null)
{
objMask.MarkChanged(m_objectData.DynamicFlags);
goMask.MarkChanged(gameObject.m_gameObjectData.StateWorldEffectIDs);
goMask.MarkChanged(gameObject.m_gameObjectData.StateSpellVisualID);
goMask.MarkChanged(gameObject.m_gameObjectData.SpawnTrackingStateAnimID);
goMask.MarkChanged(gameObject.m_gameObjectData.SpawnTrackingStateAnimKitID);
}
else if (m_questObjectiveStatus.ContainsKey((QuestObjectiveType.GameObject, (int)gameObject.GetEntry())) || gameObject.GetGoInfo().GetConditionID1() != 0)
objMask.MarkChanged(gameObject.m_objectData.DynamicFlags);
else
{
@@ -3430,6 +3551,16 @@ namespace Game.Entities
if (creature.m_unitData.NpcFlags2 != 0)
unitMask.MarkChanged(m_unitData.NpcFlags2);
SpawnMetadata data = Global.ObjectMgr.GetSpawnMetadata(SpawnObjectType.Creature, creature.GetSpawnId());
if (data != null && data.spawnTrackingQuestObjectiveId != 0 && data.spawnTrackingData != null)
{
objMask.MarkChanged(objMask.DynamicFlags);
unitMask.MarkChanged(unitMask.StateWorldEffectIDs);
unitMask.MarkChanged(unitMask.StateSpellVisualID);
unitMask.MarkChanged(unitMask.StateAnimID);
unitMask.MarkChanged(unitMask.StateAnimKitID);
}
if (objMask.GetUpdateMask().IsAnySet() || unitMask.GetUpdateMask().IsAnySet())
creature.BuildValuesUpdateForPlayerWithMask(udata, objMask.GetUpdateMask(), unitMask.GetUpdateMask(), this);
-1
View File
@@ -5524,7 +5524,6 @@ namespace Game.Entities
}
SetDisplayId(model.DisplayID, true);
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.StateAnimID), DB2Mgr.GetEmptyAnimStateID());
}
//Creature
-1
View File
@@ -172,7 +172,6 @@ namespace Game.Entities
SetGoState(goinfo.MoTransport.allowstopping == 0 ? GameObjectState.Ready : GameObjectState.Active);
SetGoType(GameObjectTypes.MapObjTransport);
SetGoAnimProgress(255);
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.SpawnTrackingStateAnimID), Global.DB2Mgr.GetEmptyAnimStateID());
SetName(goinfo.name);
SetLocalRotation(0.0f, 0.0f, 0.0f, 1.0f);
SetParentRotation(Quaternion.Identity);
+360 -2
View File
@@ -1514,7 +1514,7 @@ namespace Game
foreach (Criteria criteria in criteriaList)
if (criteria.Entry.Asset != 0)
_eventStorage.Add(criteria.Entry.Asset);
};
}
CriteriaType[] eventCriteriaTypes = { CriteriaType.PlayerTriggerGameEvent, CriteriaType.AnyoneTriggerGameEventScenario };
foreach (CriteriaType criteriaType in eventCriteriaTypes)
@@ -5804,6 +5804,26 @@ namespace Game
void OnDeleteSpawnData(SpawnData data)
{
if (data.spawnTrackingData != null)
{
SpawnTrackingTemplateData spawnTrackingData = GetSpawnTrackingData(data.spawnTrackingData.SpawnTrackingId);
Cypher.Assert(spawnTrackingData != null, $"Creature data for ({data.type},{data.SpawnId}) is being deleted and has invalid spawn tracking id {data.spawnTrackingData.SpawnTrackingId}!");
var pair = _spawnTrackingMapStorage.LookupByKey(data.spawnTrackingData.SpawnTrackingId);
bool erased = false;
foreach (var it in pair.ToList())
{
if (it != data)
continue;
_spawnTrackingMapStorage.Remove(data.spawnTrackingData.SpawnTrackingId, it);
erased = true;
}
if (!erased)
Log.outFatal(LogFilter.Misc, $"Spawn data ({data.type},{data.SpawnId}) being removed is member of spawn tracking {data.spawnTrackingData.SpawnTrackingId}, but not actually listed in the lookup table for that spawn tracking!");
}
var templateIt = _spawnGroupDataStorage.LookupByKey(data.spawnGroupData.groupId);
Cypher.Assert(templateIt != null, $"Creature data for ({data.type},{data.SpawnId}) is being deleted and has invalid spawn group index {data.spawnGroupData.groupId}!");
@@ -9094,6 +9114,341 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} points_of_interest locale strings in {1} ms", _pointOfInterestLocaleStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadSpawnTrackingTemplates()
{
uint oldMSTime = Time.GetMSTime();
// need for reload case
_spawnTrackingDataStorage.Clear();
// 0 1 2 3 4
SQLResult result = DB.World.Query("SELECT SpawnTrackingId, MapId, PhaseId, PhaseGroup, PhaseUseFlags FROM spawn_tracking_template");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, $"Loaded 0 spawn tracking templates. DB table `spawn_tracking_template` is empty!");
return;
}
do
{
uint spawnTrackingId = result.Read<uint>(0);
uint mapId = result.Read<uint>(1);
if (!CliDB.MapStorage.HasRecord(mapId))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_template` references non-existing map {mapId}, skipped");
continue;
}
SpawnTrackingTemplateData data = new();
data.SpawnTrackingId = spawnTrackingId;
data.MapId = mapId;
data.PhaseId = result.Read<uint>(2);
data.PhaseGroup = result.Read<uint>(3);
data.PhaseUseFlags = result.Read<byte>(4);
if (data.PhaseGroup != 0 && data.PhaseId != 0)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_template` has spawn tracking (Id: {data.SpawnTrackingId}) with `PhaseId` and `PhaseGroup` set, `PhaseGroup` set to 0");
data.PhaseGroup = 0;
}
if (data.PhaseId != 0)
{
if (!CliDB.PhaseStorage.HasRecord(data.PhaseId))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_template` has spawn tracking (Id: {data.SpawnTrackingId}) referencing non-existing `PhaseId` {data.PhaseId}, set to 0");
data.PhaseId = 0;
}
}
if (data.PhaseGroup != 0)
{
if (Global.DB2Mgr.GetPhasesForGroup(data.PhaseGroup) == null)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_template` has spawn tracking (Id: {data.SpawnTrackingId}) referencing non-existing `PhaseGroup` {data.PhaseGroup}, set to 0");
data.PhaseGroup = 0;
}
}
if ((data.PhaseUseFlags & ~(byte)PhaseUseFlagsValues.All) != 0)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_template` has spawn tracking (Id: {data.SpawnTrackingId}) referencing unknown `PhaseUseFlags`, removed unknown value.");
data.PhaseUseFlags &= (byte)PhaseUseFlagsValues.All;
}
if ((data.PhaseUseFlags & (byte)PhaseUseFlagsValues.AlwaysVisible) != 0 && (data.PhaseUseFlags & (byte)PhaseUseFlagsValues.Inverse) != 0)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_template` has spawn tracking (Id: {data.SpawnTrackingId}) with `PhaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE, removing PHASE_USE_FLAGS_INVERSE.");
data.PhaseUseFlags &= (byte)~PhaseUseFlagsValues.Inverse;
}
_spawnTrackingDataStorage[spawnTrackingId] = data;
} while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, $"Loaded {_spawnTrackingDataStorage.Count} spawn tracking templates in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
public SpawnTrackingTemplateData GetSpawnTrackingData(uint spawnTrackingId)
{
return _spawnTrackingDataStorage.LookupByKey(spawnTrackingId);
}
public List<SpawnMetadata> GetSpawnMetadataForSpawnTracking(uint spawnTrackingId) { return _spawnTrackingMapStorage.LookupByKey(spawnTrackingId); }
public List<QuestObjective> GetSpawnTrackingQuestObjectiveList(uint spawnTrackingId) { return _spawnTrackingQuestObjectiveStorage.LookupByKey(spawnTrackingId); }
public bool IsQuestObjectiveForSpawnTracking(uint spawnTrackingId, uint questObjectiveId)
{
var questObjectiveList = _spawnTrackingQuestObjectiveStorage.LookupByKey(spawnTrackingId);
if (questObjectiveList != null)
if (questObjectiveList.Find(p => p.Id == questObjectiveId) != null)
return true;
return false;
}
public void LoadSpawnTrackingQuestObjectives()
{
uint oldMSTime = Time.GetMSTime();
// need for reload case
_spawnTrackingQuestObjectiveStorage.Clear();
// 0 1
SQLResult result = DB.World.Query("SELECT SpawnTrackingId, QuestObjectiveId FROM spawn_tracking_quest_objective");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, $"Loaded 0 spawn tracking quest objectives. DB table `spawn_tracking_quest_objective` is empty!");
return;
}
uint count = 0;
do
{
uint spawnTrackingId = result.Read<uint>(0);
uint objectiveId = result.Read<uint>(1);
if (GetSpawnTrackingData(spawnTrackingId) == null)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_quest_objective` has quest objective {objectiveId} assigned to spawn tracking {spawnTrackingId}, but spawn tracking does not exist!");
continue;
}
QuestObjective questObjective = GetQuestObjective(objectiveId);
if (questObjective == null)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_quest_objective` has quest objective {objectiveId} assigned to spawn tracking {spawnTrackingId}, but quest objective does not exist!");
continue;
}
_spawnTrackingQuestObjectiveStorage.Add(spawnTrackingId, questObjective);
++count;
} while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} spawn tracking quest objectives in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
public void LoadSpawnTrackings()
{
uint oldMSTime = Time.GetMSTime();
// need for reload case
_spawnTrackingMapStorage.Clear();
// 0 1 2 3
SQLResult result = DB.World.Query("SELECT SpawnTrackingId, SpawnType, SpawnId, QuestObjectiveId FROM spawn_tracking");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, $"Loaded 0 spawn tracking members. DB table `spawn_tracking` is empty!");
return;
}
uint count = 0;
do
{
uint spawnTrackingId = result.Read<uint>(0);
SpawnObjectType spawnType = (SpawnObjectType)result.Read<byte>(1);
ulong spawnId = result.Read<ulong>(2);
uint objectiveId = result.Read<uint>(3);
if (!SpawnData.TypeIsValid(spawnType))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking` has spawn data with invalid type {spawnType} listed for spawn tracking {spawnTrackingId}. Skipped.");
continue;
}
else if (spawnType == SpawnObjectType.AreaTrigger)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking` has areatrigger spawn ({spawnType}) listed for spawn tracking {spawnTrackingId}. Skipped.");
continue;
}
SpawnMetadata data = GetSpawnMetadata(spawnType, spawnId);
if (data == null)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking` has spawn ({spawnType},{spawnId}) not found, but is listed as a member of spawn tracking {spawnTrackingId}!");
continue;
}
else if (data.spawnTrackingData != null)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking` has spawn ({spawnType},{spawnId}) is listed as a member of spawn tracking {spawnTrackingId}, but is already a member of spawn tracking {data.spawnTrackingData.SpawnTrackingId}. Skipped.");
continue;
}
SpawnTrackingTemplateData spawnTrackingTemplateData = GetSpawnTrackingData(spawnTrackingId);
if (spawnTrackingTemplateData == null)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking` has spawn tracking {spawnTrackingId} assigned to spawn ({spawnType},{spawnId}), but spawn tracking does not exist!");
continue;
}
if (!IsQuestObjectiveForSpawnTracking(spawnTrackingId, objectiveId))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking` has spawn tracking {spawnTrackingId} assigned to spawn ({spawnType},{spawnId}), but spawn tracking is not linked to quest objective {objectiveId}. Skipped.");
continue;
}
if (spawnTrackingTemplateData.MapId != data.MapId)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking` has spawn tracking {spawnTrackingId} (map {spawnTrackingTemplateData.MapId}) assigned to spawn ({spawnType},{spawnId}), but spawn has map {data.MapId} - spawn NOT added to spawn tracking!");
continue;
}
SpawnData spawnData = data.ToSpawnData();
if (spawnTrackingTemplateData.PhaseId != spawnData.PhaseId || spawnTrackingTemplateData.PhaseGroup != spawnData.PhaseGroup || spawnTrackingTemplateData.PhaseUseFlags != (byte)spawnData.PhaseUseFlags)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking` has spawn tracking {spawnTrackingId} with phase info (PhaseId: {spawnTrackingTemplateData.PhaseId}, PhaseGroup: {spawnTrackingTemplateData.PhaseGroup}, PhaseUseFlags: {spawnTrackingTemplateData.PhaseUseFlags}), ",
$"but spawn ({spawnType},{spawnId}) has different phase info (PhaseId: {spawnData.PhaseId}, PhaseGroup: {spawnData.PhaseGroup}, PhaseUseFlags: {spawnData.PhaseUseFlags}) - spawn NOT added to spawn tracking!");
continue;
}
data.spawnTrackingData = spawnTrackingTemplateData;
data.spawnTrackingQuestObjectiveId = objectiveId;
_spawnTrackingMapStorage.Add(spawnTrackingId, data);
++count;
} while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} spawn tracking members in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
public void LoadSpawnTrackingStates()
{
uint oldMSTime = Time.GetMSTime();
// 0 1 2 3 4 5 6 7
SQLResult result = DB.World.Query("SELECT SpawnType, SpawnId, State, Visible, StateSpellVisualId, StateAnimId, StateAnimKitId, StateWorldEffects FROM spawn_tracking_state");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, $"Loaded 0 spawn tracking states. DB table `spawn_tracking_state` is empty!");
return;
}
uint count = 0;
do
{
SpawnObjectType spawnType = (SpawnObjectType)result.Read<byte>(0);
ulong spawnId = result.Read<ulong>(1);
SpawnTrackingState state = (SpawnTrackingState)result.Read<byte>(2);
if (!SpawnData.TypeIsValid(spawnType))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` has spawn data with invalid type {spawnType}. Skipped.");
continue;
}
else if (spawnType == SpawnObjectType.AreaTrigger)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` has areatrigger spawn ({spawnType}). Skipped.");
continue;
}
SpawnMetadata data = GetSpawnMetadata(spawnType, spawnId);
if (data == null)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` has spawn ({spawnType},{spawnId}) not found!");
continue;
}
else if (data.spawnTrackingData == null)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` has spawn ({spawnType},{spawnId}) with spawn tracking states, but is not part of a spawn tracking. Skipped.");
continue;
}
if (state >= SpawnTrackingState.Max)
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` has spawn ({spawnType},{spawnId}) with invalid state type {state}. Skipped.");
continue;
}
SpawnTrackingStateData spawnTrackingStateData = data.spawnTrackingStates[(int)state];
spawnTrackingStateData.Visible = result.Read<bool>(3);
if (!result.IsNull(4))
{
spawnTrackingStateData.StateSpellVisualId = result.Read<uint>(4);
if (!CliDB.SpellVisualStorage.HasRecord(spawnTrackingStateData.StateSpellVisualId.Value))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` references invalid StateSpellVisualId {spawnTrackingStateData.StateSpellVisualId} for spawn ({spawnType},{spawnId}), set to none.");
spawnTrackingStateData.StateSpellVisualId = null;
}
}
if (!result.IsNull(5))
{
spawnTrackingStateData.StateAnimId = result.Read<ushort>(5);
if (spawnTrackingStateData.StateAnimId != Global.DB2Mgr.GetEmptyAnimStateID() && !CliDB.AnimationDataStorage.HasRecord(spawnTrackingStateData.StateAnimId.Value))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` references invalid StateAnimId {spawnTrackingStateData.StateAnimId} for spawn ({spawnType},{spawnId}), set to none.");
spawnTrackingStateData.StateAnimId = null;
}
}
if (!result.IsNull(6))
{
spawnTrackingStateData.StateAnimKitId = result.Read<ushort>(6);
if (!CliDB.AnimKitStorage.HasRecord(spawnTrackingStateData.StateAnimKitId.Value))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` references invalid StateAnimKitId {spawnTrackingStateData.StateAnimKitId} for spawn ({spawnType},{spawnId}), set to none.");
spawnTrackingStateData.StateAnimKitId = null;
}
}
if (!result.IsNull(7))
{
List<uint> worldEffectList = new();
foreach (var worldEffectsStr in result.Read<string>(7).Split(','))
{
if (!uint.TryParse(worldEffectsStr, out uint worldEffectId))
continue;
if (!CliDB.WorldEffectStorage.HasRecord(worldEffectId))
{
Log.outError(LogFilter.Sql, $"Table `spawn_tracking_state` references invalid StateAnimKitId {worldEffectId} for spawn ({spawnType},{spawnId}). Skipped.");
continue;
}
worldEffectList.Add(worldEffectId);
}
if (!worldEffectList.Empty())
spawnTrackingStateData.StateWorldEffects = worldEffectList;
}
++count;
} while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} spawn tracking states in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
public CreatureLocale GetCreatureLocale(uint entry)
{
return _creatureLocaleStorage.LookupByKey(entry);
@@ -10237,7 +10592,7 @@ namespace Game
SQLResult result = DB.World.Query("SELECT CreatureId, CurrencyId FROM creature_quest_currency ORDER BY CreatureId, CurrencyId ASC");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, ">> Loaded 0 creature quest currencies. DB table `creature_quest_currency` is empty.");
Log.outInfo(LogFilter.ServerLoading, $"Loaded 0 creature quest currencies. DB table `creature_quest_currency` is empty.");
return;
}
@@ -11242,6 +11597,9 @@ namespace Game
Dictionary<uint, QuestObjective> _questObjectives = new();
Dictionary<uint, QuestGreeting>[] _questGreetingStorage = new Dictionary<uint, QuestGreeting>[2];
Dictionary<uint, QuestGreetingLocale>[] _questGreetingLocaleStorage = new Dictionary<uint, QuestGreetingLocale>[2];
Dictionary<uint, SpawnTrackingTemplateData> _spawnTrackingDataStorage = new();
MultiMap<uint, SpawnMetadata> _spawnTrackingMapStorage = new();
MultiMap<uint, QuestObjective> _spawnTrackingQuestObjectiveStorage = new();
//Scripts
ScriptNameContainer _scriptNamesStorage = new();
+5
View File
@@ -2732,6 +2732,10 @@ namespace Game
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.QuestStatusObjectivesCriteriaProgress, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_OBJECTIVES_SPAWN_TRACKING);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.QuestStatusSpawnTracking, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CharStatements.SEL_CHARACTER_QUESTSTATUS_DAILY);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.DailyQuestStatus, stmt);
@@ -2992,6 +2996,7 @@ namespace Game
QuestStatusObjectives,
QuestStatusObjectivesCriteria,
QuestStatusObjectivesCriteriaProgress,
QuestStatusSpawnTracking,
DailyQuestStatus,
Reputation,
Inventory,
+65
View File
@@ -9,6 +9,7 @@ using Game.Networking;
using Game.Networking.Packets;
using Game.DataStorage;
using System.Collections.Generic;
using System;
namespace Game
{
@@ -416,6 +417,7 @@ namespace Game
}
}
GetPlayer().SendForceSpawnTrackingUpdate(questId);
GetPlayer().SetQuestSlot(packet.Entry, 0);
GetPlayer().TakeQuestSourceItem(questId, true); // remove quest src item from player
GetPlayer().AbandonQuest(questId); // remove all quest items player received before abandoning quest. Note, this does not remove normal drop items that happen to be quest requirements.
@@ -839,5 +841,68 @@ namespace Game
SendPacket(response);
}
[WorldPacketHandler(ClientOpcodes.SpawnTrackingUpdate)]
void HandleSpawnTrackingUpdate(SpawnTrackingUpdate spawnTrackingUpdate)
{
QuestPOIUpdateResponse response = new();
bool hasObjectTypeRequested(TypeMask objectTypeMask, SpawnObjectType objectType)
{
if (objectTypeMask.HasAnyFlag(TypeMask.Unit))
return objectType == SpawnObjectType.Creature;
else if (objectTypeMask.HasAnyFlag(TypeMask.GameObject))
return objectType == SpawnObjectType.GameObject;
return false;
}
foreach (var requestInfo in spawnTrackingUpdate.SpawnTrackingRequests)
{
SpawnTrackingResponseInfo responseInfo = new();
responseInfo.SpawnTrackingID = requestInfo.SpawnTrackingID;
responseInfo.ObjectID = requestInfo.ObjectID;
var spawnTrackingTemplateData = Global.ObjectMgr.GetSpawnTrackingData(requestInfo.SpawnTrackingID);
QuestObjective activeQuestObjective = _player.GetActiveQuestObjectiveForForSpawnTracking(requestInfo.SpawnTrackingID);
// Send phase info if map is the same or spawn tracking related quests are taken or completed
if (spawnTrackingTemplateData != null && (_player.GetMapId() == spawnTrackingTemplateData.MapId || activeQuestObjective != null))
{
responseInfo.PhaseID = (int)spawnTrackingTemplateData.PhaseId;
responseInfo.PhaseGroupID = (int)spawnTrackingTemplateData.PhaseGroup;
responseInfo.PhaseUseFlags = spawnTrackingTemplateData.PhaseUseFlags;
// Send spawn visibility data if available
if (requestInfo.ObjectTypeMask != 0 && (requestInfo.ObjectTypeMask & (int)(TypeMask.Unit | TypeMask.GameObject)) != 0)
{
// There should only be one entity
foreach (var data in Global.ObjectMgr.GetSpawnMetadataForSpawnTracking(requestInfo.SpawnTrackingID))
{
var spawnData = data.ToSpawnData();
if (spawnData == null)
continue;
if (spawnData.Id != requestInfo.ObjectID)
continue;
if (!hasObjectTypeRequested((TypeMask)requestInfo.ObjectTypeMask, data.type))
continue;
if (activeQuestObjective != null)
{
SpawnTrackingState state = _player.GetSpawnTrackingStateByObjective(requestInfo.SpawnTrackingID, activeQuestObjective.Id);
responseInfo.Visible = data.spawnTrackingStates[(int)state].Visible;
break;
}
}
}
}
response.SpawnTrackingResponses.Add(responseInfo);
}
SendPacket(response);
}
}
}
+22 -1
View File
@@ -58,7 +58,10 @@ namespace Game.Maps
public ulong SpawnId;
public uint MapId = 0xFFFFFFFF;
public bool dbData = true;
public SpawnGroupTemplateData spawnGroupData = null;
public SpawnGroupTemplateData spawnGroupData;
public SpawnTrackingTemplateData spawnTrackingData;
public uint spawnTrackingQuestObjectiveId;
public SpawnTrackingStateData[] spawnTrackingStates = new SpawnTrackingStateData[(int)SpawnTrackingState.Max];
public static bool TypeInMask(SpawnObjectType type, SpawnObjectTypeMask mask) { return ((1 << (int)type) & (int)mask) != 0; }
public static bool TypeHasData(SpawnObjectType type) { return type < SpawnObjectType.NumSpawnTypesWithData; }
@@ -71,4 +74,22 @@ namespace Game.Maps
public SpawnData ToSpawnData() { return TypeHasData(type) ? (SpawnData)this : null; }
}
public class SpawnTrackingTemplateData
{
public uint SpawnTrackingId;
public uint MapId;
public uint PhaseId;
public uint PhaseGroup;
public byte PhaseUseFlags;
}
public class SpawnTrackingStateData
{
public bool Visible = true;
public uint? StateSpellVisualId;
public ushort? StateAnimId;
public ushort? StateAnimKitId;
public List<uint> StateWorldEffects = new();
}
}
@@ -994,6 +994,52 @@ namespace Game.Networking.Packets
}
}
class SpawnTrackingUpdate : ClientPacket
{
public List<SpawnTrackingRequestInfo> SpawnTrackingRequests = new();
public SpawnTrackingUpdate(WorldPacket packet) : base(packet) { }
public override void Read()
{
var spawnTrackingRequestCount = _worldPacket.ReadUInt32();
for (var i = 0; i < spawnTrackingRequestCount; i++)
{
var spawnTrackingRequestInfo = new SpawnTrackingRequestInfo();
spawnTrackingRequestInfo.Read(_worldPacket);
SpawnTrackingRequests.Add(spawnTrackingRequestInfo);
}
}
}
class QuestPOIUpdateResponse : ServerPacket
{
public List<SpawnTrackingResponseInfo> SpawnTrackingResponses = new();
public QuestPOIUpdateResponse() : base(ServerOpcodes.QuestPoiUpdateResponse, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WriteInt32(SpawnTrackingResponses.Count);
foreach (SpawnTrackingResponseInfo spawnTrackingResponseInfo in SpawnTrackingResponses)
spawnTrackingResponseInfo.Write(_worldPacket);
}
}
class ForceSpawnTrackingUpdate : ServerPacket
{
public uint QuestID;
public ForceSpawnTrackingUpdate() : base(ServerOpcodes.ForceSpawnTrackingUpdate, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WriteUInt32(QuestID);
}
}
//Structs
public class QuestGiverInfo
{
@@ -1533,4 +1579,40 @@ namespace Game.Networking.Packets
MawPower.Value.Write(data);
}
}
struct SpawnTrackingRequestInfo
{
public int ObjectTypeMask;
public int ObjectID;
public uint SpawnTrackingID;
public void Read(WorldPacket data)
{
ObjectTypeMask = data.ReadInt32();
ObjectID = data.ReadInt32();
SpawnTrackingID = data.ReadUInt32();
}
}
class SpawnTrackingResponseInfo
{
public uint SpawnTrackingID;
public int ObjectID;
public int PhaseID;
public int PhaseGroupID;
public int PhaseUseFlags;
public bool Visible = true;
public void Write(WorldPacket data)
{
data.WriteUInt32(SpawnTrackingID);
data.WriteInt32(ObjectID);
data.WriteInt32(PhaseID);
data.WriteInt32(PhaseGroupID);
data.WriteInt32(PhaseUseFlags);
data.WriteBit(Visible);
data.FlushBits();
}
}
}
+1
View File
@@ -995,6 +995,7 @@ namespace Game
public long AcceptTime;
public uint Timer;
public bool Explored;
public List<(sbyte, uint)> SpawnTrackingList = new();
}
public class QuestGreeting
+12
View File
@@ -803,6 +803,18 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading Player Choices...");
Global.ObjectMgr.LoadPlayerChoices();
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Tracking Templates...");
Global.ObjectMgr.LoadSpawnTrackingTemplates();
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Tracking Quest Objectives...");
Global.ObjectMgr.LoadSpawnTrackingQuestObjectives();
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Tracking Spawns...");
Global.ObjectMgr.LoadSpawnTrackings();
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Tracking Spawn States...");
Global.ObjectMgr.LoadSpawnTrackingStates();
if (WorldConfig.GetBoolValue(WorldCfg.LoadLocales))
{
Log.outInfo(LogFilter.ServerLoading, "Loading Player Choices Locales...");