diff --git a/Source/Framework/Constants/CreatureConst.cs b/Source/Framework/Constants/CreatureConst.cs index 2ec8fbe7a..827eb93da 100644 --- a/Source/Framework/Constants/CreatureConst.cs +++ b/Source/Framework/Constants/CreatureConst.cs @@ -407,6 +407,15 @@ namespace Framework.Constants Max } + public enum CreatureRandomMovementType + { + Walk, + CanRun, + AlwaysRun, + + Max + } + public enum VendorInventoryReason { None = 0, diff --git a/Source/Framework/Database/Databases/WorldDatabase.cs b/Source/Framework/Database/Databases/WorldDatabase.cs index d3e05ae1d..4625a69df 100644 --- a/Source/Framework/Database/Databases/WorldDatabase.cs +++ b/Source/Framework/Database/Databases/WorldDatabase.cs @@ -76,7 +76,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, permission, 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, gossip_menu_id, minlevel, maxlevel, 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, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName 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, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, gossip_menu_id, minlevel, maxlevel, 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, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName 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_"); diff --git a/Source/Game/Entities/Creature/CreatureData.cs b/Source/Game/Entities/Creature/CreatureData.cs index c53bd9f69..7977e41c4 100644 --- a/Source/Game/Entities/Creature/CreatureData.cs +++ b/Source/Game/Entities/Creature/CreatureData.cs @@ -353,6 +353,7 @@ namespace Game.Entities public bool Swim; public bool Rooted; public CreatureChaseMovementType Chase; + public CreatureRandomMovementType Random; public CreatureMovementData() { @@ -368,10 +369,11 @@ namespace Game.Entities public bool IsRooted() { return Rooted; } public CreatureChaseMovementType GetChase() { return Chase; } + public CreatureRandomMovementType GetRandom() { return Random; } public override string ToString() { - return $"Ground: {Ground}, Swim: {Swim}, Flight: {Flight} {(Rooted ? ", Rooted" : "")}, Chase: {Chase}"; + return $"Ground: {Ground}, Swim: {Swim}, Flight: {Flight} {(Rooted ? ", Rooted" : "")}, Chase: {Chase}, Random: {Random}"; } } diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index b3134e416..23b86fc8d 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -1832,24 +1832,27 @@ namespace Game if (!fields.IsNull(64)) creature.Movement.Chase = (CreatureChaseMovementType)fields.Read(64); - creature.HoverHeight = fields.Read(65); - creature.ModHealth = fields.Read(66); - creature.ModHealthExtra = fields.Read(67); - creature.ModMana = fields.Read(68); - creature.ModManaExtra = fields.Read(69); - creature.ModArmor = fields.Read(70); - creature.ModDamage = fields.Read(71); - creature.ModExperience = fields.Read(72); - creature.RacialLeader = fields.Read(73); - creature.MovementId = fields.Read(74); - creature.CreatureDifficultyID = fields.Read(75); - creature.WidgetSetID = fields.Read(76); - creature.WidgetSetUnitConditionID = fields.Read(77); - creature.RegenHealth = fields.Read(78); - creature.MechanicImmuneMask = fields.Read(79); - creature.SpellSchoolImmuneMask = fields.Read(80); - creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(81); - creature.ScriptID = GetScriptId(fields.Read(82)); + if (!fields.IsNull(65)) + creature.Movement.Random = (CreatureRandomMovementType)fields.Read(65); + + creature.HoverHeight = fields.Read(66); + creature.ModHealth = fields.Read(67); + creature.ModHealthExtra = fields.Read(68); + creature.ModMana = fields.Read(69); + creature.ModManaExtra = fields.Read(70); + creature.ModArmor = fields.Read(71); + creature.ModDamage = fields.Read(72); + creature.ModExperience = fields.Read(73); + creature.RacialLeader = fields.Read(74); + creature.MovementId = fields.Read(75); + creature.CreatureDifficultyID = fields.Read(76); + creature.WidgetSetID = fields.Read(77); + creature.WidgetSetUnitConditionID = fields.Read(78); + creature.RegenHealth = fields.Read(79); + creature.MechanicImmuneMask = fields.Read(80); + creature.SpellSchoolImmuneMask = fields.Read(81); + creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(82); + creature.ScriptID = GetScriptId(fields.Read(83)); creatureTemplateStorage[entry] = creature; } @@ -2252,7 +2255,7 @@ namespace Game creatureMovementOverrides.Clear(); - SQLResult result = DB.World.Query("SELECT SpawnId, Ground, Swim, Flight, Rooted, Chase from creature_movement_override"); + SQLResult result = DB.World.Query("SELECT SpawnId, Ground, Swim, Flight, Rooted, Chase, Random from creature_movement_override"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!"); @@ -2274,6 +2277,7 @@ namespace Game movement.Flight = (CreatureFlightMovementType)result.Read(3); movement.Rooted = result.Read(4); movement.Chase = (CreatureChaseMovementType)result.Read(5); + movement.Random = (CreatureRandomMovementType)result.Read(6); CheckCreatureMovement("creature_movement_override", spawnId, movement); @@ -2750,6 +2754,12 @@ namespace Game Log.outError(LogFilter.Sql, $"`{table}`.`Chase` wrong value ({creatureMovement.Chase}) for Id {id}, setting to Run."); creatureMovement.Chase = CreatureChaseMovementType.Run; } + + if (creatureMovement.Random >= CreatureRandomMovementType.Max) + { + Log.outError(LogFilter.Sql, $"`{table}`.`Random` wrong value ({creatureMovement.Random}) for Id {id}, setting to Walk."); + creatureMovement.Random = CreatureRandomMovementType.Walk; + } } public void LoadLinkedRespawn() { diff --git a/Source/Game/Movement/Generators/RandomMovement.cs b/Source/Game/Movement/Generators/RandomMovement.cs index 0f5a79c8c..fa0f82bfc 100644 --- a/Source/Game/Movement/Generators/RandomMovement.cs +++ b/Source/Game/Movement/Generators/RandomMovement.cs @@ -138,9 +138,22 @@ namespace Game.Movement owner.AddUnitState(UnitState.RoamingMove); + bool walk = true; + switch (owner.GetMovementTemplate().GetRandom()) + { + case CreatureRandomMovementType.CanRun: + walk = owner.IsWalking(); + break; + case CreatureRandomMovementType.AlwaysRun: + walk = false; + break; + default: + break; + } + MoveSplineInit init = new(owner); init.MovebyPath(_path.GetPath()); - init.SetWalk(true); + init.SetWalk(walk); int traveltime = init.Launch(); _timer.Reset(traveltime + resetTimer); diff --git a/sql/updates/world/master/2021_11_24_13_world_2019_03_03_03_world.sql b/sql/updates/world/master/2021_11_24_13_world_2019_03_03_03_world.sql new file mode 100644 index 000000000..336c6f214 --- /dev/null +++ b/sql/updates/world/master/2021_11_24_13_world_2019_03_03_03_world.sql @@ -0,0 +1,3 @@ +-- +ALTER TABLE `creature_template_movement` ADD `Random` tinyint(3) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `creature_movement_override` ADD `Random` tinyint(3) UNSIGNED NOT NULL DEFAULT '0';