Core/Misc: Implemented spawn tracking
Port From (https://github.com/TrinityCore/TrinityCore/commit/c9099c87567ead635811ef71b990ae7f19103509)
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -5524,7 +5524,6 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
SetDisplayId(model.DisplayID, true);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.StateAnimID), DB2Mgr.GetEmptyAnimStateID());
|
||||
}
|
||||
|
||||
//Creature
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -995,6 +995,7 @@ namespace Game
|
||||
public long AcceptTime;
|
||||
public uint Timer;
|
||||
public bool Explored;
|
||||
public List<(sbyte, uint)> SpawnTrackingList = new();
|
||||
}
|
||||
|
||||
public class QuestGreeting
|
||||
|
||||
@@ -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...");
|
||||
|
||||
Reference in New Issue
Block a user