Core/Creatures: Move creature difficulty specific data from creature_template table to creature_template_difficulty
Port From (https://github.com/TrinityCore/TrinityCore/commit/06d0b16f158e8793860d9edd11b990f20b1d0dac)
This commit is contained in:
@@ -60,7 +60,7 @@ namespace Framework.Database
|
||||
PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?");
|
||||
PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?");
|
||||
PrepareStatement(WorldStatements.SEL_COMMANDS, "SELECT name, help FROM command");
|
||||
PrepareStatement(WorldStatements.SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?");
|
||||
PrepareStatement(WorldStatements.SEL_CREATURE_TEMPLATE, "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, VehicleId, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?");
|
||||
PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?");
|
||||
PrepareStatement(WorldStatements.SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?");
|
||||
PrepareStatement(WorldStatements.SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_");
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Game.AI
|
||||
return false;
|
||||
|
||||
//experimental (unknown) flag not present
|
||||
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist))
|
||||
if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
|
||||
return false;
|
||||
|
||||
//not a player
|
||||
|
||||
@@ -283,7 +283,7 @@ namespace Game.AI
|
||||
return false;
|
||||
|
||||
//experimental (unknown) flag not present
|
||||
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist))
|
||||
if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
|
||||
return false;
|
||||
|
||||
if (!who.IsInAccessiblePlaceFor(me))
|
||||
|
||||
@@ -496,7 +496,7 @@ namespace Game.AI
|
||||
return false;
|
||||
|
||||
//experimental (unknown) flag not present
|
||||
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist))
|
||||
if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
|
||||
return false;
|
||||
|
||||
//not a player
|
||||
|
||||
@@ -124,7 +124,9 @@ namespace Game.Chat
|
||||
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoDynamicFlags, target.GetDynamicFlags());
|
||||
handler.SendSysMessage(CypherStrings.CommandRawpawntimes, defRespawnDelayStr, curRespawnDelayStr);
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoLoot, cInfo.LootId, cInfo.PickPocketId, cInfo.SkinLootId);
|
||||
|
||||
CreatureDifficulty creatureDifficulty = target.GetCreatureDifficulty();
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoLoot, creatureDifficulty.LootID, creatureDifficulty.PickPocketLootID, creatureDifficulty.SkinLootID);
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoDungeonId, target.GetInstanceId());
|
||||
|
||||
CreatureData data = Global.ObjectMgr.GetCreatureData(target.GetSpawnId());
|
||||
@@ -406,7 +408,7 @@ namespace Game.Chat
|
||||
|
||||
CreatureTemplate cInfo = creatureTarget.GetCreatureTemplate();
|
||||
|
||||
if (!cInfo.IsTameable(player.CanTameExoticPets()))
|
||||
if (!cInfo.IsTameable(player.CanTameExoticPets(), creatureTarget.GetCreatureDifficulty()))
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.CreatureNonTameable, cInfo.Entry);
|
||||
return false;
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Game.Entities
|
||||
{
|
||||
CreatureTemplate m_creatureInfo;
|
||||
CreatureData m_creatureData;
|
||||
CreatureDifficulty m_creatureDifficulty;
|
||||
|
||||
string[] m_stringIds = new string[3];
|
||||
string m_scriptStringId;
|
||||
|
||||
@@ -211,53 +211,30 @@ namespace Game.Entities
|
||||
|
||||
public bool InitEntry(uint entry, CreatureData data = null)
|
||||
{
|
||||
CreatureTemplate normalInfo = Global.ObjectMgr.GetCreatureTemplate(entry);
|
||||
if (normalInfo == null)
|
||||
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(entry);
|
||||
if (creatureInfo == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature.InitEntry creature entry {0} does not exist.", entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get difficulty 1 mode entry
|
||||
CreatureTemplate cInfo = null;
|
||||
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(GetMap().GetDifficultyID());
|
||||
while (cInfo == null && difficultyEntry != null)
|
||||
{
|
||||
int idx = CreatureTemplate.DifficultyIDToDifficultyEntryIndex(difficultyEntry.Id);
|
||||
if (idx == -1)
|
||||
break;
|
||||
|
||||
if (normalInfo.DifficultyEntry[idx] != 0)
|
||||
{
|
||||
cInfo = Global.ObjectMgr.GetCreatureTemplate(normalInfo.DifficultyEntry[idx]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (difficultyEntry.FallbackDifficultyID == 0)
|
||||
break;
|
||||
|
||||
difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficultyEntry.FallbackDifficultyID);
|
||||
}
|
||||
|
||||
if (cInfo == null)
|
||||
cInfo = normalInfo;
|
||||
|
||||
SetEntry(entry); // normal entry always
|
||||
m_creatureInfo = cInfo; // map mode related always
|
||||
m_creatureInfo = creatureInfo;
|
||||
SetEntry(entry);
|
||||
m_creatureDifficulty = creatureInfo.GetDifficulty(GetMap().GetDifficultyID());
|
||||
|
||||
// equal to player Race field, but creature does not have race
|
||||
SetRace(0);
|
||||
SetClass((Class)cInfo.UnitClass);
|
||||
SetClass((Class)creatureInfo.UnitClass);
|
||||
|
||||
// Cancel load if no model defined
|
||||
if (cInfo.GetFirstValidModel() == null)
|
||||
if (creatureInfo.GetFirstValidModel() == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has no model defined in table `creature_template`, can't load. ", entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
CreatureModel model = ObjectManager.ChooseDisplayId(cInfo, data);
|
||||
CreatureModelInfo minfo = Global.ObjectMgr.GetCreatureModelRandomGender(ref model, cInfo);
|
||||
CreatureModel model = ObjectManager.ChooseDisplayId(creatureInfo, data);
|
||||
CreatureModelInfo minfo = Global.ObjectMgr.GetCreatureModelRandomGender(ref model, creatureInfo);
|
||||
if (minfo == null) // Cancel load if no model defined
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has invalid model {1} defined in table `creature_template`, can't load.", entry, model.CreatureDisplayID);
|
||||
@@ -277,7 +254,7 @@ namespace Game.Entities
|
||||
LoadEquipment(data.equipmentId);
|
||||
}
|
||||
|
||||
SetName(normalInfo.Name); // at normal entry always
|
||||
SetName(creatureInfo.Name); // at normal entry always
|
||||
|
||||
SetModCastingSpeed(1.0f);
|
||||
SetModSpellHaste(1.0f);
|
||||
@@ -286,25 +263,25 @@ namespace Game.Entities
|
||||
SetModHasteRegen(1.0f);
|
||||
SetModTimeRate(1.0f);
|
||||
|
||||
SetSpeedRate(UnitMoveType.Walk, cInfo.SpeedWalk);
|
||||
SetSpeedRate(UnitMoveType.Run, cInfo.SpeedRun);
|
||||
SetSpeedRate(UnitMoveType.Walk, creatureInfo.SpeedWalk);
|
||||
SetSpeedRate(UnitMoveType.Run, creatureInfo.SpeedRun);
|
||||
SetSpeedRate(UnitMoveType.Swim, 1.0f); // using 1.0 rate
|
||||
SetSpeedRate(UnitMoveType.Flight, 1.0f); // using 1.0 rate
|
||||
|
||||
SetObjectScale(GetNativeObjectScale());
|
||||
|
||||
SetCanDualWield(cInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.UseOffhandAttack));
|
||||
SetCanDualWield(creatureInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.UseOffhandAttack));
|
||||
|
||||
// checked at loading
|
||||
DefaultMovementType = (MovementGeneratorType)(data != null ? data.movementType : cInfo.MovementType);
|
||||
DefaultMovementType = (MovementGeneratorType)(data != null ? data.movementType : creatureInfo.MovementType);
|
||||
if (m_wanderDistance == 0 && DefaultMovementType == MovementGeneratorType.Random)
|
||||
DefaultMovementType = MovementGeneratorType.Idle;
|
||||
|
||||
for (byte i = 0; i < SharedConst.MaxCreatureSpells; ++i)
|
||||
m_spells[i] = GetCreatureTemplate().Spells[i];
|
||||
|
||||
_staticFlags.ApplyFlag(CreatureStaticFlags.NoXp, cInfo.CreatureType == CreatureType.Critter || IsPet() || IsTotem() || cInfo.FlagsExtra.HasFlag(CreatureFlagsExtra.NoXP));
|
||||
_staticFlags.ApplyFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells, cInfo.TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit));
|
||||
_staticFlags.ApplyFlag(CreatureStaticFlags.NoXp, creatureInfo.CreatureType == CreatureType.Critter || IsPet() || IsTotem() || creatureInfo.FlagsExtra.HasFlag(CreatureFlagsExtra.NoXP));
|
||||
_staticFlags.ApplyFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells, GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -808,6 +785,8 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
CreatureDifficulty creatureDifficulty = cinfo.GetDifficulty(GetMap().GetDifficultyID());
|
||||
|
||||
//! Relocate before CreateFromProto, to initialize coords and allow
|
||||
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
|
||||
Relocate(pos);
|
||||
@@ -828,7 +807,7 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
// Allow players to see those units while dead, do it here (mayby altered by addon auras)
|
||||
if (cinfo.TypeFlags.HasAnyFlag(CreatureTypeFlags.VisibleToGhosts))
|
||||
if (creatureDifficulty.TypeFlags.HasAnyFlag(CreatureTypeFlags.VisibleToGhosts))
|
||||
m_serverSideVisibility.SetValue(ServerSideVisibilityType.Ghost, GhostVisibilityType.Alive | GhostVisibilityType.Ghost);
|
||||
|
||||
if (!CreateFromProto(guidlow, entry, data, vehId))
|
||||
@@ -1050,7 +1029,7 @@ namespace Game.Entities
|
||||
{
|
||||
base.AtEngage(target);
|
||||
|
||||
if (!GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.AllowMountedCombat))
|
||||
if (!GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.AllowMountedCombat))
|
||||
Dismount();
|
||||
|
||||
RefreshCanSwimFlag();
|
||||
@@ -1199,7 +1178,7 @@ namespace Game.Entities
|
||||
if (_lootId.HasValue)
|
||||
return _lootId.Value;
|
||||
|
||||
return GetCreatureTemplate().LootId;
|
||||
return GetCreatureDifficulty().LootID;
|
||||
}
|
||||
|
||||
public void SetLootId(uint? lootId)
|
||||
@@ -1470,7 +1449,7 @@ namespace Game.Entities
|
||||
// mana
|
||||
PowerType powerType = CalculateDisplayPowerType();
|
||||
SetCreateMana(stats.BaseMana);
|
||||
SetStatPctModifier(UnitMods.PowerStart + (int)powerType, UnitModifierPctType.Base, cInfo.ModMana * cInfo.ModManaExtra);
|
||||
SetStatPctModifier(UnitMods.PowerStart + (int)powerType, UnitModifierPctType.Base, GetCreatureDifficulty().ManaModifier);
|
||||
SetPowerType(powerType);
|
||||
|
||||
PowerTypeRecord powerTypeEntry = Global.DB2Mgr.GetPowerTypeEntry(powerType);
|
||||
@@ -2208,7 +2187,7 @@ namespace Game.Entities
|
||||
if (IsPet())
|
||||
return false;
|
||||
|
||||
return Convert.ToBoolean(GetCreatureTemplate().TypeFlags & CreatureTypeFlags.BossMob);
|
||||
return GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.BossMob);
|
||||
}
|
||||
|
||||
// select nearest hostile unit within the given distance (regardless of threat list).
|
||||
@@ -2462,7 +2441,7 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
// dependent from difficulty mode entry
|
||||
return Global.ObjectMgr.GetCreatureTemplateAddon(GetCreatureTemplate().Entry);
|
||||
return Global.ObjectMgr.GetCreatureTemplateAddon(GetEntry());
|
||||
}
|
||||
|
||||
public bool LoadCreaturesAddon()
|
||||
@@ -2716,29 +2695,29 @@ namespace Game.Entities
|
||||
|
||||
public void ApplyLevelScaling()
|
||||
{
|
||||
CreatureLevelScaling scaling = GetCreatureTemplate().GetLevelScaling(GetMap().GetDifficultyID());
|
||||
var levels = Global.DB2Mgr.GetContentTuningData(scaling.ContentTuningID, 0);
|
||||
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
|
||||
var levels = Global.DB2Mgr.GetContentTuningData(creatureDifficulty.ContentTuningID, 0);
|
||||
if (levels.HasValue)
|
||||
{
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ScalingLevelMin), levels.Value.MinLevel);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ScalingLevelMax), levels.Value.MaxLevel);
|
||||
}
|
||||
|
||||
int mindelta = Math.Min(scaling.DeltaLevelMax, scaling.DeltaLevelMin);
|
||||
int maxdelta = Math.Max(scaling.DeltaLevelMax, scaling.DeltaLevelMin);
|
||||
int mindelta = Math.Min(creatureDifficulty.DeltaLevelMax, creatureDifficulty.DeltaLevelMin);
|
||||
int maxdelta = Math.Max(creatureDifficulty.DeltaLevelMax, creatureDifficulty.DeltaLevelMin);
|
||||
int delta = mindelta == maxdelta ? mindelta : RandomHelper.IRand(mindelta, maxdelta);
|
||||
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ScalingLevelDelta), delta);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ContentTuningID), scaling.ContentTuningID);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ContentTuningID), creatureDifficulty.ContentTuningID);
|
||||
}
|
||||
|
||||
ulong GetMaxHealthByLevel(uint level)
|
||||
{
|
||||
CreatureTemplate cInfo = GetCreatureTemplate();
|
||||
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID());
|
||||
float baseHealth = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass);
|
||||
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
|
||||
float baseHealth = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
|
||||
|
||||
return (ulong)Math.Max(baseHealth * cInfo.ModHealth * cInfo.ModHealthExtra, 1.0f);
|
||||
return (ulong)Math.Max(baseHealth * creatureDifficulty.HealthModifier, 1.0f);
|
||||
}
|
||||
|
||||
public override float GetHealthMultiplierForTarget(WorldObject target)
|
||||
@@ -2756,8 +2735,8 @@ namespace Game.Entities
|
||||
public float GetBaseDamageForLevel(uint level)
|
||||
{
|
||||
CreatureTemplate cInfo = GetCreatureTemplate();
|
||||
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID());
|
||||
return Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureAutoAttackDps, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass);
|
||||
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
|
||||
return Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureAutoAttackDps, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
|
||||
}
|
||||
|
||||
public override float GetDamageMultiplierForTarget(WorldObject target)
|
||||
@@ -2773,9 +2752,9 @@ namespace Game.Entities
|
||||
float GetBaseArmorForLevel(uint level)
|
||||
{
|
||||
CreatureTemplate cInfo = GetCreatureTemplate();
|
||||
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID());
|
||||
float baseArmor = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureArmor, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass);
|
||||
return baseArmor * cInfo.ModArmor;
|
||||
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
|
||||
float baseArmor = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureArmor, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
|
||||
return baseArmor * creatureDifficulty.ArmorModifier;
|
||||
}
|
||||
|
||||
public override float GetArmorMultiplierForTarget(WorldObject target)
|
||||
@@ -3317,7 +3296,8 @@ namespace Game.Entities
|
||||
|
||||
public CreatureTemplate GetCreatureTemplate() { return m_creatureInfo; }
|
||||
public CreatureData GetCreatureData() { return m_creatureData; }
|
||||
|
||||
public CreatureDifficulty GetCreatureDifficulty() { return m_creatureDifficulty; }
|
||||
|
||||
public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
|
||||
{
|
||||
if (!allowDuplicate)
|
||||
|
||||
@@ -7,13 +7,13 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Game.Networking.Packets;
|
||||
using Game.Maps;
|
||||
using Game.DataStorage;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
public class CreatureTemplate
|
||||
{
|
||||
public uint Entry;
|
||||
public uint[] DifficultyEntry = new uint[SharedConst.MaxCreatureDifficulties];
|
||||
public uint[] KillCredit = new uint[SharedConst.MaxCreatureKillCredit];
|
||||
public List<CreatureModel> Models = new();
|
||||
public string Name;
|
||||
@@ -22,8 +22,7 @@ namespace Game.Entities
|
||||
public string TitleAlt;
|
||||
public string IconName;
|
||||
public List<uint> GossipMenuIds = new();
|
||||
public Dictionary<Difficulty, CreatureLevelScaling> scalingStorage = new();
|
||||
public int HealthScalingExpansion;
|
||||
public Dictionary<Difficulty, CreatureDifficulty> difficultyStorage = new();
|
||||
public uint RequiredExpansion;
|
||||
public uint VignetteID; // @todo Read Vignette.db2
|
||||
public uint Faction;
|
||||
@@ -45,29 +44,15 @@ namespace Game.Entities
|
||||
public CreatureFamily Family;
|
||||
public Class TrainerClass;
|
||||
public CreatureType CreatureType;
|
||||
public CreatureTypeFlags TypeFlags;
|
||||
public uint TypeFlags2;
|
||||
public uint LootId;
|
||||
public uint PickPocketId;
|
||||
public uint SkinLootId;
|
||||
public int[] Resistance = new int[7];
|
||||
public uint[] Spells = new uint[8];
|
||||
public uint VehicleId;
|
||||
public uint MinGold;
|
||||
public uint MaxGold;
|
||||
public string AIName;
|
||||
public uint MovementType;
|
||||
public CreatureMovementData Movement = new();
|
||||
public float ModHealth;
|
||||
public float ModHealthExtra;
|
||||
public float ModMana;
|
||||
public float ModManaExtra;
|
||||
public float ModArmor;
|
||||
public float ModDamage;
|
||||
public float ModExperience;
|
||||
public bool RacialLeader;
|
||||
public uint MovementId;
|
||||
public int CreatureDifficultyID;
|
||||
public int WidgetSetID;
|
||||
public int WidgetSetUnitConditionID;
|
||||
public bool RegenHealth;
|
||||
@@ -77,7 +62,7 @@ namespace Game.Entities
|
||||
public uint ScriptID;
|
||||
public string StringId;
|
||||
|
||||
public QueryCreatureResponse QueryData;
|
||||
public QueryCreatureResponse[] QueryData = new QueryCreatureResponse[(int)Locale.Total];
|
||||
|
||||
public CreatureModel GetModelByIdx(int idx)
|
||||
{
|
||||
@@ -142,74 +127,33 @@ namespace Game.Entities
|
||||
return CreatureModel.DefaultVisibleModel;
|
||||
}
|
||||
|
||||
public int GetHealthScalingExpansion()
|
||||
public bool IsExotic(CreatureDifficulty creatureDifficulty)
|
||||
{
|
||||
return HealthScalingExpansion == (int)Expansion.LevelCurrent ? (int)Expansion.WarlordsOfDraenor : HealthScalingExpansion;
|
||||
return creatureDifficulty.TypeFlags.HasFlag(CreatureTypeFlags.TameableExotic);
|
||||
}
|
||||
|
||||
public SkillType GetRequiredLootSkill()
|
||||
public bool IsTameable(bool canTameExotic, CreatureDifficulty creatureDifficulty)
|
||||
{
|
||||
if (TypeFlags.HasAnyFlag(CreatureTypeFlags.SkinWithHerbalism))
|
||||
return SkillType.Herbalism;
|
||||
else if (TypeFlags.HasAnyFlag(CreatureTypeFlags.SkinWithMining))
|
||||
return SkillType.Mining;
|
||||
else if (TypeFlags.HasAnyFlag(CreatureTypeFlags.SkinWithEngineering))
|
||||
return SkillType.Engineering;
|
||||
else
|
||||
return SkillType.Skinning; // normal case
|
||||
}
|
||||
|
||||
public bool IsExotic()
|
||||
{
|
||||
return (TypeFlags & CreatureTypeFlags.TameableExotic) != 0;
|
||||
}
|
||||
public bool IsTameable(bool canTameExotic)
|
||||
{
|
||||
if (CreatureType != CreatureType.Beast || Family == CreatureFamily.None || !TypeFlags.HasAnyFlag(CreatureTypeFlags.Tameable))
|
||||
if (CreatureType != CreatureType.Beast || Family == CreatureFamily.None || !creatureDifficulty.TypeFlags.HasFlag(CreatureTypeFlags.Tameable))
|
||||
return false;
|
||||
|
||||
// if can tame exotic then can tame any tameable
|
||||
return canTameExotic || !IsExotic();
|
||||
}
|
||||
|
||||
public static int DifficultyIDToDifficultyEntryIndex(uint difficulty)
|
||||
{
|
||||
switch ((Difficulty)difficulty)
|
||||
{
|
||||
case Difficulty.None:
|
||||
case Difficulty.Normal:
|
||||
case Difficulty.Raid10N:
|
||||
case Difficulty.Raid40:
|
||||
case Difficulty.Scenario3ManN:
|
||||
case Difficulty.NormalRaid:
|
||||
return -1;
|
||||
case Difficulty.Heroic:
|
||||
case Difficulty.Raid25N:
|
||||
case Difficulty.Scenario3ManHC:
|
||||
case Difficulty.HeroicRaid:
|
||||
return 0;
|
||||
case Difficulty.Raid10HC:
|
||||
case Difficulty.MythicKeystone:
|
||||
case Difficulty.MythicRaid:
|
||||
return 1;
|
||||
case Difficulty.Raid25HC:
|
||||
return 2;
|
||||
case Difficulty.LFR:
|
||||
case Difficulty.LFRNew:
|
||||
case Difficulty.EventRaid:
|
||||
case Difficulty.EventDungeon:
|
||||
case Difficulty.EventScenario:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return canTameExotic || !IsExotic(creatureDifficulty);
|
||||
}
|
||||
|
||||
public void InitializeQueryData()
|
||||
{
|
||||
QueryData = new QueryCreatureResponse();
|
||||
for (var loc = Locale.enUS; loc < Locale.Total; ++loc)
|
||||
QueryData[(int)loc] = BuildQueryData(loc, Difficulty.None);
|
||||
}
|
||||
|
||||
QueryData.CreatureID = Entry;
|
||||
QueryData.Allow = true;
|
||||
public QueryCreatureResponse BuildQueryData(Locale locale, Difficulty difficulty)
|
||||
{
|
||||
CreatureDifficulty creatureDifficulty = GetDifficulty(difficulty);
|
||||
|
||||
var queryTemp = new QueryCreatureResponse();
|
||||
|
||||
queryTemp.CreatureID = Entry;
|
||||
queryTemp.Allow = true;
|
||||
|
||||
CreatureStats stats = new();
|
||||
stats.Leader = RacialLeader;
|
||||
@@ -217,8 +161,8 @@ namespace Game.Entities
|
||||
stats.Name[0] = Name;
|
||||
stats.NameAlt[0] = FemaleName;
|
||||
|
||||
stats.Flags[0] = (uint)TypeFlags;
|
||||
stats.Flags[1] = TypeFlags2;
|
||||
stats.Flags[0] = (uint)creatureDifficulty.TypeFlags;
|
||||
stats.Flags[1] = creatureDifficulty.TypeFlags2;
|
||||
|
||||
stats.CreatureType = (int)CreatureType;
|
||||
stats.CreatureFamily = (int)Family;
|
||||
@@ -233,15 +177,15 @@ namespace Game.Entities
|
||||
stats.Display.CreatureDisplay.Add(new CreatureXDisplay(model.CreatureDisplayID, model.DisplayScale, model.Probability));
|
||||
}
|
||||
|
||||
stats.HpMulti = ModHealth;
|
||||
stats.EnergyMulti = ModMana;
|
||||
stats.HpMulti = creatureDifficulty.HealthModifier;
|
||||
stats.EnergyMulti = creatureDifficulty.ManaModifier;
|
||||
|
||||
stats.CreatureMovementInfoID = MovementId;
|
||||
stats.RequiredExpansion = RequiredExpansion;
|
||||
stats.HealthScalingExpansion = HealthScalingExpansion;
|
||||
stats.HealthScalingExpansion = creatureDifficulty.HealthScalingExpansion;
|
||||
stats.VignetteID = VignetteID;
|
||||
stats.Class = (int)UnitClass;
|
||||
stats.CreatureDifficultyID = CreatureDifficultyID;
|
||||
stats.CreatureDifficultyID = creatureDifficulty.CreatureDifficultyID;
|
||||
stats.WidgetSetID = WidgetSetID;
|
||||
stats.WidgetSetUnitConditionID = WidgetSetUnitConditionID;
|
||||
|
||||
@@ -249,20 +193,41 @@ namespace Game.Entities
|
||||
stats.TitleAlt = TitleAlt;
|
||||
stats.CursorName = IconName;
|
||||
|
||||
var items = Global.ObjectMgr.GetCreatureQuestItemList(Entry);
|
||||
var items = Global.ObjectMgr.GetCreatureQuestItemList(Entry, difficulty);
|
||||
if (items != null)
|
||||
stats.QuestItems.AddRange(items);
|
||||
|
||||
QueryData.Stats = stats;
|
||||
if (locale != Locale.enUS)
|
||||
{
|
||||
CreatureLocale creatureLocale = Global.ObjectMgr.GetCreatureLocale(Entry);
|
||||
if (creatureLocale != null)
|
||||
{
|
||||
string name = stats.Name[0];
|
||||
string nameAlt = stats.NameAlt[0];
|
||||
|
||||
ObjectManager.GetLocaleString(creatureLocale.Name, locale, ref name);
|
||||
ObjectManager.GetLocaleString(creatureLocale.NameAlt, locale, ref nameAlt);
|
||||
ObjectManager.GetLocaleString(creatureLocale.Title, locale, ref stats.Title);
|
||||
ObjectManager.GetLocaleString(creatureLocale.TitleAlt, locale, ref stats.TitleAlt);
|
||||
}
|
||||
}
|
||||
|
||||
queryTemp.Stats = stats;
|
||||
return queryTemp;
|
||||
}
|
||||
|
||||
public CreatureLevelScaling GetLevelScaling(Difficulty difficulty)
|
||||
public CreatureDifficulty GetDifficulty(Difficulty difficulty)
|
||||
{
|
||||
var creatureLevelScaling = scalingStorage.LookupByKey(difficulty);
|
||||
if (creatureLevelScaling != null)
|
||||
return creatureLevelScaling;
|
||||
var creatureDifficulty = difficultyStorage.LookupByKey(difficulty);
|
||||
if (creatureDifficulty != null)
|
||||
return creatureDifficulty;
|
||||
|
||||
return new CreatureLevelScaling();
|
||||
// If there is no data for the difficulty, try to get data for the fallback difficulty
|
||||
var difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficulty);
|
||||
if (difficultyEntry != null)
|
||||
return GetDifficulty((Difficulty)difficultyEntry.FallbackDifficultyID);
|
||||
|
||||
return new CreatureDifficulty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +312,7 @@ namespace Game.Entities
|
||||
return $"Ground: {Ground}, Swim: {Swim}, Flight: {Flight} {(Rooted ? ", Rooted" : "")}, Chase: {Chase}, Random: {Random}, InteractionPauseTimer: {InteractionPauseTimer}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CreatureModelInfo
|
||||
{
|
||||
public float BoundingRadius;
|
||||
@@ -462,10 +427,49 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
public class CreatureLevelScaling
|
||||
public class CreatureDifficulty
|
||||
{
|
||||
public short DeltaLevelMin;
|
||||
public short DeltaLevelMax;
|
||||
public uint ContentTuningID;
|
||||
public int HealthScalingExpansion;
|
||||
public float HealthModifier;
|
||||
public float ManaModifier;
|
||||
public float ArmorModifier;
|
||||
public float DamageModifier;
|
||||
public int CreatureDifficultyID;
|
||||
public CreatureTypeFlags TypeFlags;
|
||||
public uint TypeFlags2;
|
||||
public uint LootID;
|
||||
public uint PickPocketLootID;
|
||||
public uint SkinLootID;
|
||||
public uint GoldMin;
|
||||
public uint GoldMax;
|
||||
|
||||
public CreatureDifficulty()
|
||||
{
|
||||
HealthModifier = 1.0f;
|
||||
ManaModifier = 1.0f;
|
||||
ArmorModifier = 1.0f;
|
||||
DamageModifier = 1.0f;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
public int GetHealthScalingExpansion()
|
||||
{
|
||||
return HealthScalingExpansion == (int)Expansion.LevelCurrent ? (int)PlayerConst.CurrentExpansion : HealthScalingExpansion;
|
||||
}
|
||||
|
||||
public SkillType GetRequiredLootSkill()
|
||||
{
|
||||
if (TypeFlags.HasFlag(CreatureTypeFlags.SkinWithHerbalism))
|
||||
return SkillType.Herbalism;
|
||||
else if (TypeFlags.HasFlag(CreatureTypeFlags.SkinWithMining))
|
||||
return SkillType.Mining;
|
||||
else if (TypeFlags.HasFlag(CreatureTypeFlags.SkinWithEngineering))
|
||||
return SkillType.Engineering;
|
||||
else
|
||||
return SkillType.Skinning; // Default case
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2773,7 +2773,7 @@ namespace Game.Entities
|
||||
{
|
||||
Creature creatureTarget = target.ToCreature();
|
||||
if (creatureTarget != null)
|
||||
return creatureTarget.HasFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells) || creatureTarget.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist);
|
||||
return creatureTarget.HasFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells) || creatureTarget.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Game.Entities
|
||||
if (petInfo.Type == PetType.Hunter)
|
||||
{
|
||||
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(petInfo.CreatureId);
|
||||
if (creatureInfo == null || !creatureInfo.IsTameable(owner.CanTameExoticPets()))
|
||||
if (creatureInfo == null || !creatureInfo.IsTameable(owner.CanTameExoticPets(), GetCreatureDifficulty()))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5169,11 +5169,11 @@ namespace Game.Entities
|
||||
return null;
|
||||
|
||||
// Deathstate checks
|
||||
if (!IsAlive() && !Convert.ToBoolean(creature.GetCreatureTemplate().TypeFlags & CreatureTypeFlags.VisibleToGhosts))
|
||||
if (!IsAlive() && !creature.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.VisibleToGhosts))
|
||||
return null;
|
||||
|
||||
// alive or spirit healer
|
||||
if (!creature.IsAlive() && !Convert.ToBoolean(creature.GetCreatureTemplate().TypeFlags & CreatureTypeFlags.InteractWhileDead))
|
||||
if (!creature.IsAlive() && !creature.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.InteractWhileDead))
|
||||
return null;
|
||||
|
||||
// appropriate npc type
|
||||
|
||||
@@ -2254,7 +2254,7 @@ namespace Game.Entities
|
||||
float basePct = GetPctModifierValue(unitMod, UnitModifierPctType.Base) * attackSpeedMulti;
|
||||
float totalValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Total);
|
||||
float totalPct = addTotalPct ? GetPctModifierValue(unitMod, UnitModifierPctType.Total) : 1.0f;
|
||||
float dmgMultiplier = GetCreatureTemplate().ModDamage; // = ModDamage * _GetDamageMod(rank);
|
||||
float dmgMultiplier = GetCreatureDifficulty().DamageModifier; // = DamageModifier * _GetDamageMod(rank);
|
||||
|
||||
minDamage = ((weaponMinDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
|
||||
maxDamage = ((weaponMaxDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
|
||||
|
||||
@@ -584,7 +584,8 @@ namespace Game.Entities
|
||||
CreatureBaseStats stats = Global.ObjectMgr.GetCreatureBaseStats(petlevel, cinfo.UnitClass);
|
||||
ApplyLevelScaling();
|
||||
|
||||
SetCreateHealth((uint)Math.Max(Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, petlevel, cinfo.GetHealthScalingExpansion(), m_unitData.ContentTuningID, (Class)cinfo.UnitClass) * cinfo.ModHealth * cinfo.ModHealthExtra * GetHealthMod(cinfo.Rank), 1.0f));
|
||||
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
|
||||
SetCreateHealth((uint)Math.Max(Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, petlevel, creatureDifficulty.GetHealthScalingExpansion(), m_unitData.ContentTuningID, (Class)cinfo.UnitClass) * creatureDifficulty.HealthModifier * GetHealthMod(cinfo.Rank), 1.0f));
|
||||
SetCreateMana(stats.BaseMana);
|
||||
|
||||
SetCreateStat(Stats.Strength, 22);
|
||||
|
||||
@@ -772,7 +772,7 @@ namespace Game.Entities
|
||||
if (dungeonEncounter != null)
|
||||
{
|
||||
creature.m_personalLoot = LootManager.GenerateDungeonEncounterPersonalLoot(dungeonEncounter.Id, creature.GetLootId(),
|
||||
LootStorage.Creature, LootType.Corpse, creature, creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold,
|
||||
LootStorage.Creature, LootType.Corpse, creature, creature.GetCreatureDifficulty().GoldMin, creature.GetCreatureDifficulty().GoldMax,
|
||||
(ushort)creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext(), tappers);
|
||||
}
|
||||
else if (!tappers.Empty())
|
||||
@@ -787,7 +787,7 @@ namespace Game.Entities
|
||||
loot.FillLoot(lootid, LootStorage.Creature, looter, dungeonEncounter != null, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
|
||||
|
||||
if (creature.GetLootMode() > 0)
|
||||
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
|
||||
loot.GenerateMoneyLoot(creature.GetCreatureDifficulty().GoldMin, creature.GetCreatureDifficulty().GoldMax);
|
||||
|
||||
if (group)
|
||||
loot.NotifyLootList(creature.GetMap());
|
||||
@@ -814,7 +814,7 @@ namespace Game.Entities
|
||||
loot.FillLoot(lootid, LootStorage.Creature, tapper, true, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
|
||||
|
||||
if (creature.GetLootMode() > 0)
|
||||
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
|
||||
loot.GenerateMoneyLoot(creature.GetCreatureDifficulty().GoldMin, creature.GetCreatureDifficulty().GoldMax);
|
||||
|
||||
creature.m_personalLoot[tapper.GetGUID()] = loot;
|
||||
}
|
||||
@@ -918,7 +918,7 @@ namespace Game.Entities
|
||||
else
|
||||
creature.AllLootRemovedFromCorpse();
|
||||
|
||||
if (creature.CanHaveLoot() && LootStorage.Skinning.HaveLootFor(creature.GetCreatureTemplate().SkinLootId))
|
||||
if (creature.CanHaveLoot() && LootStorage.Skinning.HaveLootFor(creature.GetCreatureDifficulty().SkinLootID))
|
||||
{
|
||||
creature.SetDynamicFlag(UnitDynFlags.CanSkin);
|
||||
creature.SetUnitFlag(UnitFlags.Skinnable);
|
||||
|
||||
@@ -25,14 +25,7 @@ namespace Game
|
||||
{
|
||||
public sealed class ObjectManager : Singleton<ObjectManager>
|
||||
{
|
||||
ObjectManager()
|
||||
{
|
||||
for (var i = 0; i < SharedConst.MaxCreatureDifficulties; ++i)
|
||||
{
|
||||
difficultyEntries[i] = new List<uint>();
|
||||
hasDifficultyEntries[i] = new List<uint>();
|
||||
}
|
||||
}
|
||||
ObjectManager() { }
|
||||
|
||||
//Static Methods
|
||||
public static bool NormalizePlayerName(ref string name)
|
||||
@@ -1792,44 +1785,35 @@ namespace Game
|
||||
CreatureTemplate creature = new();
|
||||
creature.Entry = entry;
|
||||
|
||||
for (var i = 0; i < SharedConst.MaxCreatureDifficulties; ++i)
|
||||
creature.DifficultyEntry[i] = fields.Read<uint>(1 + i);
|
||||
|
||||
for (var i = 0; i < 2; ++i)
|
||||
creature.KillCredit[i] = fields.Read<uint>(4 + i);
|
||||
creature.KillCredit[i] = fields.Read<uint>(1 + i);
|
||||
|
||||
creature.Name = fields.Read<string>(6);
|
||||
creature.FemaleName = fields.Read<string>(7);
|
||||
creature.SubName = fields.Read<string>(8);
|
||||
creature.TitleAlt = fields.Read<string>(9);
|
||||
creature.IconName = fields.Read<string>(10);
|
||||
creature.HealthScalingExpansion = fields.Read<int>(11);
|
||||
creature.RequiredExpansion = fields.Read<uint>(12);
|
||||
creature.VignetteID = fields.Read<uint>(13);
|
||||
creature.Faction = fields.Read<uint>(14);
|
||||
creature.Npcflag = fields.Read<ulong>(15);
|
||||
creature.SpeedWalk = fields.Read<float>(16);
|
||||
creature.SpeedRun = fields.Read<float>(17);
|
||||
creature.Scale = fields.Read<float>(18);
|
||||
creature.Rank = (CreatureEliteType)fields.Read<uint>(19);
|
||||
creature.DmgSchool = fields.Read<uint>(20);
|
||||
creature.BaseAttackTime = fields.Read<uint>(21);
|
||||
creature.RangeAttackTime = fields.Read<uint>(22);
|
||||
creature.BaseVariance = fields.Read<float>(23);
|
||||
creature.RangeVariance = fields.Read<float>(24);
|
||||
creature.UnitClass = fields.Read<uint>(25);
|
||||
creature.UnitFlags = (UnitFlags)fields.Read<uint>(26);
|
||||
creature.UnitFlags2 = fields.Read<uint>(27);
|
||||
creature.UnitFlags3 = fields.Read<uint>(28);
|
||||
creature.DynamicFlags = fields.Read<uint>(29);
|
||||
creature.Family = (CreatureFamily)fields.Read<uint>(30);
|
||||
creature.TrainerClass = (Class)fields.Read<byte>(31);
|
||||
creature.CreatureType = (CreatureType)fields.Read<byte>(32);
|
||||
creature.TypeFlags = (CreatureTypeFlags)fields.Read<uint>(33);
|
||||
creature.TypeFlags2 = fields.Read<uint>(34);
|
||||
creature.LootId = fields.Read<uint>(35);
|
||||
creature.PickPocketId = fields.Read<uint>(36);
|
||||
creature.SkinLootId = fields.Read<uint>(37);
|
||||
creature.Name = fields.Read<string>(3);
|
||||
creature.FemaleName = fields.Read<string>(4);
|
||||
creature.SubName = fields.Read<string>(5);
|
||||
creature.TitleAlt = fields.Read<string>(6);
|
||||
creature.IconName = fields.Read<string>(7);
|
||||
creature.RequiredExpansion = fields.Read<uint>(8);
|
||||
creature.VignetteID = fields.Read<uint>(9);
|
||||
creature.Faction = fields.Read<uint>(10);
|
||||
creature.Npcflag = fields.Read<ulong>(11);
|
||||
creature.SpeedWalk = fields.Read<float>(12);
|
||||
creature.SpeedRun = fields.Read<float>(13);
|
||||
creature.Scale = fields.Read<float>(14);
|
||||
creature.Rank = (CreatureEliteType)fields.Read<uint>(15);
|
||||
creature.DmgSchool = fields.Read<uint>(16);
|
||||
creature.BaseAttackTime = fields.Read<uint>(17);
|
||||
creature.RangeAttackTime = fields.Read<uint>(18);
|
||||
creature.BaseVariance = fields.Read<float>(19);
|
||||
creature.RangeVariance = fields.Read<float>(20);
|
||||
creature.UnitClass = fields.Read<uint>(21);
|
||||
creature.UnitFlags = (UnitFlags)fields.Read<uint>(22);
|
||||
creature.UnitFlags2 = fields.Read<uint>(23);
|
||||
creature.UnitFlags3 = fields.Read<uint>(24);
|
||||
creature.DynamicFlags = fields.Read<uint>(25);
|
||||
creature.Family = (CreatureFamily)fields.Read<uint>(26);
|
||||
creature.TrainerClass = (Class)fields.Read<byte>(27);
|
||||
creature.CreatureType = (CreatureType)fields.Read<byte>(28);
|
||||
|
||||
for (var i = (int)SpellSchools.Holy; i < (int)SpellSchools.Max; ++i)
|
||||
creature.Resistance[i] = 0;
|
||||
@@ -1837,51 +1821,42 @@ namespace Game
|
||||
for (var i = 0; i < SharedConst.MaxCreatureSpells; ++i)
|
||||
creature.Spells[i] = 0;
|
||||
|
||||
creature.VehicleId = fields.Read<uint>(38);
|
||||
creature.MinGold = fields.Read<uint>(39);
|
||||
creature.MaxGold = fields.Read<uint>(40);
|
||||
creature.AIName = fields.Read<string>(41);
|
||||
creature.MovementType = fields.Read<uint>(42);
|
||||
creature.VehicleId = fields.Read<uint>(29);
|
||||
creature.AIName = fields.Read<string>(30);
|
||||
creature.MovementType = fields.Read<uint>(31);
|
||||
|
||||
if (!fields.IsNull(43))
|
||||
creature.Movement.Ground = (CreatureGroundMovementType)fields.Read<byte>(43);
|
||||
if (!fields.IsNull(32))
|
||||
creature.Movement.Ground = (CreatureGroundMovementType)fields.Read<byte>(32);
|
||||
|
||||
if (!fields.IsNull(44))
|
||||
creature.Movement.Swim = fields.Read<bool>(44);
|
||||
if (!fields.IsNull(33))
|
||||
creature.Movement.Swim = fields.Read<bool>(33);
|
||||
|
||||
if (!fields.IsNull(45))
|
||||
creature.Movement.Flight = (CreatureFlightMovementType)fields.Read<byte>(45);
|
||||
if (!fields.IsNull(34))
|
||||
creature.Movement.Flight = (CreatureFlightMovementType)fields.Read<byte>(34);
|
||||
|
||||
if (!fields.IsNull(46))
|
||||
creature.Movement.Rooted = fields.Read<bool>(46);
|
||||
if (!fields.IsNull(35))
|
||||
creature.Movement.Rooted = fields.Read<bool>(35);
|
||||
|
||||
if (!fields.IsNull(47))
|
||||
creature.Movement.Chase = (CreatureChaseMovementType)fields.Read<byte>(47);
|
||||
if (!fields.IsNull(36))
|
||||
creature.Movement.Chase = (CreatureChaseMovementType)fields.Read<byte>(36);
|
||||
|
||||
if (!fields.IsNull(48))
|
||||
creature.Movement.Random = (CreatureRandomMovementType)fields.Read<byte>(48);
|
||||
if (!fields.IsNull(37))
|
||||
creature.Movement.Random = (CreatureRandomMovementType)fields.Read<byte>(37);
|
||||
|
||||
if (!fields.IsNull(49))
|
||||
creature.Movement.InteractionPauseTimer = fields.Read<uint>(49);
|
||||
if (!fields.IsNull(38))
|
||||
creature.Movement.InteractionPauseTimer = fields.Read<uint>(38);
|
||||
|
||||
creature.ModHealth = fields.Read<float>(50);
|
||||
creature.ModHealthExtra = fields.Read<float>(51);
|
||||
creature.ModMana = fields.Read<float>(52);
|
||||
creature.ModManaExtra = fields.Read<float>(53);
|
||||
creature.ModArmor = fields.Read<float>(54);
|
||||
creature.ModDamage = fields.Read<float>(55);
|
||||
creature.ModExperience = fields.Read<float>(56);
|
||||
creature.RacialLeader = fields.Read<bool>(57);
|
||||
creature.MovementId = fields.Read<uint>(58);
|
||||
creature.CreatureDifficultyID = fields.Read<int>(59);
|
||||
creature.WidgetSetID = fields.Read<int>(60);
|
||||
creature.WidgetSetUnitConditionID = fields.Read<int>(61);
|
||||
creature.RegenHealth = fields.Read<bool>(62);
|
||||
creature.MechanicImmuneMask = fields.Read<ulong>(63);
|
||||
creature.SpellSchoolImmuneMask = fields.Read<uint>(64);
|
||||
creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(65);
|
||||
creature.ScriptID = GetScriptId(fields.Read<string>(66));
|
||||
creature.StringId = fields.Read<string>(67);
|
||||
creature.ModExperience = fields.Read<float>(39);
|
||||
creature.RacialLeader = fields.Read<bool>(40);
|
||||
creature.MovementId = fields.Read<uint>(41);
|
||||
creature.WidgetSetID = fields.Read<int>(42);
|
||||
creature.WidgetSetUnitConditionID = fields.Read<int>(43);
|
||||
creature.RegenHealth = fields.Read<bool>(44);
|
||||
creature.MechanicImmuneMask = fields.Read<ulong>(45);
|
||||
creature.SpellSchoolImmuneMask = fields.Read<uint>(46);
|
||||
creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(47);
|
||||
creature.ScriptID = GetScriptId(fields.Read<string>(48));
|
||||
creature.StringId = fields.Read<string>(49);
|
||||
|
||||
creatureTemplateStorage[entry] = creature;
|
||||
}
|
||||
@@ -2392,8 +2367,8 @@ namespace Game
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
|
||||
// 0 1 2
|
||||
SQLResult result = DB.World.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
|
||||
// 0 1 2 3
|
||||
SQLResult result = DB.World.Query("SELECT CreatureEntry, DifficultyID, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
|
||||
if (result.IsEmpty())
|
||||
{
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
|
||||
@@ -2404,22 +2379,23 @@ namespace Game
|
||||
do
|
||||
{
|
||||
uint entry = result.Read<uint>(0);
|
||||
uint item = result.Read<uint>(1);
|
||||
uint idx = result.Read<uint>(2);
|
||||
Difficulty difficulty = (Difficulty)result.Read<byte>(1);
|
||||
uint item = result.Read<uint>(2);
|
||||
uint idx = result.Read<uint>(3);
|
||||
|
||||
if (!creatureTemplateStorage.ContainsKey(entry))
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Table `creature_questitem` has data for nonexistent creature (entry: {0}, idx: {1}), skipped", entry, idx);
|
||||
Log.outError(LogFilter.Sql, $"Table `creature_questitem` has data for nonexistent creature (entry: {entry}, difficulty: {difficulty} idx: {idx}), skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CliDB.ItemStorage.ContainsKey(item))
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Table `creature_questitem` has nonexistent item (ID: {0}) in creature (entry: {1}, idx: {2}), skipped", item, entry, idx);
|
||||
Log.outError(LogFilter.Sql, $"Table `creature_questitem` has nonexistent item (ID: {item}) in creature (entry: {entry}, difficulty: {difficulty} idx: {idx}), skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
creatureQuestItemStorage.Add(entry, item);
|
||||
creatureQuestItemStorage.Add((entry, difficulty), item);
|
||||
|
||||
++count;
|
||||
}
|
||||
@@ -2709,15 +2685,15 @@ namespace Game
|
||||
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template sparring rows in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||
}
|
||||
|
||||
public void LoadCreatureScalingData()
|
||||
public void LoadCreatureTemplateDifficulty()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
|
||||
// 0 1 2 3 4
|
||||
var result = DB.World.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID FROM creature_template_scaling ORDER BY Entry");
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
||||
SQLResult result = DB.World.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID, HealthScalingExpansion, HealthModifier, ManaModifier, ArmorModifier, DamageModifier, CreatureDifficultyID, TypeFlags, TypeFlags2, LootID, PickPocketLootID, SkinLootID, GoldMin, GoldMax FROM creature_template_difficulty ORDER BY Entry");
|
||||
if (result.IsEmpty())
|
||||
{
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature template scaling definitions. DB table `creature_template_scaling` is empty.");
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature template difficulty definitions. DB table `creature_template_difficulty` is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2730,196 +2706,55 @@ namespace Game
|
||||
var template = creatureTemplateStorage.LookupByKey(entry);
|
||||
if (template == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"Creature template (Entry: {entry}) does not exist but has a record in `creature_template_scaling`");
|
||||
Log.outError(LogFilter.Sql, $"Creature template (Entry: {entry}) does not exist but has a record in `creature_template_difficulty`");
|
||||
continue;
|
||||
}
|
||||
|
||||
CreatureLevelScaling creatureLevelScaling = new();
|
||||
creatureLevelScaling.DeltaLevelMin = result.Read<short>(2);
|
||||
creatureLevelScaling.DeltaLevelMax = result.Read<short>(3);
|
||||
creatureLevelScaling.ContentTuningID = result.Read<uint>(4);
|
||||
CreatureDifficulty creatureDifficulty = new();
|
||||
creatureDifficulty.DeltaLevelMin = result.Read<short>(2);
|
||||
creatureDifficulty.DeltaLevelMax = result.Read<short>(3);
|
||||
creatureDifficulty.ContentTuningID = result.Read<uint>(4);
|
||||
creatureDifficulty.HealthScalingExpansion = result.Read<int>(5);
|
||||
creatureDifficulty.HealthModifier = result.Read<float>(6);
|
||||
creatureDifficulty.ManaModifier = result.Read<float>(7);
|
||||
creatureDifficulty.ArmorModifier = result.Read<float>(8);
|
||||
creatureDifficulty.DamageModifier = result.Read<float>(9);
|
||||
creatureDifficulty.CreatureDifficultyID = result.Read<int>(10);
|
||||
creatureDifficulty.TypeFlags = (CreatureTypeFlags)result.Read<uint>(11);
|
||||
creatureDifficulty.TypeFlags2 = result.Read<uint>(12);
|
||||
creatureDifficulty.LootID = result.Read<uint>(13);
|
||||
creatureDifficulty.PickPocketLootID = result.Read<uint>(14);
|
||||
creatureDifficulty.SkinLootID = result.Read<uint>(15);
|
||||
creatureDifficulty.GoldMin = result.Read<uint>(16);
|
||||
creatureDifficulty.GoldMax = result.Read<uint>(17);
|
||||
|
||||
template.scalingStorage[difficulty] = creatureLevelScaling;
|
||||
// TODO: Check if this still applies
|
||||
creatureDifficulty.DamageModifier *= Creature._GetDamageMod(template.Rank);
|
||||
|
||||
// Assign creature level scaling to creature difficulty entry (if any)
|
||||
// TODO: Drop the use of creature difficulties
|
||||
int difficultyIndex = CreatureTemplate.DifficultyIDToDifficultyEntryIndex((uint)difficulty);
|
||||
if (difficultyIndex != -1)
|
||||
if (creatureDifficulty.HealthScalingExpansion < (int)Expansion.LevelCurrent || creatureDifficulty.HealthScalingExpansion >= (int)Expansion.Max)
|
||||
{
|
||||
uint difficultyEntry = template.DifficultyEntry[difficultyIndex];
|
||||
if (difficultyEntry != 0)
|
||||
{
|
||||
var difficultyTemplate = creatureTemplateStorage.LookupByKey(difficultyEntry);
|
||||
if (difficultyTemplate != null)
|
||||
difficultyTemplate.scalingStorage[difficulty] = creatureLevelScaling;
|
||||
}
|
||||
Log.outError(LogFilter.Sql, $"Table `creature_template_difficulty` lists creature (ID: {entry}) with invalid `HealthScalingExpansion` {creatureDifficulty.HealthScalingExpansion}. Ignored and set to 0.");
|
||||
creatureDifficulty.HealthScalingExpansion = 0;
|
||||
}
|
||||
|
||||
if (creatureDifficulty.GoldMin > creatureDifficulty.GoldMax)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"Table `creature_template_difficulty` lists creature (ID: {entry}) with `GoldMin` {creatureDifficulty.GoldMin} greater than `GoldMax` {creatureDifficulty.GoldMax}, setting `GoldMax` to {creatureDifficulty.GoldMin}.");
|
||||
creatureDifficulty.GoldMax = creatureDifficulty.GoldMin;
|
||||
}
|
||||
|
||||
template.difficultyStorage[difficulty] = creatureDifficulty;
|
||||
|
||||
++count;
|
||||
} while (result.NextRow());
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template scaling data in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template difficulty data in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||
}
|
||||
public void CheckCreatureTemplate(CreatureTemplate cInfo)
|
||||
{
|
||||
if (cInfo == null)
|
||||
return;
|
||||
|
||||
bool ok = true; // bool to allow continue outside this loop
|
||||
for (uint diff = 0; diff < SharedConst.MaxCreatureDifficulties && ok; ++diff)
|
||||
{
|
||||
if (cInfo.DifficultyEntry[diff] == 0)
|
||||
continue;
|
||||
ok = false; // will be set to true at the end of this loop again
|
||||
|
||||
CreatureTemplate difficultyInfo = GetCreatureTemplate(cInfo.DifficultyEntry[diff]);
|
||||
if (difficultyInfo == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has `difficulty_entry_{1}`={2} but creature entry {3} does not exist.",
|
||||
cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff], cInfo.DifficultyEntry[diff]);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok2 = true;
|
||||
for (uint diff2 = 0; diff2 < SharedConst.MaxCreatureDifficulties && ok2; ++diff2)
|
||||
{
|
||||
ok2 = false;
|
||||
if (difficultyEntries[diff2].Contains(cInfo.Entry))
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) is listed as `difficulty_entry_{1}` of another creature, but itself lists {2} in `difficulty_entry_{3}`.",
|
||||
cInfo.Entry, diff2 + 1, cInfo.DifficultyEntry[diff], diff + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (difficultyEntries[diff2].Contains(cInfo.DifficultyEntry[diff]))
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) already listed as `difficulty_entry_{1}` for another entry.", cInfo.DifficultyEntry[diff], diff2 + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hasDifficultyEntries[diff2].Contains(cInfo.DifficultyEntry[diff]))
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has `difficulty_entry_{1}`={2} but creature entry {3} has itself a value in `difficulty_entry_{4}`.",
|
||||
cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff], cInfo.DifficultyEntry[diff], diff2 + 1);
|
||||
continue;
|
||||
}
|
||||
ok2 = true;
|
||||
}
|
||||
if (!ok2)
|
||||
continue;
|
||||
|
||||
if (cInfo.HealthScalingExpansion > difficultyInfo.HealthScalingExpansion)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Id: {0}, Expansion {1}) has different `HealthScalingExpansion` in difficulty {2} mode (Id: {3}, Expansion: {4}).",
|
||||
cInfo.Entry, cInfo.HealthScalingExpansion, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.HealthScalingExpansion);
|
||||
}
|
||||
|
||||
if (cInfo.Faction != difficultyInfo.Faction)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, faction: {1}) has different `faction` in difficulty {2} mode (Entry: {3}, faction: {4}).",
|
||||
cInfo.Entry, cInfo.Faction, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.Faction);
|
||||
}
|
||||
|
||||
if (cInfo.UnitClass != difficultyInfo.UnitClass)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, class: {1}) has different `unit_class` in difficulty {2} mode (Entry: {3}, class: {4}).",
|
||||
cInfo.Entry, cInfo.UnitClass, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.UnitClass);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cInfo.Npcflag != difficultyInfo.Npcflag)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has different `npcflag` in difficulty {1} mode (Entry: {2}).", cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff]);
|
||||
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `npcflag`=`npcflag`^{0} WHERE `entry`={1};", cInfo.Npcflag ^ difficultyInfo.Npcflag, cInfo.DifficultyEntry[diff]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cInfo.DmgSchool != difficultyInfo.DmgSchool)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, `dmgschool`: {1}) has different `dmgschool` in difficulty {2} mode (Entry: {3}, `dmgschool`: {4}).",
|
||||
cInfo.Entry, cInfo.DmgSchool, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.DmgSchool);
|
||||
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `dmgschool`={0} WHERE `entry`={1};", cInfo.DmgSchool, cInfo.DifficultyEntry[diff]);
|
||||
}
|
||||
|
||||
if (cInfo.UnitFlags2 != difficultyInfo.UnitFlags2)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, `unit_flags2`: {1}) has different `unit_flags2` in difficulty {2} mode (Entry: {3}, `unit_flags2`: {4}).",
|
||||
cInfo.Entry, cInfo.UnitFlags2, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.UnitFlags2);
|
||||
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `unit_flags2`=`unit_flags2`^{0} WHERE `entry`={1};", cInfo.UnitFlags2 ^ difficultyInfo.UnitFlags2, cInfo.DifficultyEntry[diff]);
|
||||
}
|
||||
|
||||
if (cInfo.Family != difficultyInfo.Family)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, family: {1}) has different `family` in difficulty {2} mode (Entry: {3}, family: {4}).",
|
||||
cInfo.Entry, cInfo.Family, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.Family);
|
||||
}
|
||||
|
||||
if (cInfo.TrainerClass != difficultyInfo.TrainerClass)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has different `trainer_class` in difficulty {1} mode (Entry: {2}).", cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cInfo.CreatureType != difficultyInfo.CreatureType)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, type: {1}) has different `type` in difficulty {2} mode (Entry: {3}, type: {4}).",
|
||||
cInfo.Entry, cInfo.CreatureType, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.CreatureType);
|
||||
}
|
||||
|
||||
if (cInfo.VehicleId == 0 && difficultyInfo.VehicleId != 0)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Non-vehicle Creature (Entry: {0}, VehicleId: {1}) has `VehicleId` set in difficulty {2} mode (Entry: {3}, VehicleId: {4}).",
|
||||
cInfo.Entry, cInfo.VehicleId, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.VehicleId);
|
||||
}
|
||||
|
||||
if (cInfo.RegenHealth != difficultyInfo.RegenHealth)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, RegenHealth: {1}) has different `RegenHealth` in difficulty {2} mode (Entry: {3}, RegenHealth: {4}).",
|
||||
cInfo.Entry, cInfo.RegenHealth, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.RegenHealth);
|
||||
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `RegenHealth`={0} WHERE `entry`={1};", cInfo.RegenHealth, cInfo.DifficultyEntry[diff]);
|
||||
}
|
||||
|
||||
ulong differenceMask = cInfo.MechanicImmuneMask & (~difficultyInfo.MechanicImmuneMask);
|
||||
if (differenceMask != 0)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, mechanic_immune_mask: {1}) has weaker immunities in difficulty {2} mode (Entry: {3}, mechanic_immune_mask: {4}).",
|
||||
cInfo.Entry, cInfo.MechanicImmuneMask, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.MechanicImmuneMask);
|
||||
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `mechanic_immune_mask`=`mechanic_immune_mask`|{0} WHERE `entry`={1};", differenceMask, cInfo.DifficultyEntry[diff]);
|
||||
}
|
||||
|
||||
differenceMask = (uint)((cInfo.FlagsExtra ^ difficultyInfo.FlagsExtra) & (~CreatureFlagsExtra.InstanceBind));
|
||||
if (differenceMask != 0)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, flags_extra: {1}) has different `flags_extra` in difficulty {2} mode (Entry: {3}, flags_extra: {4}).",
|
||||
cInfo.Entry, cInfo.FlagsExtra, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.FlagsExtra);
|
||||
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `flags_extra`=`flags_extra`^{0} WHERE `entry`={1};", differenceMask, cInfo.DifficultyEntry[diff]);
|
||||
}
|
||||
|
||||
if (difficultyInfo.AIName.IsEmpty())
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) lists difficulty {1} mode entry {2} with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.",
|
||||
cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (difficultyInfo.ScriptID != 0)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) lists difficulty {1} mode entry {2} with `ScriptName` filled in. `ScriptName` of difficulty 0 mode creature is always used instead.",
|
||||
cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff]);
|
||||
continue;
|
||||
}
|
||||
|
||||
hasDifficultyEntries[diff].Add(cInfo.Entry);
|
||||
difficultyEntries[diff].Add(cInfo.DifficultyEntry[diff]);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (cInfo.MinGold > cInfo.MaxGold)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"Creature (Entry: {cInfo.Entry}) has `mingold` {cInfo.MinGold} which is greater than `maxgold` {cInfo.MaxGold}, setting `maxgold` to {cInfo.MinGold}.");
|
||||
cInfo.MaxGold = cInfo.MinGold;
|
||||
}
|
||||
|
||||
if (!CliDB.FactionTemplateStorage.ContainsKey(cInfo.Faction))
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has non-existing faction template ({1}). This can lead to crashes, set to faction 35", cInfo.Entry, cInfo.Faction);
|
||||
@@ -3009,12 +2844,6 @@ namespace Game
|
||||
cInfo.MovementType = (uint)MovementGeneratorType.Idle;
|
||||
}
|
||||
|
||||
if (cInfo.HealthScalingExpansion < (int)Expansion.LevelCurrent || cInfo.HealthScalingExpansion >= (int)Expansion.Max)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Table `creature_template` lists creature (Id: {0}) with invalid `HealthScalingExpansion` {1}. Ignored and set to 0.", cInfo.Entry, cInfo.HealthScalingExpansion);
|
||||
cInfo.HealthScalingExpansion = 0;
|
||||
}
|
||||
|
||||
if (cInfo.RequiredExpansion > (int)Expansion.Max)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Table `creature_template` lists creature (Entry: {0}) with `RequiredExpansion` {1}. Ignored and set to 0.", cInfo.Entry, cInfo.RequiredExpansion);
|
||||
@@ -3055,8 +2884,6 @@ namespace Game
|
||||
cInfo.DynamicFlags = 0;
|
||||
}
|
||||
|
||||
cInfo.ModDamage *= Creature._GetDamageMod(cInfo.Rank);
|
||||
|
||||
if (!cInfo.GossipMenuIds.Empty() && !cInfo.Npcflag.HasAnyFlag((uint)NPCFlags.Gossip))
|
||||
Log.outInfo(LogFilter.Sql, $"Creature (Entry: {cInfo.Entry}) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.");
|
||||
else if (cInfo.GossipMenuIds.Empty() && cInfo.Npcflag.HasAnyFlag((uint)NPCFlags.Gossip))
|
||||
@@ -3694,18 +3521,6 @@ namespace Game
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
for (uint diff = 0; diff < SharedConst.MaxCreatureDifficulties && ok; ++diff)
|
||||
{
|
||||
if (difficultyEntries[diff].Contains(data.Id))
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that listed as difficulty {1} template (entry: {2}) in `creaturetemplate`, skipped.", guid, diff + 1, data.Id);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
continue;
|
||||
|
||||
// -1 random, 0 no equipment,
|
||||
if (data.equipmentId != 0)
|
||||
{
|
||||
@@ -3934,39 +3749,43 @@ namespace Game
|
||||
RemoveSpawnDataFromGrid(data);
|
||||
}
|
||||
|
||||
public List<uint> GetCreatureQuestItemList(uint id)
|
||||
{
|
||||
return creatureQuestItemStorage.LookupByKey(id);
|
||||
}
|
||||
public CreatureAddon GetCreatureAddon(ulong lowguid)
|
||||
{
|
||||
return creatureAddonStorage.LookupByKey(lowguid);
|
||||
}
|
||||
|
||||
public CreatureTemplate GetCreatureTemplate(uint entry)
|
||||
{
|
||||
return creatureTemplateStorage.LookupByKey(entry);
|
||||
}
|
||||
|
||||
public CreatureAddon GetCreatureTemplateAddon(uint entry)
|
||||
{
|
||||
return creatureTemplateAddonStorage.LookupByKey(entry);
|
||||
}
|
||||
|
||||
public uint GetCreatureDefaultTrainer(uint creatureId)
|
||||
{
|
||||
return GetCreatureTrainerForGossipOption(creatureId, 0, 0);
|
||||
}
|
||||
|
||||
public uint GetCreatureTrainerForGossipOption(uint creatureId, uint gossipMenuId, uint gossipOptionIndex)
|
||||
{
|
||||
return _creatureDefaultTrainers.LookupByKey((creatureId, gossipMenuId, gossipOptionIndex));
|
||||
}
|
||||
|
||||
public Dictionary<uint, CreatureTemplate> GetCreatureTemplates()
|
||||
{
|
||||
return creatureTemplateStorage;
|
||||
}
|
||||
|
||||
public Dictionary<ulong, CreatureData> GetAllCreatureData() { return creatureDataStorage; }
|
||||
|
||||
public CreatureData GetCreatureData(ulong spawnId)
|
||||
{
|
||||
return creatureDataStorage.LookupByKey(spawnId);
|
||||
}
|
||||
|
||||
public ObjectGuid GetLinkedRespawnGuid(ObjectGuid spawnId)
|
||||
{
|
||||
var retGuid = linkedRespawnStorage.LookupByKey(spawnId);
|
||||
@@ -3974,6 +3793,7 @@ namespace Game
|
||||
return ObjectGuid.Empty;
|
||||
return retGuid;
|
||||
}
|
||||
|
||||
public bool SetCreatureLinkedRespawn(ulong guidLow, ulong linkedGuidLow)
|
||||
{
|
||||
if (guidLow == 0)
|
||||
@@ -4024,12 +3844,14 @@ namespace Game
|
||||
DB.World.Execute(stmt);
|
||||
return true;
|
||||
}
|
||||
|
||||
public CreatureData NewOrExistCreatureData(ulong spawnId)
|
||||
{
|
||||
if (!creatureDataStorage.ContainsKey(spawnId))
|
||||
creatureDataStorage[spawnId] = new CreatureData();
|
||||
return creatureDataStorage[spawnId];
|
||||
}
|
||||
|
||||
public void DeleteCreatureData(ulong spawnId)
|
||||
{
|
||||
CreatureData data = GetCreatureData(spawnId);
|
||||
@@ -4041,6 +3863,7 @@ namespace Game
|
||||
|
||||
creatureDataStorage.Remove(spawnId);
|
||||
}
|
||||
|
||||
public CreatureBaseStats GetCreatureBaseStats(uint level, uint unitClass)
|
||||
{
|
||||
var stats = creatureBaseStatsStorage.LookupByKey(MathFunctions.MakePair16(level, unitClass));
|
||||
@@ -4049,6 +3872,7 @@ namespace Game
|
||||
|
||||
return new DefaultCreatureBaseStats();
|
||||
}
|
||||
|
||||
public CreatureModelInfo GetCreatureModelRandomGender(ref CreatureModel model, CreatureTemplate creatureTemplate)
|
||||
{
|
||||
CreatureModelInfo modelInfo = GetCreatureModelInfo(model.CreatureDisplayID);
|
||||
@@ -4081,14 +3905,17 @@ namespace Game
|
||||
|
||||
return modelInfo;
|
||||
}
|
||||
|
||||
public CreatureModelInfo GetCreatureModelInfo(uint modelId)
|
||||
{
|
||||
return creatureModelStorage.LookupByKey(modelId);
|
||||
}
|
||||
|
||||
public CreatureSummonedData GetCreatureSummonedData(uint entryId)
|
||||
{
|
||||
return creatureSummonedDataStorage.LookupByKey(entryId);
|
||||
}
|
||||
|
||||
public NpcText GetNpcText(uint textId)
|
||||
{
|
||||
return npcTextStorage.LookupByKey(textId);
|
||||
@@ -4289,6 +4116,7 @@ namespace Game
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} game object templates in {1} ms", _gameObjectTemplateStorage.Count, Time.GetMSTimeDiffToNow(time));
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadGameObjectTemplateAddons()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -4372,6 +4200,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} game object template addons in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
public void LoadGameObjectOverrides()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -4409,6 +4238,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} gameobject faction and flags overrides in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||
}
|
||||
|
||||
public void LoadGameObjects()
|
||||
{
|
||||
var time = Time.GetMSTime();
|
||||
@@ -4639,6 +4469,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} gameobjects in {1} ms", count, Time.GetMSTimeDiffToNow(time));
|
||||
}
|
||||
|
||||
public void LoadGameObjectAddons()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -4710,6 +4541,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} gameobject addons in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
|
||||
}
|
||||
|
||||
public void LoadGameObjectQuestItems()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -4749,6 +4581,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} gameobject quest items in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
public void LoadGameObjectForQuests()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -4814,10 +4647,12 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} GameObjects for quests in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
public void AddGameObjectToGrid(GameObjectData data)
|
||||
{
|
||||
AddSpawnDataToGrid(data);
|
||||
}
|
||||
|
||||
public void RemoveGameObjectFromGrid(GameObjectData data)
|
||||
{
|
||||
RemoveSpawnDataFromGrid(data);
|
||||
@@ -4827,16 +4662,21 @@ namespace Game
|
||||
{
|
||||
return _gameObjectAddonStorage.LookupByKey(lowguid);
|
||||
}
|
||||
|
||||
public List<uint> GetGameObjectQuestItemList(uint id)
|
||||
{
|
||||
return _gameObjectQuestItemStorage.LookupByKey(id);
|
||||
}
|
||||
|
||||
MultiMap<uint, uint> GetGameObjectQuestItemMap() { return _gameObjectQuestItemStorage; }
|
||||
|
||||
public Dictionary<ulong, GameObjectData> GetAllGameObjectData() { return gameObjectDataStorage; }
|
||||
|
||||
public GameObjectData GetGameObjectData(ulong spawnId)
|
||||
{
|
||||
return gameObjectDataStorage.LookupByKey(spawnId);
|
||||
}
|
||||
|
||||
public void DeleteGameObjectData(ulong spawnId)
|
||||
{
|
||||
GameObjectData data = GetGameObjectData(spawnId);
|
||||
@@ -4848,6 +4688,7 @@ namespace Game
|
||||
|
||||
gameObjectDataStorage.Remove(spawnId);
|
||||
}
|
||||
|
||||
public GameObjectData NewOrExistGameObjectData(ulong spawnId)
|
||||
{
|
||||
if (!gameObjectDataStorage.ContainsKey(spawnId))
|
||||
@@ -4855,26 +4696,32 @@ namespace Game
|
||||
|
||||
return gameObjectDataStorage[spawnId];
|
||||
}
|
||||
|
||||
public GameObjectTemplate GetGameObjectTemplate(uint entry)
|
||||
{
|
||||
return _gameObjectTemplateStorage.LookupByKey(entry);
|
||||
}
|
||||
|
||||
public GameObjectTemplateAddon GetGameObjectTemplateAddon(uint entry)
|
||||
{
|
||||
return _gameObjectTemplateAddonStorage.LookupByKey(entry);
|
||||
}
|
||||
|
||||
public GameObjectOverride GetGameObjectOverride(ulong spawnId)
|
||||
{
|
||||
return _gameObjectOverrideStorage.LookupByKey(spawnId);
|
||||
}
|
||||
|
||||
public Dictionary<uint, GameObjectTemplate> GetGameObjectTemplates()
|
||||
{
|
||||
return _gameObjectTemplateStorage;
|
||||
}
|
||||
|
||||
public bool IsGameObjectForQuests(uint entry)
|
||||
{
|
||||
return _gameObjectForQuestStorage.Contains(entry);
|
||||
}
|
||||
|
||||
void CheckGOLockId(GameObjectTemplate goInfo, uint dataN, uint N)
|
||||
{
|
||||
if (CliDB.LockStorage.ContainsKey(dataN))
|
||||
@@ -4882,6 +4729,7 @@ namespace Game
|
||||
|
||||
Log.outError(LogFilter.Sql, "Gameobject (Entry: {0} GoType: {1}) have data{2}={3} but lock (Id: {4}) not found.", goInfo.entry, goInfo.type, N, goInfo.Door.open, goInfo.Door.open);
|
||||
}
|
||||
|
||||
void CheckGOLinkedTrapId(GameObjectTemplate goInfo, uint dataN, uint N)
|
||||
{
|
||||
GameObjectTemplate trapInfo = GetGameObjectTemplate(dataN);
|
||||
@@ -4891,6 +4739,7 @@ namespace Game
|
||||
Log.outError(LogFilter.Sql, "Gameobject (Entry: {0} GoType: {1}) have data{2}={3} but GO (Entry {4}) have not GAMEOBJECT_TYPE_TRAP type.", goInfo.entry, goInfo.type, N, dataN, dataN);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckGOSpellId(GameObjectTemplate goInfo, uint dataN, uint N)
|
||||
{
|
||||
if (Global.SpellMgr.HasSpellInfo(dataN, Difficulty.None))
|
||||
@@ -4898,6 +4747,7 @@ namespace Game
|
||||
|
||||
Log.outError(LogFilter.Sql, "Gameobject (Entry: {0} GoType: {1}) have data{2}={3} but Spell (Entry {4}) not exist.", goInfo.entry, goInfo.type, N, dataN, dataN);
|
||||
}
|
||||
|
||||
void CheckAndFixGOChairHeightId(GameObjectTemplate goInfo, ref uint dataN, uint N)
|
||||
{
|
||||
if (dataN <= (UnitStandStateType.SitHighChair - UnitStandStateType.SitLowChair))
|
||||
@@ -4908,6 +4758,7 @@ namespace Game
|
||||
// prevent client and server unexpected work
|
||||
dataN = 0;
|
||||
}
|
||||
|
||||
void CheckGONoDamageImmuneId(GameObjectTemplate goTemplate, uint dataN, uint N)
|
||||
{
|
||||
// 0/1 correct values
|
||||
@@ -4916,6 +4767,7 @@ namespace Game
|
||||
|
||||
Log.outError(LogFilter.Sql, "Gameobject (Entry: {0} GoType: {1}) have data{2}={3} but expected boolean (0/1) noDamageImmune field value.", goTemplate.entry, goTemplate.type, N, dataN);
|
||||
}
|
||||
|
||||
void CheckGOConsumable(GameObjectTemplate goInfo, uint dataN, uint N)
|
||||
{
|
||||
// 0/1 correct values
|
||||
@@ -5668,16 +5520,6 @@ namespace Game
|
||||
continue;
|
||||
}
|
||||
creatureInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss;
|
||||
for (byte diff = 0; diff < SharedConst.MaxCreatureDifficulties; ++diff)
|
||||
{
|
||||
uint diffEntry = creatureInfo.DifficultyEntry[diff];
|
||||
if (diffEntry != 0)
|
||||
{
|
||||
CreatureTemplate diffInfo = GetCreatureTemplate(diffEntry);
|
||||
if (diffInfo != null)
|
||||
diffInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EncounterCreditType.CastSpell:
|
||||
@@ -8087,6 +7929,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quests definitions in {1} ms", _questTemplates.Count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
public void LoadQuestStartersAndEnders()
|
||||
{
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading GO Start Quest Data...");
|
||||
@@ -8098,6 +7941,7 @@ namespace Game
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature End Quest Data...");
|
||||
LoadCreatureQuestEnders();
|
||||
}
|
||||
|
||||
public void LoadGameobjectQuestStarters()
|
||||
{
|
||||
LoadQuestRelationsHelper(_goQuestRelations, null, "gameobject_queststarter");
|
||||
@@ -8111,6 +7955,7 @@ namespace Game
|
||||
Log.outError(LogFilter.Sql, "Table `gameobject_queststarter` have data gameobject entry ({0}) for quest {1}, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadGameobjectQuestEnders()
|
||||
{
|
||||
LoadQuestRelationsHelper(_goQuestInvolvedRelations, _goQuestInvolvedRelationsReverse, "gameobject_questender");
|
||||
@@ -8124,6 +7969,7 @@ namespace Game
|
||||
Log.outError(LogFilter.Sql, "Table `gameobject_questender` have data gameobject entry ({0}) for quest {1}, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadCreatureQuestStarters()
|
||||
{
|
||||
LoadQuestRelationsHelper(_creatureQuestRelations, null, "creature_queststarter");
|
||||
@@ -8137,6 +7983,7 @@ namespace Game
|
||||
Log.outError(LogFilter.Sql, "Table `creature_queststarter` has creature entry ({0}) for quest {1}, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadCreatureQuestEnders()
|
||||
{
|
||||
LoadQuestRelationsHelper(_creatureQuestInvolvedRelations, _creatureQuestInvolvedRelationsReverse, "creature_questender");
|
||||
@@ -8150,6 +7997,7 @@ namespace Game
|
||||
Log.outError(LogFilter.Sql, "Table `creature_questender` has creature entry ({0}) for quest {1}, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadQuestRelationsHelper(MultiMap<uint, uint> map, MultiMap<uint, uint> reverseMap, string table)
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -8186,6 +8034,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest relations from {1} in {2} ms", count, table, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
public void LoadQuestPOI()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -8264,6 +8113,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest POI definitions in {1} ms", _questPOIStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
public void LoadQuestAreaTriggers()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -8324,6 +8174,7 @@ namespace Game
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest trigger points in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
public void LoadQuestGreetings()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
@@ -8381,34 +8232,48 @@ namespace Game
|
||||
{
|
||||
return _questTemplates.LookupByKey(questId);
|
||||
}
|
||||
|
||||
public Dictionary<uint, Quest> GetQuestTemplates()
|
||||
{
|
||||
return _questTemplates;
|
||||
}
|
||||
|
||||
public List<Quest> GetQuestTemplatesAutoPush()
|
||||
{
|
||||
return _questTemplatesAutoPush;
|
||||
}
|
||||
|
||||
public MultiMap<uint, uint> GetGOQuestRelationMapHACK() { return _goQuestRelations; }
|
||||
|
||||
public QuestRelationResult GetGOQuestRelations(uint entry) { return GetQuestRelationsFrom(_goQuestRelations, entry, true); }
|
||||
|
||||
public QuestRelationResult GetGOQuestInvolvedRelations(uint entry) { return GetQuestRelationsFrom(_goQuestInvolvedRelations, entry, false); }
|
||||
|
||||
public List<uint> GetGOQuestInvolvedRelationReverseBounds(uint questId) { return _goQuestInvolvedRelationsReverse.LookupByKey(questId); }
|
||||
|
||||
public MultiMap<uint, uint> GetCreatureQuestRelationMapHACK() { return _creatureQuestRelations; }
|
||||
|
||||
public QuestRelationResult GetCreatureQuestRelations(uint entry) { return GetQuestRelationsFrom(_creatureQuestRelations, entry, true); }
|
||||
|
||||
public QuestRelationResult GetCreatureQuestInvolvedRelations(uint entry) { return GetQuestRelationsFrom(_creatureQuestInvolvedRelations, entry, false); }
|
||||
|
||||
public List<uint> GetCreatureQuestInvolvedRelationReverseBounds(uint questId) { return _creatureQuestInvolvedRelationsReverse.LookupByKey(questId); }
|
||||
|
||||
public QuestPOIData GetQuestPOIData(uint questId)
|
||||
{
|
||||
return _questPOIStorage.LookupByKey(questId);
|
||||
}
|
||||
|
||||
public QuestObjective GetQuestObjective(uint questObjectiveId)
|
||||
{
|
||||
return _questObjectives.LookupByKey(questObjectiveId);
|
||||
}
|
||||
|
||||
public List<uint> GetQuestsForAreaTrigger(uint triggerId)
|
||||
{
|
||||
return _questAreaTriggerStorage.LookupByKey(triggerId);
|
||||
}
|
||||
|
||||
public QuestGreeting GetQuestGreeting(TypeId type, uint id)
|
||||
{
|
||||
byte typeIndex;
|
||||
@@ -8421,6 +8286,7 @@ namespace Game
|
||||
|
||||
return _questGreetingStorage[typeIndex].LookupByKey(id);
|
||||
}
|
||||
|
||||
public QuestGreetingLocale GetQuestGreetingLocale(TypeId type, uint id)
|
||||
{
|
||||
byte typeIndex;
|
||||
@@ -8433,12 +8299,28 @@ namespace Game
|
||||
|
||||
return _questGreetingLocaleStorage[typeIndex].LookupByKey(id);
|
||||
}
|
||||
|
||||
public List<uint> GetExclusiveQuestGroupBounds(int exclusiveGroupId)
|
||||
{
|
||||
return _exclusiveQuestGroups.LookupByKey(exclusiveGroupId);
|
||||
}
|
||||
|
||||
QuestRelationResult GetQuestRelationsFrom(MultiMap<uint, uint> map, uint key, bool onlyActive) { return new QuestRelationResult(map.LookupByKey(key), onlyActive); }
|
||||
|
||||
public List<uint> GetCreatureQuestItemList(uint creatureEntry, Difficulty difficulty)
|
||||
{
|
||||
var itr = creatureQuestItemStorage.LookupByKey((creatureEntry, difficulty));
|
||||
if (itr != null)
|
||||
return itr;
|
||||
|
||||
// If there is no data for the difficulty, try to get data for the fallback difficulty
|
||||
var difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficulty);
|
||||
if (difficultyEntry != null)
|
||||
return GetCreatureQuestItemList(creatureEntry, (Difficulty)difficultyEntry.FallbackDifficultyID);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//Spells /Skills / Phases
|
||||
public void LoadPhases()
|
||||
{
|
||||
@@ -11039,7 +10921,7 @@ namespace Game
|
||||
Dictionary<uint, CreatureSummonedData> creatureSummonedDataStorage = new();
|
||||
Dictionary<ulong, CreatureData> creatureDataStorage = new();
|
||||
Dictionary<ulong, CreatureAddon> creatureAddonStorage = new();
|
||||
MultiMap<uint, uint> creatureQuestItemStorage = new();
|
||||
MultiMap<(uint, Difficulty), uint> creatureQuestItemStorage = new();
|
||||
Dictionary<uint, CreatureAddon> creatureTemplateAddonStorage = new();
|
||||
MultiMap<uint, float> _creatureTemplateSparringStorage = new();
|
||||
Dictionary<ulong, CreatureMovementData> creatureMovementOverrides = new();
|
||||
@@ -11049,8 +10931,6 @@ namespace Game
|
||||
Dictionary<uint, VendorItemData> cacheVendorItemStorage = new();
|
||||
Dictionary<uint, Trainer> trainers = new();
|
||||
Dictionary<(uint creatureId, uint gossipMenuId, uint gossipOptionIndex), uint> _creatureDefaultTrainers = new();
|
||||
List<uint>[] difficultyEntries = new List<uint>[SharedConst.MaxCreatureDifficulties]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate
|
||||
List<uint>[] hasDifficultyEntries = new List<uint>[SharedConst.MaxCreatureDifficulties]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate
|
||||
Dictionary<uint, NpcText> npcTextStorage = new();
|
||||
|
||||
//GameObject
|
||||
|
||||
@@ -458,7 +458,7 @@ namespace Game
|
||||
if (dstPet != null)
|
||||
{
|
||||
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(dstPet.CreatureId);
|
||||
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets()))
|
||||
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets(), creatureInfo.GetDifficulty(Difficulty.None)))
|
||||
{
|
||||
SendPetStableResult(StableResult.CantControlExotic);
|
||||
return;
|
||||
@@ -484,7 +484,7 @@ namespace Game
|
||||
}
|
||||
|
||||
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(srcPet.CreatureId);
|
||||
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets()))
|
||||
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets(), creatureInfo.GetDifficulty(Difficulty.None)))
|
||||
{
|
||||
SendPetStableResult(StableResult.CantControlExotic);
|
||||
return;
|
||||
|
||||
@@ -99,31 +99,16 @@ namespace Game
|
||||
CreatureTemplate ci = Global.ObjectMgr.GetCreatureTemplate(packet.CreatureID);
|
||||
if (ci != null)
|
||||
{
|
||||
if (!WorldConfig.GetBoolValue(WorldCfg.CacheDataQueries))
|
||||
ci.InitializeQueryData();
|
||||
Difficulty difficulty = _player.GetMap().GetDifficultyID();
|
||||
|
||||
QueryCreatureResponse queryCreatureResponse = ci.QueryData;
|
||||
|
||||
Locale loc = GetSessionDbLocaleIndex();
|
||||
if (loc != Locale.enUS)
|
||||
// Cache only exists for difficulty base
|
||||
if (!WorldConfig.GetBoolValue(WorldCfg.CacheDataQueries) && difficulty == Difficulty.None)
|
||||
SendPacket(ci.QueryData[(int)GetSessionDbLocaleIndex()]);
|
||||
else
|
||||
{
|
||||
CreatureLocale creatureLocale = Global.ObjectMgr.GetCreatureLocale(ci.Entry);
|
||||
if (creatureLocale != null)
|
||||
{
|
||||
string name = queryCreatureResponse.Stats.Name[0];
|
||||
string nameAlt = queryCreatureResponse.Stats.NameAlt[0];
|
||||
|
||||
ObjectManager.GetLocaleString(creatureLocale.Name, loc, ref name);
|
||||
ObjectManager.GetLocaleString(creatureLocale.NameAlt, loc, ref nameAlt);
|
||||
ObjectManager.GetLocaleString(creatureLocale.Title, loc, ref queryCreatureResponse.Stats.Title);
|
||||
ObjectManager.GetLocaleString(creatureLocale.TitleAlt, loc, ref queryCreatureResponse.Stats.TitleAlt);
|
||||
|
||||
queryCreatureResponse.Stats.Name[0] = name;
|
||||
queryCreatureResponse.Stats.NameAlt[0] = nameAlt;
|
||||
}
|
||||
var response = ci.BuildQueryData(GetSessionDbLocaleIndex(), difficulty);
|
||||
SendPacket(response);
|
||||
}
|
||||
|
||||
SendPacket(queryCreatureResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -97,16 +97,18 @@ namespace Game.Loots
|
||||
uint count = Creature.LoadAndCollectLootIds(out lootIdSet);
|
||||
|
||||
// Remove real entries and check loot existence
|
||||
var ctc = Global.ObjectMgr.GetCreatureTemplates();
|
||||
foreach (var pair in ctc)
|
||||
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
||||
foreach (var creatureTemplate in templates.Values)
|
||||
{
|
||||
uint lootid = pair.Value.LootId;
|
||||
if (lootid != 0)
|
||||
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
||||
{
|
||||
if (!lootIdSet.Contains(lootid))
|
||||
Creature.ReportNonExistingId(lootid, pair.Value.Entry);
|
||||
else
|
||||
lootIdSetUsed.Add(lootid);
|
||||
if (creatureDifficulty.LootID != 0)
|
||||
{
|
||||
if (!lootIdSet.Contains(creatureDifficulty.LootID))
|
||||
Creature.ReportNonExistingId(creatureDifficulty.LootID, creatureTemplate.Entry);
|
||||
else
|
||||
lootIdSetUsed.Add(creatureDifficulty.LootID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,16 +291,18 @@ namespace Game.Loots
|
||||
uint count = Pickpocketing.LoadAndCollectLootIds(out lootIdSet);
|
||||
|
||||
// Remove real entries and check loot existence
|
||||
var ctc = Global.ObjectMgr.GetCreatureTemplates();
|
||||
foreach (var pair in ctc)
|
||||
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
||||
foreach (var creatureTemplate in templates.Values)
|
||||
{
|
||||
uint lootid = pair.Value.PickPocketId;
|
||||
if (lootid != 0)
|
||||
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
||||
{
|
||||
if (!lootIdSet.Contains(lootid))
|
||||
Pickpocketing.ReportNonExistingId(lootid, pair.Value.Entry);
|
||||
else
|
||||
lootIdSetUsed.Add(lootid);
|
||||
if (creatureDifficulty.PickPocketLootID != 0)
|
||||
{
|
||||
if (!lootIdSet.Contains(creatureDifficulty.PickPocketLootID))
|
||||
Pickpocketing.ReportNonExistingId(creatureDifficulty.PickPocketLootID, creatureTemplate.Entry);
|
||||
else
|
||||
lootIdSetUsed.Add(creatureDifficulty.PickPocketLootID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,16 +381,18 @@ namespace Game.Loots
|
||||
uint count = Skinning.LoadAndCollectLootIds(out lootIdSet);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
var ctc = Global.ObjectMgr.GetCreatureTemplates();
|
||||
foreach (var pair in ctc)
|
||||
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
||||
foreach (var creatureTemplate in templates.Values)
|
||||
{
|
||||
uint lootid = pair.Value.SkinLootId;
|
||||
if (lootid != 0)
|
||||
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
||||
{
|
||||
if (!lootIdSet.Contains(lootid))
|
||||
Skinning.ReportNonExistingId(lootid, pair.Value.Entry);
|
||||
else
|
||||
lootIdSetUsed.Add(lootid);
|
||||
if (creatureDifficulty.SkinLootID != 0)
|
||||
{
|
||||
if (!lootIdSet.Contains(creatureDifficulty.SkinLootID))
|
||||
Skinning.ReportNonExistingId(creatureDifficulty.SkinLootID, creatureTemplate.Entry);
|
||||
else
|
||||
lootIdSetUsed.Add(creatureDifficulty.SkinLootID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace Game
|
||||
if (gain != 0 && creature)
|
||||
{
|
||||
// Players get only 10% xp for killing creatures of lower expansion levels than himself
|
||||
if ((creature.GetCreatureTemplate().GetHealthScalingExpansion() < (int)GetExpansionForLevel(player.GetLevel())))
|
||||
if ((creature.GetCreatureDifficulty().GetHealthScalingExpansion() < (int)GetExpansionForLevel(player.GetLevel())))
|
||||
gain = (uint)Math.Round(gain / 10.0f);
|
||||
|
||||
if (creature.IsElite())
|
||||
|
||||
@@ -1245,32 +1245,32 @@ namespace Game.Networking.Packets
|
||||
bool GenerateDataCreatureToPlayer(Creature attacker, Player target)
|
||||
{
|
||||
CreatureTemplate creatureTemplate = attacker.GetCreatureTemplate();
|
||||
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(attacker.GetMap().GetDifficultyID());
|
||||
CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(attacker.GetMap().GetDifficultyID());
|
||||
|
||||
TuningType = ContentTuningType.CreatureToPlayerDamage;
|
||||
PlayerLevelDelta = (short)target.m_activePlayerData.ScalingPlayerLevelDelta;
|
||||
PlayerItemLevel = (ushort)target.GetAverageItemLevel();
|
||||
ScalingHealthItemLevelCurveID = (ushort)target.m_unitData.ScalingHealthItemLevelCurveID;
|
||||
TargetLevel = (byte)target.GetLevel();
|
||||
Expansion = (byte)creatureTemplate.HealthScalingExpansion;
|
||||
Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
|
||||
TargetScalingLevelDelta = (sbyte)attacker.m_unitData.ScalingLevelDelta;
|
||||
TargetContentTuningID = creatureScaling.ContentTuningID;
|
||||
TargetContentTuningID = creatureDifficulty.ContentTuningID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GenerateDataPlayerToCreature(Player attacker, Creature target)
|
||||
{
|
||||
CreatureTemplate creatureTemplate = target.GetCreatureTemplate();
|
||||
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(target.GetMap().GetDifficultyID());
|
||||
CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(target.GetMap().GetDifficultyID());
|
||||
|
||||
TuningType = ContentTuningType.PlayerToCreatureDamage;
|
||||
PlayerLevelDelta = (short)attacker.m_activePlayerData.ScalingPlayerLevelDelta;
|
||||
PlayerItemLevel = (ushort)attacker.GetAverageItemLevel();
|
||||
ScalingHealthItemLevelCurveID = (ushort)target.m_unitData.ScalingHealthItemLevelCurveID;
|
||||
TargetLevel = (byte)target.GetLevel();
|
||||
Expansion = (byte)creatureTemplate.HealthScalingExpansion;
|
||||
Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
|
||||
TargetScalingLevelDelta = (sbyte)target.m_unitData.ScalingLevelDelta;
|
||||
TargetContentTuningID = creatureScaling.ContentTuningID;
|
||||
TargetContentTuningID = creatureDifficulty.ContentTuningID;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1278,15 +1278,15 @@ namespace Game.Networking.Packets
|
||||
{
|
||||
Creature accessor = target.HasScalableLevels() ? target : attacker;
|
||||
CreatureTemplate creatureTemplate = accessor.GetCreatureTemplate();
|
||||
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(accessor.GetMap().GetDifficultyID());
|
||||
CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(accessor.GetMap().GetDifficultyID());
|
||||
|
||||
TuningType = ContentTuningType.CreatureToCreatureDamage;
|
||||
PlayerLevelDelta = 0;
|
||||
PlayerItemLevel = 0;
|
||||
TargetLevel = (byte)target.GetLevel();
|
||||
Expansion = (byte)creatureTemplate.HealthScalingExpansion;
|
||||
Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
|
||||
TargetScalingLevelDelta = (sbyte)accessor.m_unitData.ScalingLevelDelta;
|
||||
TargetContentTuningID = creatureScaling.ContentTuningID;
|
||||
TargetContentTuningID = creatureDifficulty.ContentTuningID;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1402,7 +1402,7 @@ namespace Game.Spells
|
||||
Creature creatureTarget = unitTarget.ToCreature();
|
||||
if (creatureTarget)
|
||||
{
|
||||
if (!creatureTarget.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CollideWithMissiles))
|
||||
if (!creatureTarget.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CollideWithMissiles))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -5405,10 +5405,11 @@ namespace Game.Spells
|
||||
if (info.Item1.Type == PetType.Hunter)
|
||||
{
|
||||
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(info.Item1.CreatureId);
|
||||
if (creatureInfo == null || !creatureInfo.IsTameable(playerCaster.CanTameExoticPets()))
|
||||
CreatureDifficulty creatureDifficulty = creatureInfo.GetDifficulty(Difficulty.None);
|
||||
if (creatureInfo == null || !creatureInfo.IsTameable(playerCaster.CanTameExoticPets(), creatureDifficulty))
|
||||
{
|
||||
// if problem in exotic pet
|
||||
if (creatureInfo != null && creatureInfo.IsTameable(true))
|
||||
if (creatureInfo != null && creatureInfo.IsTameable(true, creatureDifficulty))
|
||||
playerCaster.SendTameFailure(PetTameResult.CantControlExotic);
|
||||
else
|
||||
playerCaster.SendTameFailure(PetTameResult.NoPetAvailable);
|
||||
|
||||
@@ -1819,7 +1819,7 @@ namespace Game.Spells
|
||||
creature.StartPickPocketRefillTimer();
|
||||
|
||||
creature._loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
|
||||
uint lootid = creature.GetCreatureTemplate().PickPocketId;
|
||||
uint lootid = creature.GetCreatureDifficulty().PickPocketLootID;
|
||||
if (lootid != 0)
|
||||
creature._loot.FillLoot(lootid, LootStorage.Pickpocketing, player, true);
|
||||
|
||||
@@ -3442,13 +3442,13 @@ namespace Game.Spells
|
||||
Creature creature = unitTarget.ToCreature();
|
||||
int targetLevel = (int)creature.GetLevelForTarget(m_caster);
|
||||
|
||||
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
||||
SkillType skill = creature.GetCreatureDifficulty().GetRequiredLootSkill();
|
||||
|
||||
creature.SetUnitFlag3(UnitFlags3.AlreadySkinned);
|
||||
creature.SetDynamicFlag(UnitDynFlags.Lootable);
|
||||
Loot loot = new(creature.GetMap(), creature.GetGUID(), LootType.Skinning, null);
|
||||
creature.m_personalLoot[player.GetGUID()] = loot;
|
||||
loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, player, true);
|
||||
loot.FillLoot(creature.GetCreatureDifficulty().SkinLootID, LootStorage.Skinning, player, true);
|
||||
player.SendLoot(loot);
|
||||
|
||||
if (!Global.SpellMgr.IsPartOfSkillLine(skill, m_spellInfo.Id))
|
||||
|
||||
@@ -1032,7 +1032,7 @@ namespace Game.Spells
|
||||
if (targetCreature == null)
|
||||
return SpellCastResult.BadTargets;
|
||||
|
||||
if (!targetCreature.CanHaveLoot() || !Loots.LootStorage.Pickpocketing.HaveLootFor(targetCreature.GetCreatureTemplate().PickPocketId))
|
||||
if (!targetCreature.CanHaveLoot() || !Loots.LootStorage.Pickpocketing.HaveLootFor(targetCreature.GetCreatureDifficulty().PickPocketLootID))
|
||||
return SpellCastResult.TargetNoPockets;
|
||||
}
|
||||
|
||||
|
||||
@@ -606,12 +606,12 @@ namespace Game
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template addons...");
|
||||
Global.ObjectMgr.LoadCreatureTemplateAddons();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template difficulty...");
|
||||
Global.ObjectMgr.LoadCreatureTemplateDifficulty();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template sparring...");
|
||||
Global.ObjectMgr.LoadCreatureTemplateSparring();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template scaling...");
|
||||
Global.ObjectMgr.LoadCreatureScalingData();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Reputation Reward Rates...");
|
||||
Global.ObjectMgr.LoadReputationRewardRate();
|
||||
|
||||
|
||||
@@ -454,7 +454,7 @@ namespace Scripts.Spells.Hunter
|
||||
return SpellCastResult.Highlevel;
|
||||
|
||||
// use SMSG_PET_TAME_FAILURE?
|
||||
if (!target.GetCreatureTemplate().IsTameable(caster.CanTameExoticPets()))
|
||||
if (!target.GetCreatureTemplate().IsTameable(caster.CanTameExoticPets(), target.GetCreatureDifficulty()))
|
||||
return SpellCastResult.BadTargets;
|
||||
|
||||
PetStable petStable = caster.GetPetStable();
|
||||
|
||||
@@ -7,7 +7,6 @@ using Game.BattleGrounds;
|
||||
using Game.DataStorage;
|
||||
using Game.Entities;
|
||||
using Game.Loots;
|
||||
using Game.Networking.Packets;
|
||||
using Game.Scripting;
|
||||
using Game.Spells;
|
||||
using System;
|
||||
@@ -1322,11 +1321,12 @@ namespace Scripts.Spells.Items
|
||||
{
|
||||
Player player = GetCaster().ToPlayer();
|
||||
Creature creature = GetTarget().ToCreature();
|
||||
CreatureDifficulty creatureDifficulty = creature.GetCreatureDifficulty();
|
||||
// missing lootid has been reported on startup - just return
|
||||
if (creature.GetCreatureTemplate().SkinLootId == 0)
|
||||
if (creatureDifficulty.SkinLootID == 0)
|
||||
return;
|
||||
|
||||
player.AutoStoreLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, ItemContext.None, true);
|
||||
player.AutoStoreLoot(creatureDifficulty.SkinLootID, LootStorage.Skinning, ItemContext.None, true);
|
||||
creature.DespawnOrUnsummon();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user