Core/Conditions: Refactor ConditionMgr internals to get rid of separate containers for some condition source types

Port From (https://github.com/TrinityCore/TrinityCore/commit/0b5406dd882c6d96bc1be6fd0a78375c3b316415)
This commit is contained in:
hondacrx
2024-02-06 09:39:26 -05:00
parent ebcad7dbc5
commit 80936edeab
12 changed files with 292 additions and 383 deletions
+49 -1
View File
@@ -619,8 +619,10 @@ namespace Game.Conditions
{
StringBuilder ss = new();
ss.AppendFormat("[Condition SourceType: {0}", SourceType);
if (SourceType < ConditionSourceType.Max)
if (SourceType < ConditionSourceType.MaxDbAllowed)
ss.AppendFormat(" ({0})", Global.ConditionMgr.StaticSourceTypeData[(int)SourceType]);
else if (SourceType == ConditionSourceType.ReferenceCondition)
ss.Append(" (Reference)");
else
ss.Append(" (Unknown)");
if (Global.ConditionMgr.CanHaveSourceGroupSet(SourceType))
@@ -659,6 +661,52 @@ namespace Game.Conditions
public bool NegativeCondition;
}
public struct ConditionId
{
public uint SourceGroup;
public int SourceEntry;
public uint SourceId;
public ConditionId(uint sourceGroup, int sourceEntry, uint sourceId)
{
SourceGroup = sourceGroup;
SourceEntry = sourceEntry;
SourceId = sourceId;
}
public override int GetHashCode()
{
return SourceGroup.GetHashCode() ^ SourceEntry.GetHashCode() ^ SourceId.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is ConditionId)
return (ConditionId)obj == this;
return false;
}
public static bool operator ==(ConditionId left, ConditionId right)
{
if (left.SourceGroup != right.SourceGroup)
return false;
if (left.SourceEntry != right.SourceEntry)
return false;
if (left.SourceId != right.SourceId)
return false;
return true;
}
public static bool operator !=(ConditionId left, ConditionId right)
{
return !(left == right);
}
}
public class ConditionSourceInfo
{
public ConditionSourceInfo(WorldObject target0, WorldObject target1 = null, WorldObject target2 = null)
+204 -306
View File
@@ -41,7 +41,7 @@ namespace Game
if (i.ReferenceId != 0) // handle reference
{
var refe = conditionReferenceStorage.LookupByKey(i.ReferenceId);
var refe = ConditionStorage[ConditionSourceType.ReferenceCondition].LookupByKey(new ConditionId(i.ReferenceId, 0, 0));
Cypher.Assert(refe.Empty(), "ConditionMgr.GetSearcherTypeMaskForConditionList - incorrect reference");
elseGroupSearcherTypeMasks[i.ElseGroup] &= GetSearcherTypeMaskForConditionList(refe);
}
@@ -71,15 +71,13 @@ namespace Game
if (condition.IsLoaded())
{
//! Find ElseGroup in ElseGroupStore
//! If not found, add an entry in the store and set to true (placeholder)
if (!elseGroupStore.ContainsKey(condition.ElseGroup))
elseGroupStore[condition.ElseGroup] = true;
else if (!elseGroupStore[condition.ElseGroup]) //! If another condition in this group was unmatched before this, don't bother checking (the group is false anyway)
var added = elseGroupStore.TryAdd(condition.ElseGroup, true);
if (!added) //! If another condition in this group was unmatched before this, don't bother checking (the group is false anyway)
continue;
if (condition.ReferenceId != 0)//handle reference
{
var refe = conditionReferenceStorage.LookupByKey(condition.ReferenceId);
var refe = ConditionStorage[ConditionSourceType.ReferenceCondition].LookupByKey(new ConditionId(condition.ReferenceId, 0, 0));
if (!refe.Empty())
{
if (!IsObjectMeetToConditionList(sourceInfo, refe))
@@ -152,7 +150,8 @@ namespace Game
sourceType == ConditionSourceType.Graveyard ||
sourceType == ConditionSourceType.AreaTrigger ||
sourceType == ConditionSourceType.TrainerSpell ||
sourceType == ConditionSourceType.ObjectIdVisibility;
sourceType == ConditionSourceType.ObjectIdVisibility ||
sourceType == ConditionSourceType.ReferenceCondition;
}
public bool CanHaveSourceIdSet(ConditionSourceType sourceType)
@@ -189,7 +188,7 @@ namespace Game
{
if (sourceType > ConditionSourceType.None && sourceType < ConditionSourceType.Max)
{
var conditions = conditionStorage[sourceType].LookupByKey(entry);
var conditions = ConditionStorage[sourceType].LookupByKey(new ConditionId(0, (int)entry, 0));
if (!conditions.Empty())
{
Log.outDebug(LogFilter.Condition, "GetConditionsForNotGroupedEntry: found conditions for type {0} and entry {1}", sourceType, entry);
@@ -211,92 +210,75 @@ namespace Game
ConditionSourceInfo conditionSource = new(map);
return IsObjectMeetingNotGroupedConditions(sourceType, entry, conditionSource);
}
public bool HasConditionsForNotGroupedEntry(ConditionSourceType sourceType, uint entry)
{
if (sourceType > ConditionSourceType.None && sourceType < ConditionSourceType.Max)
if (conditionStorage[sourceType].ContainsKey(entry))
return true;
return ConditionStorage[sourceType].ContainsKey(new ConditionId(0, (int)entry, 0));
return false;
}
public bool IsObjectMeetingSpellClickConditions(uint creatureId, uint spellId, WorldObject clicker, WorldObject target)
{
var multiMap = spellClickEventConditionStorage.LookupByKey(creatureId);
if (multiMap != null)
var conditions = ConditionStorage[ConditionSourceType.SpellClickEvent].LookupByKey(new ConditionId(creatureId, (int)spellId, 0));
if (!conditions.Empty())
{
var conditions = multiMap.LookupByKey(spellId);
if (!conditions.Empty())
{
Log.outDebug(LogFilter.Condition, "GetConditionsForSpellClickEvent: found conditions for SpellClickEvent entry {0} spell {1}", creatureId, spellId);
ConditionSourceInfo sourceInfo = new(clicker, target);
return IsObjectMeetToConditions(sourceInfo, conditions);
}
Log.outDebug(LogFilter.Condition, "GetConditionsForSpellClickEvent: found conditions for SpellClickEvent entry {0} spell {1}", creatureId, spellId);
ConditionSourceInfo sourceInfo = new(clicker, target);
return IsObjectMeetToConditions(sourceInfo, conditions);
}
return true;
}
public List<Condition> GetConditionsForSpellClickEvent(uint creatureId, uint spellId)
public bool HasConditionsForSpellClickEvent(uint creatureId, uint spellId)
{
var multiMap = spellClickEventConditionStorage.LookupByKey(creatureId);
if (multiMap != null)
if (ConditionStorage[ConditionSourceType.SpellClickEvent].ContainsKey(new ConditionId(creatureId, (int)spellId, 0)))
{
var conditions = multiMap.LookupByKey(spellId);
if (!conditions.Empty())
{
Log.outDebug(LogFilter.Condition, "GetConditionsForSpellClickEvent: found conditions for SpellClickEvent entry {0} spell {1}", creatureId, spellId);
return conditions;
}
Log.outDebug(LogFilter.Condition, "GetConditionsForSpellClickEvent: found conditions for SpellClickEvent entry {0} spell {1}", creatureId, spellId);
return true;
}
return null;
return false;
}
public bool IsObjectMeetingVehicleSpellConditions(uint creatureId, uint spellId, Player player, Unit vehicle)
{
var multiMap = vehicleSpellConditionStorage.LookupByKey(creatureId);
if (multiMap != null)
var conditions = ConditionStorage[ConditionSourceType.VehicleSpell].LookupByKey(new ConditionId(creatureId, (int)spellId, 0));
if (conditions != null)
{
var conditions = multiMap.LookupByKey(spellId);
if (!conditions.Empty())
{
Log.outDebug(LogFilter.Condition, "GetConditionsForVehicleSpell: found conditions for Vehicle entry {0} spell {1}", creatureId, spellId);
ConditionSourceInfo sourceInfo = new(player, vehicle);
return IsObjectMeetToConditions(sourceInfo, conditions);
}
Log.outDebug(LogFilter.Condition, "GetConditionsForVehicleSpell: found conditions for Vehicle entry {0} spell {1}", creatureId, spellId);
ConditionSourceInfo sourceInfo = new(player, vehicle);
return IsObjectMeetToConditions(sourceInfo, conditions);
}
return true;
}
public bool IsObjectMeetingSmartEventConditions(long entryOrGuid, uint eventId, SmartScriptType sourceType, Unit unit, WorldObject baseObject)
{
var multiMap = smartEventConditionStorage.LookupByKey(Tuple.Create((int)entryOrGuid, (uint)sourceType));
if (multiMap != null)
var conditions = ConditionStorage[ConditionSourceType.SmartEvent].LookupByKey(new ConditionId(eventId + 1, (int)entryOrGuid, (uint)sourceType));
if (!conditions.Empty())
{
var conditions = multiMap.LookupByKey(eventId + 1);
if (!conditions.Empty())
{
Log.outDebug(LogFilter.Condition, "GetConditionsForSmartEvent: found conditions for Smart Event entry or guid {0} eventId {1}", entryOrGuid, eventId);
ConditionSourceInfo sourceInfo = new(unit, baseObject);
return IsObjectMeetToConditions(sourceInfo, conditions);
}
Log.outDebug(LogFilter.Condition, "GetConditionsForSmartEvent: found conditions for Smart Event entry or guid {0} eventId {1}", entryOrGuid, eventId);
ConditionSourceInfo sourceInfo = new(unit, baseObject);
return IsObjectMeetToConditions(sourceInfo, conditions);
}
return true;
}
public bool IsObjectMeetingVendorItemConditions(uint creatureId, uint itemId, Player player, Creature vendor)
{
var multiMap = npcVendorConditionContainerStorage.LookupByKey(creatureId);
if (multiMap != null)
var conditions = ConditionStorage[ConditionSourceType.NpcVendor].LookupByKey(new ConditionId(creatureId, (int)itemId, 0));
if (!conditions.Empty())
{
var conditions = multiMap.LookupByKey(itemId);
if (!conditions.Empty())
{
Log.outDebug(LogFilter.Condition, "GetConditionsForNpcVendor: found conditions for creature entry {0} item {1}", creatureId, itemId);
ConditionSourceInfo sourceInfo = new(player, vendor);
return IsObjectMeetToConditions(sourceInfo, conditions);
}
Log.outDebug(LogFilter.Condition, "GetConditionsForNpcVendor: found conditions for creature entry {0} item {1}", creatureId, itemId);
ConditionSourceInfo sourceInfo = new(player, vendor);
return IsObjectMeetToConditions(sourceInfo, conditions);
}
return true;
}
@@ -304,38 +286,35 @@ namespace Game
{
return spellsUsedInSpellClickConditions.Contains(spellId);
}
public List<Condition> GetConditionsForAreaTrigger(uint areaTriggerId, bool isServerSide)
{
return areaTriggerConditionContainerStorage.LookupByKey(Tuple.Create(areaTriggerId, isServerSide));
return ConditionStorage[ConditionSourceType.AreaTrigger].LookupByKey(new ConditionId(areaTriggerId, isServerSide ? 1 : 0, 0));
}
public bool IsObjectMeetingTrainerSpellConditions(uint trainerId, uint spellId, Player player)
{
var multiMap = trainerSpellConditionContainerStorage.LookupByKey(trainerId);
if (multiMap != null)
var conditions = ConditionStorage[ConditionSourceType.NpcVendor].LookupByKey(new ConditionId(trainerId, (int)spellId, 0));
if (!conditions.Empty())
{
var conditionList = multiMap.LookupByKey(spellId);
if (!conditionList.Empty())
{
Log.outDebug(LogFilter.Condition, $"GetConditionsForTrainerSpell: found conditions for trainer id {trainerId} spell {spellId}");
return IsObjectMeetToConditions(player, conditionList);
}
Log.outDebug(LogFilter.Condition, $"GetConditionsForTrainerSpell: found conditions for trainer id {trainerId} spell {spellId}");
return IsObjectMeetToConditions(player, conditions);
}
return true;
}
public bool IsObjectMeetingVisibilityByObjectIdConditions(uint objectType, uint entry, WorldObject seer)
{
var conditions = objectVisibilityConditionStorage.LookupByKey((objectType, entry));
if (conditions != null)
var conditions = ConditionStorage[ConditionSourceType.ObjectIdVisibility].LookupByKey(new ConditionId(objectType, (int)entry, 0));
if (!conditions.Empty())
{
Log.outDebug(LogFilter.Condition, $"IsObjectMeetingVisibilityByObjectIdConditions: found conditions for objectType {objectType} entry {entry}");
return IsObjectMeetToConditions(seer, conditions);
}
return true;
}
public void LoadConditions(bool isReload = false)
{
uint oldMSTime = Time.GetMSTime();
@@ -345,25 +324,6 @@ namespace Game
//must clear all custom handled cases (groupped types) before reload
if (isReload)
{
Log.outInfo(LogFilter.Server, "Reseting Loot Conditions...");
LootStorage.Creature.ResetConditions();
LootStorage.Fishing.ResetConditions();
LootStorage.Gameobject.ResetConditions();
LootStorage.Items.ResetConditions();
LootStorage.Mail.ResetConditions();
LootStorage.Milling.ResetConditions();
LootStorage.Pickpocketing.ResetConditions();
LootStorage.Reference.ResetConditions();
LootStorage.Skinning.ResetConditions();
LootStorage.Disenchant.ResetConditions();
LootStorage.Prospecting.ResetConditions();
LootStorage.Spell.ResetConditions();
Log.outInfo(LogFilter.Server, "Re-Loading `gossip_menu` Table for Conditions!");
Global.ObjectMgr.LoadGossipMenu();
Log.outInfo(LogFilter.Server, "Re-Loading `gossip_menu_option` Table for Conditions!");
Global.ObjectMgr.LoadGossipMenuItems();
Global.SpellMgr.UnloadSpellInfoImplicitTargetConditionLists();
Global.ObjectMgr.UnloadPhaseConditions();
@@ -379,6 +339,7 @@ namespace Game
}
uint count = 0;
do
{
Condition cond = new();
@@ -410,6 +371,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Condition reference {1} is referencing self, skipped", iSourceTypeOrReferenceId);
continue;
}
cond.ReferenceId = (uint)Math.Abs(iConditionTypeOrReference);
string rowType = "reference template";
@@ -426,8 +388,6 @@ namespace Game
Log.outError(LogFilter.Sql, "Condition {0} {1} has useless data in value3 ({2})!", rowType, iSourceTypeOrReferenceId, cond.ConditionValue3);
if (cond.NegativeCondition)
Log.outError(LogFilter.Sql, "Condition {0} {1} has useless data in NegativeCondition ({2})!", rowType, iSourceTypeOrReferenceId, cond.NegativeCondition);
if (cond.SourceGroup != 0 && iSourceTypeOrReferenceId < 0)
Log.outError(LogFilter.Sql, "Condition {0} {1} has useless data in SourceGroup ({2})!", rowType, iSourceTypeOrReferenceId, cond.SourceGroup);
if (cond.SourceEntry != 0 && iSourceTypeOrReferenceId < 0)
Log.outError(LogFilter.Sql, "Condition {0} {1} has useless data in SourceEntry ({2})!", rowType, iSourceTypeOrReferenceId, cond.SourceEntry);
}
@@ -436,13 +396,17 @@ namespace Game
if (iSourceTypeOrReferenceId < 0)//it is a reference template
{
conditionReferenceStorage.Add((uint)Math.Abs(iSourceTypeOrReferenceId), cond);//add to reference storage
count++;
continue;
}//end of reference templates
if (cond.SourceGroup != 0)
Log.outError(LogFilter.Sql, $"Condition reference template {iSourceTypeOrReferenceId} has useless data in SourceGroup ({cond.SourceGroup})!");
if (cond.SourceEntry != 0)
Log.outError(LogFilter.Sql, $"Condition reference template {iSourceTypeOrReferenceId} has useless data in SourceEntry ({cond.SourceEntry})!");
if (cond.SourceId != 0)
Log.outError(LogFilter.Sql, $"Condition reference template {iSourceTypeOrReferenceId} has useless data in SourceId ({cond.SourceId})!");
//if not a reference and SourceType is invalid, skip
if (iConditionTypeOrReference >= 0 && !IsSourceTypeValid(cond))
cond.SourceType = ConditionSourceType.ReferenceCondition;
cond.SourceGroup = (uint)-iSourceTypeOrReferenceId;
}
else if (!IsSourceTypeValid(cond)) //if not a reference and SourceType is invalid, skip
continue;
//Grouping is only allowed for some types (loot templates, gossip menus, gossip items)
@@ -469,197 +433,127 @@ namespace Game
cond.ErrorTextId = 0;
}
if (cond.SourceGroup != 0)
{
bool valid = false;
// handle grouped conditions
switch (cond.SourceType)
{
case ConditionSourceType.CreatureLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Creature.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.DisenchantLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Disenchant.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.FishingLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Fishing.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.GameobjectLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Gameobject.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.ItemLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Items.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.MailLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Mail.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.MillingLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Milling.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.PickpocketingLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Pickpocketing.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.ProspectingLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Prospecting.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.ReferenceLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Reference.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.SkinningLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Skinning.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.SpellLootTemplate:
valid = AddToLootTemplate(cond, LootStorage.Spell.GetLootForConditionFill(cond.SourceGroup));
break;
case ConditionSourceType.GossipMenu:
valid = AddToGossipMenus(cond);
break;
case ConditionSourceType.GossipMenuOption:
valid = AddToGossipMenuItems(cond);
break;
case ConditionSourceType.SpellClickEvent:
{
if (!spellClickEventConditionStorage.ContainsKey(cond.SourceGroup))
spellClickEventConditionStorage[cond.SourceGroup] = new MultiMap<uint, Condition>();
var key = new ConditionId(cond.SourceGroup, cond.SourceEntry, cond.SourceId);
if (!ConditionStorage[cond.SourceType].ContainsKey(key))
ConditionStorage[cond.SourceType][key] = new List<Condition>();
spellClickEventConditionStorage[cond.SourceGroup].Add((uint)cond.SourceEntry, cond);
if (cond.ConditionType == ConditionTypes.Aura)
spellsUsedInSpellClickConditions.Add(cond.ConditionValue1);
++count;
continue; // do not add to m_AllocatedMemory to avoid double deleting
}
case ConditionSourceType.SpellImplicitTarget:
valid = AddToSpellImplicitTargetConditions(cond);
break;
case ConditionSourceType.VehicleSpell:
{
if (!vehicleSpellConditionStorage.ContainsKey(cond.SourceGroup))
vehicleSpellConditionStorage[cond.SourceGroup] = new MultiMap<uint, Condition>();
vehicleSpellConditionStorage[cond.SourceGroup].Add((uint)cond.SourceEntry, cond);
++count;
continue; // do not add to m_AllocatedMemory to avoid double deleting
}
case ConditionSourceType.SmartEvent:
{
//! TODO: PAIR_32 ?
var key = Tuple.Create(cond.SourceEntry, cond.SourceId);
if (!smartEventConditionStorage.ContainsKey(key))
smartEventConditionStorage[key] = new MultiMap<uint, Condition>();
smartEventConditionStorage[key].Add(cond.SourceGroup, cond);
++count;
continue;
}
case ConditionSourceType.NpcVendor:
{
if (!npcVendorConditionContainerStorage.ContainsKey(cond.SourceGroup))
npcVendorConditionContainerStorage[cond.SourceGroup] = new MultiMap<uint, Condition>();
npcVendorConditionContainerStorage[cond.SourceGroup].Add((uint)cond.SourceEntry, cond);
++count;
continue;
}
case ConditionSourceType.Phase:
valid = AddToPhases(cond);
break;
case ConditionSourceType.Graveyard:
valid = AddToGraveyardData(cond);
break;
case ConditionSourceType.AreaTrigger:
areaTriggerConditionContainerStorage.Add(Tuple.Create(cond.SourceGroup, cond.SourceEntry != 0), cond);
++count;
continue;
case ConditionSourceType.TrainerSpell:
{
if (!trainerSpellConditionContainerStorage.ContainsKey(cond.SourceGroup))
trainerSpellConditionContainerStorage[cond.SourceGroup] = new MultiMap<uint, Condition>();
trainerSpellConditionContainerStorage[cond.SourceGroup].Add((uint)cond.SourceEntry, cond);
++count;
continue;
}
case ConditionSourceType.ObjectIdVisibility:
{
objectVisibilityConditionStorage.Add((cond.SourceGroup, (uint)cond.SourceEntry), cond);
valid = true;
++count;
continue;
}
default:
break;
}
if (!valid)
Log.outError(LogFilter.Sql, "{0} Not handled grouped condition.", cond.ToString());
else
++count;
continue;
}
//add new Condition to storage based on Type/Entry
if (cond.SourceType == ConditionSourceType.SpellClickEvent && cond.ConditionType == ConditionTypes.Aura)
spellsUsedInSpellClickConditions.Add(cond.ConditionValue1);
conditionStorage[cond.SourceType].Add((uint)cond.SourceEntry, cond);
ConditionStorage[cond.SourceType][key].Add(cond);
++count;
}
while (result.NextRow());
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.CreatureLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Creature.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.DisenchantLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Disenchant.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.FishingLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Fishing.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.GameobjectLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Gameobject.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.ItemLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Items.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.MailLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Mail.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.MillingLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Milling.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.PickpocketingLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Pickpocketing.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.ProspectingLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Prospecting.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.ReferenceLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Reference.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.SkinningLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Skinning.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.SpellLootTemplate])
AddToLootTemplate(id, conditions, LootStorage.Spell.GetLootForConditionFill(id.SourceGroup));
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.GossipMenu])
AddToGossipMenus(id, conditions);
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.GossipMenuOption])
AddToGossipMenuItems(id, conditions);
spellsUsedInSpellClickConditions.Clear();
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.SpellClickEvent])
foreach (Condition condition in conditions)
if (condition.ConditionType == ConditionTypes.Aura)
spellsUsedInSpellClickConditions.Add(condition.ConditionValue1);
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.SpellImplicitTarget])
foreach (Condition condition in conditions)
AddToSpellImplicitTargetConditions(condition);
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.Phase])
AddToPhases(id, conditions);
foreach (var (id, conditions) in ConditionStorage[ConditionSourceType.Graveyard])
AddToGraveyardData(id, conditions);
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} conditions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
bool AddToLootTemplate(Condition cond, LootTemplate loot)
void AddToLootTemplate(ConditionId id, List<Condition> conditions, LootTemplate loot)
{
if (loot == null)
{
Log.outError(LogFilter.Sql, "{0} LootTemplate {1} not found.", cond.ToString(), cond.SourceGroup);
return false;
foreach (Condition condition in conditions)
Log.outError(LogFilter.Sql, $"{condition} LootTemplate {condition.SourceGroup} not found.");
return;
}
if (loot.AddConditionItem(cond))
return true;
if (loot.LinkConditions(id, new ConditionsReference(conditions)))
return;
Log.outError(LogFilter.Sql, "{0} Item {1} not found in LootTemplate {2}.", cond.ToString(), cond.SourceEntry, cond.SourceGroup);
return false;
foreach (Condition condition in conditions)
Log.outError(LogFilter.Sql, $"{condition} Item {condition.SourceEntry} not found in LootTemplate {condition.SourceGroup}.");
}
bool AddToGossipMenus(Condition cond)
void AddToGossipMenus(ConditionId id, List<Condition> conditions)
{
var pMenuBounds = Global.ObjectMgr.GetGossipMenusMapBounds(cond.SourceGroup);
var pMenuBounds = Global.ObjectMgr.GetGossipMenusMapBounds(id.SourceGroup);
if (!pMenuBounds.Empty())
{
foreach (var menu in pMenuBounds)
{
if (menu.MenuId == cond.SourceGroup && (menu.TextId == cond.SourceEntry || cond.SourceEntry == 0))
menu.Conditions.Add(cond);
if (menu.MenuId == id.SourceGroup && (menu.TextId == id.SourceEntry || id.SourceEntry == 0))
menu.Conditions = new(conditions);
}
return true;
return;
}
Log.outError(LogFilter.Sql, $"{cond} GossipMenu {cond.SourceGroup} not found.");
return false;
foreach (Condition condition in conditions)
Log.outError(LogFilter.Sql, $"{condition} GossipMenu {condition.SourceGroup} not found.");
}
bool AddToGossipMenuItems(Condition cond)
void AddToGossipMenuItems(ConditionId id, List<Condition> conditions)
{
var pMenuItemBounds = Global.ObjectMgr.GetGossipMenuItemsMapBounds(cond.SourceGroup);
var pMenuItemBounds = Global.ObjectMgr.GetGossipMenuItemsMapBounds(id.SourceGroup);
foreach (var gossipMenuItem in pMenuItemBounds)
{
if (gossipMenuItem.MenuID == cond.SourceGroup && gossipMenuItem.OrderIndex == cond.SourceEntry)
if (gossipMenuItem.MenuID == id.SourceGroup && gossipMenuItem.OrderIndex == id.SourceEntry)
{
gossipMenuItem.Conditions.Add(cond);
return true;
gossipMenuItem.Conditions = new(conditions);
return;
}
}
Log.outError(LogFilter.Sql, "{0} GossipMenuId {1} Item {2} not found.", cond.ToString(), cond.SourceGroup, cond.SourceEntry);
return false;
foreach (Condition condition in conditions)
Log.outError(LogFilter.Sql, $"{condition} GossipMenuId {condition.SourceGroup} Item {condition.SourceEntry} not found.");
}
bool AddToSpellImplicitTargetConditions(Condition cond)
void AddToSpellImplicitTargetConditions(Condition cond)
{
Global.SpellMgr.ForEachSpellInfoDifficulty((uint)cond.SourceEntry, spellInfo =>
{
@@ -705,9 +599,8 @@ namespace Game
// build new shared mask with found effect
uint sharedMask = (uint)(1 << (int)spellEffectInfo.EffectIndex);
List<Condition> cmp = spellEffectInfo.ImplicitTargetConditions;
for (uint effIndex = spellEffectInfo.EffectIndex + 1; effIndex < spellInfo.GetEffects().Count; ++effIndex)
if (spellInfo.GetEffect(effIndex).ImplicitTargetConditions == cmp)
if (spellInfo.GetEffect(effIndex).ImplicitTargetConditions == spellEffectInfo.ImplicitTargetConditions)
sharedMask |= (uint)(1 << (int)effIndex);
sharedMasks.Add(sharedMask);
@@ -764,14 +657,13 @@ namespace Game
}
}
});
return true;
}
bool AddToPhases(Condition cond)
void AddToPhases(ConditionId id, List<Condition> conditions)
{
if (cond.SourceEntry == 0)
if (id.SourceEntry == 0)
{
PhaseInfoStruct phaseInfo = Global.ObjectMgr.GetPhaseInfo(cond.SourceGroup);
PhaseInfoStruct phaseInfo = Global.ObjectMgr.GetPhaseInfo(id.SourceGroup);
if (phaseInfo != null)
{
bool found = false;
@@ -782,9 +674,9 @@ namespace Game
{
foreach (PhaseAreaInfo phase in phases)
{
if (phase.PhaseInfo.Id == cond.SourceGroup)
if (phase.PhaseInfo.Id == id.SourceGroup)
{
phase.Conditions.Add(cond);
phase.Conditions.AddRange(conditions);
found = true;
}
}
@@ -792,39 +684,39 @@ namespace Game
}
if (found)
return true;
return;
}
}
else
{
var phases = Global.ObjectMgr.GetPhasesForArea((uint)cond.SourceEntry);
var phases = Global.ObjectMgr.GetPhasesForArea((uint)id.SourceEntry);
foreach (PhaseAreaInfo phase in phases)
{
if (phase.PhaseInfo.Id == cond.SourceGroup)
if (phase.PhaseInfo.Id == id.SourceGroup)
{
phase.Conditions.Add(cond);
return true;
phase.Conditions.AddRange(conditions);
return;
}
}
}
Log.outError(LogFilter.Sql, $"{cond} Area {cond.SourceEntry} does not have phase {cond.SourceGroup}.");
return false;
foreach (Condition condition in conditions)
Log.outError(LogFilter.Sql, $"{condition} Area {condition.SourceEntry} does not have phase {condition.SourceGroup}.");
}
bool AddToGraveyardData(Condition cond)
void AddToGraveyardData(ConditionId id, List<Condition> conditions)
{
GraveyardData graveyard = Global.ObjectMgr.FindGraveyardData((uint)cond.SourceEntry, cond.SourceGroup);
GraveyardData graveyard = Global.ObjectMgr.FindGraveyardData((uint)id.SourceEntry, id.SourceGroup);
if (graveyard != null)
{
graveyard.Conditions.Add(cond);
return true;
graveyard.Conditions = new(conditions);
return;
}
Log.outError(LogFilter.Sql, $"{cond}, Graveyard {cond.SourceEntry} does not have ghostzone {cond.SourceGroup}.");
return false;
foreach (Condition condition in conditions)
Log.outError(LogFilter.Sql, $"{condition}, Graveyard {condition.SourceEntry} does not have ghostzone {condition.SourceGroup}.");
}
bool IsSourceTypeValid(Condition cond)
{
switch (cond.SourceType)
@@ -1206,7 +1098,7 @@ namespace Game
Log.outError(LogFilter.Sql, $"{cond.ToString()} in `condition` table, unexpected SourceEntry value (expected 0 or 1), ignoring.");
return false;
}
if (Global.AreaTriggerDataStorage.GetAreaTriggerTemplate(new AreaTriggerId(cond.SourceGroup, cond.SourceEntry != 0)) == null)
if (Global.AreaTriggerDataStorage.GetAreaTriggerTemplate(new AreaTriggerId(cond.SourceGroup, cond.SourceEntry == 1)) == null)
{
Log.outError(LogFilter.Sql, $"{cond.ToString()} in `condition` table, does not exist in `areatrigger_template`, ignoring.");
return false;
@@ -1883,26 +1775,10 @@ namespace Game
void Clean()
{
conditionReferenceStorage.Clear();
foreach (var (_, conditionsMap) in ConditionStorage)
conditionsMap.Clear();
conditionStorage.Clear();
for (ConditionSourceType i = 0; i < ConditionSourceType.Max; ++i)
conditionStorage[i] = new MultiMap<uint, Condition>();//add new empty list for SourceType
vehicleSpellConditionStorage.Clear();
smartEventConditionStorage.Clear();
spellClickEventConditionStorage.Clear();
spellsUsedInSpellClickConditions.Clear();
npcVendorConditionContainerStorage.Clear();
areaTriggerConditionContainerStorage.Clear();
trainerSpellConditionContainerStorage.Clear();
objectVisibilityConditionStorage.Clear();
}
static bool PlayerConditionCompare(int comparisonType, int value1, int value2)
@@ -2619,7 +2495,7 @@ namespace Game
case UnitConditionVariable.InCombat:
return unit.IsInCombat() ? 1 : 0;
case UnitConditionVariable.IsMoving:
return unit.HasUnitMovementFlag(MovementFlag.Forward | MovementFlag.Backward | MovementFlag.StrafeLeft | MovementFlag.StrafeRight) ? 1:0;
return unit.HasUnitMovementFlag(MovementFlag.Forward | MovementFlag.Backward | MovementFlag.StrafeLeft | MovementFlag.StrafeRight) ? 1 : 0;
case UnitConditionVariable.IsCasting:
case UnitConditionVariable.IsCastingSpell: // this is supposed to return spell id by client code but data always has 0 or 1
return unit.GetCurrentSpell(CurrentSpellTypes.Generic) != null ? 1 : 0;
@@ -2635,7 +2511,7 @@ namespace Game
return unit.GetExactDistSq(attacker) < distance * distance;
});
case UnitConditionVariable.IsAttackingMe:
return (otherUnit != null && unit.GetTarget() == otherUnit.GetGUID()) ? 1:0;
return (otherUnit != null && unit.GetTarget() == otherUnit.GetGUID()) ? 1 : 0;
case UnitConditionVariable.Range:
return otherUnit != null ? (int)unit.GetExactDist(otherUnit) : 0;
case UnitConditionVariable.InMeleeRange:
@@ -2802,7 +2678,7 @@ namespace Game
{
if (condition.Variable[i] == 0)
break;
int unitValue = GetUnitConditionVariable(unit, otherUnit, (UnitConditionVariable)condition.Variable[i], condition.Value[i]);
bool meets = false;
switch ((UnitConditionOp)condition.Op[i])
@@ -2840,7 +2716,7 @@ namespace Game
return !condition.GetFlags().HasFlag(UnitConditionFlags.LogicOr);
}
static int EvalSingleValue(ByteBuffer buffer, Map map)
{
WorldStateExpressionValueType valueType = (WorldStateExpressionValueType)buffer.ReadUInt8();
@@ -3024,16 +2900,8 @@ namespace Game
return false;
}
Dictionary<ConditionSourceType, MultiMap<uint, Condition>> conditionStorage = new();
MultiMap<uint, Condition> conditionReferenceStorage = new();
Dictionary<uint, MultiMap<uint, Condition>> vehicleSpellConditionStorage = new();
Dictionary<uint, MultiMap<uint, Condition>> spellClickEventConditionStorage = new();
Dictionary<ConditionSourceType, Dictionary<ConditionId, List<Condition>>> ConditionStorage = new();
List<uint> spellsUsedInSpellClickConditions = new();
Dictionary<uint, MultiMap<uint, Condition>> npcVendorConditionContainerStorage = new();
Dictionary<Tuple<int, uint>, MultiMap<uint, Condition>> smartEventConditionStorage = new();
MultiMap<Tuple<uint, bool>, Condition> areaTriggerConditionContainerStorage = new();
Dictionary<uint, MultiMap<uint, Condition>> trainerSpellConditionContainerStorage = new();
MultiMap<(uint objectType, uint objectId), Condition> objectVisibilityConditionStorage = new();
public string[] StaticSourceTypeData =
{
@@ -3150,4 +3018,34 @@ namespace Game
public bool HasConditionValue3;
}
}
public struct ConditionsReference
{
public List<Condition> Conditions = new();
public ConditionsReference(List<Condition> conditions)
{
Conditions = conditions;
}
public bool Meets(WorldObject obj)
{
return Global.ConditionMgr.IsObjectMeetToConditions(obj, Conditions);
}
public bool Meets(WorldObject obj1, WorldObject obj2)
{
return Global.ConditionMgr.IsObjectMeetToConditions(obj1, obj2, Conditions);
}
public bool Meets(ConditionSourceInfo sourceInfo)
{
return Global.ConditionMgr.IsObjectMeetToConditions(sourceInfo, Conditions);
}
public bool IsEmpty()
{
return Conditions.Empty();
}
}
}