diff --git a/sql/updates/world/master/2024_03_30_00_world.sql b/sql/updates/world/master/2024_03_30_00_world.sql new file mode 100644 index 0000000000000..7d6e46091e759 --- /dev/null +++ b/sql/updates/world/master/2024_03_30_00_world.sql @@ -0,0 +1,71 @@ +DELIMITER ;; +CREATE PROCEDURE creature_health_2024_03_30_00_world() BEGIN + IF NOT EXISTS (SELECT * FROM `information_schema`.`columns` WHERE `table_schema`=SCHEMA() AND `table_name`='creature' AND `column_name`='curHealthPct') THEN + + ALTER TABLE `creature` + CHANGE COLUMN `curhealth` `curHealthPct` INT UNSIGNED NOT NULL DEFAULT 100, + DROP COLUMN `curmana`; + + END IF; +END;; + +DELIMITER ; +CALL creature_health_2024_03_30_00_world(); + +DROP PROCEDURE IF EXISTS creature_health_2024_03_30_00_world; + +UPDATE `creature` c +INNER JOIN `creature_template` ct ON c.`id`=ct.`entry` +SET c.`curHealthPct`=100 +WHERE ct.`RegenHealth`=1; + +-- Spells +DELETE FROM `serverside_spell` WHERE `Id` IN (86562); +INSERT INTO `serverside_spell` (`Id`, `DifficultyID`, `CategoryId`, `Dispel`, `Mechanic`, `Attributes`, `AttributesEx`, `AttributesEx2`, `AttributesEx3`, `AttributesEx4`, `AttributesEx5`, `AttributesEx6`, `AttributesEx7`, `AttributesEx8`, `AttributesEx9`, `AttributesEx10`, `AttributesEx11`, `AttributesEx12`, `AttributesEx13`, `AttributesEx14`, `Stances`, `StancesNot`, `Targets`, `TargetCreatureType`, `RequiresSpellFocus`, `FacingCasterFlags`, `CasterAuraState`, `TargetAuraState`, `ExcludeCasterAuraState`, `ExcludeTargetAuraState`, `CasterAuraSpell`, `TargetAuraSpell`, `ExcludeCasterAuraSpell`, `ExcludeTargetAuraSpell`, `CasterAuraType`, `TargetAuraType`, `ExcludeCasterAuraType`, `ExcludeTargetAuraType`, `CastingTimeIndex`, `RecoveryTime`, `CategoryRecoveryTime`, `StartRecoveryCategory`, `StartRecoveryTime`, `InterruptFlags`, `AuraInterruptFlags1`, `AuraInterruptFlags2`, `ChannelInterruptFlags1`, `ChannelInterruptFlags2`, `ProcFlags`, `ProcFlags2`, `ProcChance`, `ProcCharges`, `ProcCooldown`, `ProcBasePPM`, `MaxLevel`, `BaseLevel`, `SpellLevel`, `DurationIndex`, `RangeIndex`, `Speed`, `LaunchDelay`, `StackAmount`, `EquippedItemClass`, `EquippedItemSubClassMask`, `EquippedItemInventoryTypeMask`, `ContentTuningId`, `SpellName`, `ConeAngle`, `ConeWidth`, `MaxTargetLevel`, `MaxAffectedTargets`, `SpellFamilyName`, `SpellFamilyFlags1`, `SpellFamilyFlags2`, `SpellFamilyFlags3`, `SpellFamilyFlags4`, `DmgClass`, `PreventionType`, `AreaGroupId`, `SchoolMask`, `ChargeCategoryId`) VALUES +(86562, 0, 0, 0, 0, 384, 268435456, 0, 0, 128, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, '1 Health', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0); + +UPDATE `serverside_spell` SET `AttributesEx4`=128, `AttributesEx5`=8, `ProcChance`=0 WHERE `Id` IN (24931, 24959, 28838, 73342); +UPDATE `serverside_spell` SET `Attributes`=384, `DurationIndex`=0 WHERE `Id` IN (24931, 24959); + +DELETE FROM `serverside_spell_effect` WHERE `SpellID` IN (24931, 24959, 43645, 73342, 86562); +INSERT INTO `serverside_spell_effect` (`SpellID`, `EffectIndex`, `DifficultyID`, `Effect`, `EffectAura`, `EffectAmplitude`, `EffectAttributes`, `EffectAuraPeriod`, `EffectBonusCoefficient`, `EffectChainAmplitude`, `EffectChainTargets`, `EffectItemType`, `EffectMechanic`, `EffectPointsPerResource`, `EffectPosFacing`, `EffectRealPointsPerLevel`, `EffectTriggerSpell`, `BonusCoefficientFromAP`, `PvpMultiplier`, `Coefficient`, `Variance`, `ResourceCoefficient`, `GroupSizeBasePointsCoefficient`, `EffectBasePoints`, `EffectMiscValue1`, `EffectMiscValue2`, `EffectRadiusIndex1`, `EffectRadiusIndex2`, `EffectSpellClassMask1`, `EffectSpellClassMask2`, `EffectSpellClassMask3`, `EffectSpellClassMask4`, `ImplicitTarget1`, `ImplicitTarget2`) VALUES +(24931, 0, 0, 77, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0), +(24959, 0, 0, 77, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0), +(43645, 0, 0, 77, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0), +(73342, 0, 0, 77, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0), +(86562, 0, 0, 77, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0); + +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_gen_set_health_1', 'spell_gen_set_health_100', 'spell_gen_set_health_500'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(24931, 'spell_gen_set_health_100'), +(24959, 'spell_gen_set_health_500'), +(28838, 'spell_gen_set_health_1'), +(43645, 'spell_gen_set_health_1'), +(73342, 'spell_gen_set_health_1'), +(86562, 'spell_gen_set_health_1'); + +-- Special cases +UPDATE `creature` SET `curHealthPct`=100 WHERE `id` IN (6172, 6177, 16601, 26335); + +UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry` IN (16601, 26335); + +UPDATE `smart_scripts` SET `id`=2 WHERE `entryorguid` IN (6172,6177) AND `source_type`=0 AND `id`=1 AND `event_type`=8; +UPDATE `smart_scripts` SET `link`=1 WHERE `entryorguid` IN (6172,6177) AND `source_type`=0 AND `id`=0; +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (16601, 26335) AND `source_type`=0; +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (6172,6177) AND `source_type`=0 AND `id`=1; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param_string`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `action_param7`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(6172,0,1,0,61,0,100,0,0,0,0,0,0,'',11,28838,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Henze Faulk - Event linked - Cast ''1 Health'''), +(6177,0,1,0,61,0,100,0,0,0,0,0,0,'',11,28838,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Narm Faulk - Event linked - Cast ''1 Health'''), +(16601,0,0,0,25,0,100,0,0,0,0,0,0,'',11,28838,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Dying Blood Elf - On reset - Cast ''1 Health'''), +(26335,0,0,0,25,0,100,0,0,0,0,0,0,'',11,43645,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Fallen Earthen Warrior - On reset - Cast ''1 Health'''); + +UPDATE `creature` SET `curHealthPct`=100 WHERE `id` IN (4510,7604,7605,7606,7607,7608,18192,18256,25063,27894,28781,30236,34775,34776,34793,34802,34929,34935,34944,35069,35273); +UPDATE `creature` SET `curHealthPct`=10 WHERE `id` IN (16483,149917,166800); +UPDATE `creature` SET `curHealthPct`=20 WHERE `id` IN (44617,44795,44857,50047,156609,156610,156612,156622); +UPDATE `creature` SET `curHealthPct`=30 WHERE `id` IN (13020,25661,30035,31273,31304,166784); +UPDATE `creature` SET `curHealthPct`=40 WHERE `id` IN (18708,20869,166791); +UPDATE `creature` SET `curHealthPct`=50 WHERE `id` IN (166786,166796); +UPDATE `creature` SET `curHealthPct`=60 WHERE `id` IN (12423,12427,12428,12429,12430,17551); + +DELETE FROM `creature` WHERE `guid`=137747 AND `id`=36789; +UPDATE `creature` SET `spawnDifficulties`='3,4,5,6', `curHealthPct`=50 WHERE `id`=36789; diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 2438bc1c1e1ca..9827b44246499 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -66,7 +66,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_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_", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_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 creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH); - PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id , map, spawnDifficulties, PhaseId, PhaseGroup, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, unit_flags2, unit_flags3) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id , map, spawnDifficulties, PhaseId, PhaseGroup, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curHealthPct, MovementType, npcflag, unit_flags, unit_flags2, unit_flags3) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_GAME_EVENT_CREATURE, "DELETE FROM game_event_creature WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_GAME_EVENT_MODEL_EQUIP, "DELETE FROM game_event_model_equip WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_INS_GAMEOBJECT, "INSERT INTO gameobject (guid, id, map, spawnDifficulties, PhaseId, PhaseGroup, position_x, position_y, position_z, orientation, rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 92c71f389e106..0f6780d56d9af 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -1843,7 +1843,7 @@ enum class PowerTypeFlags : int16 ContinueRegenWhileFatigued = 0x0200, // NYI RegenAffectedByHaste = 0x0400, // NYI SetToMaxOnLevelUp = 0x1000, - SetToMaxLevelOnInitialLogIn = 0x2000, // NYI + SetToMaxOnInitialLogIn = 0x2000, // NYI AllowCostModsForPlayers = 0x4000 // NYI }; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 1834377997864..fedad2a592d82 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1492,8 +1492,7 @@ void Creature::SaveToDB(uint32 mapid, std::vector const& spawnDiffic // prevent add data integrity problems data.wander_distance = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0.0f : m_wanderDistance; data.currentwaypoint = 0; - data.curhealth = GetHealth(); - data.curmana = GetPower(POWER_MANA); + data.curHealthPct = uint32(GetHealthPct()); // prevent add data integrity problems data.movementType = !m_wanderDistance && GetDefaultMovementType() == RANDOM_MOTION_TYPE ? IDLE_MOTION_TYPE : GetDefaultMovementType(); @@ -1546,8 +1545,7 @@ void Creature::SaveToDB(uint32 mapid, std::vector const& spawnDiffic stmt->setUInt32(index++, m_respawnDelay); stmt->setFloat(index++, m_wanderDistance); stmt->setUInt32(index++, 0); - stmt->setUInt32(index++, GetHealth()); - stmt->setUInt32(index++, GetPower(POWER_MANA)); + stmt->setUInt32(index++, uint32(GetHealthPct())); stmt->setUInt8(index++, uint8(GetDefaultMovementType())); if (npcflag.has_value()) stmt->setUInt64(index++, *npcflag); @@ -1608,15 +1606,7 @@ void Creature::UpdateLevelDependantStats() Powers powerType = CalculateDisplayPowerType(); SetCreateMana(stats->BaseMana); SetStatPctModifier(UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(powerType)), BASE_PCT, GetCreatureDifficulty()->ManaModifier); - SetPowerType(powerType); - - if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType)) - { - if (powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::UnitsUseDefaultPowerOnInit)) - SetPower(powerType, powerTypeEntry->DefaultPower); - else - SetFullPower(powerType); - } + SetPowerType(powerType, true, true); // damage float basedamage = GetBaseDamageForLevel(level); @@ -1968,28 +1958,8 @@ void Creature::LoadEquipment(int8 id, bool force /*= true*/) void Creature::SetSpawnHealth() { - if (_staticFlags.HasFlag(CREATURE_STATIC_FLAG_5_NO_HEALTH_REGEN)) - return; - - uint32 curhealth; - if (m_creatureData && !_regenerateHealth) - { - curhealth = m_creatureData->curhealth; - if (curhealth) - { - curhealth = uint32(curhealth*GetHealthMod(GetCreatureTemplate()->Classification)); - if (curhealth < 1) - curhealth = 1; - } - SetPower(POWER_MANA, m_creatureData->curmana); - } - else - { - curhealth = GetMaxHealth(); - SetFullPower(POWER_MANA); - } - - SetHealth((m_deathState == ALIVE || m_deathState == JUST_RESPAWNED) ? curhealth : 0); + SetHealth(CountPctFromMaxHealth(m_creatureData ? m_creatureData->curHealthPct : 100)); + SetInitialPowerValue(GetPowerType()); } bool Creature::hasQuest(uint32 quest_id) const diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 976244a8f8591..dea0e4be37bc0 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -592,8 +592,7 @@ struct CreatureData : public SpawnData int8 equipmentId = 0; float wander_distance = 0.0f; uint32 currentwaypoint = 0; - uint32 curhealth = 0; - uint32 curmana = 0; + uint32 curHealthPct = 0; uint8 movementType = 0; Optional npcflag; Optional unit_flags; // enum UnitFlags mask values diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 4291953358987..6bb4cd2a8cb87 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -5531,12 +5531,43 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType SendAttackStateUpdate(&dmgInfo); } -void Unit::SetPowerType(Powers new_powertype, bool sendUpdate/* = true*/) +void Unit::SetPowerType(Powers power, bool sendUpdate/* = true*/, bool onInit /*= false*/) { - if (GetPowerType() == new_powertype) + if (!onInit && GetPowerType() == power) return; - SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::DisplayPower), new_powertype); + PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(power); + if (!powerTypeEntry) + return; + + if (IsCreature() && !powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::IsUsedByNPCs)) + return; + + SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::DisplayPower), power); + + // Update max power + UpdateMaxPower(power); + + // Update current power + if (!onInit) + { + switch (power) + { + case POWER_MANA: // Keep the same (druid form switching...) + case POWER_ENERGY: + break; + case POWER_RAGE: // Reset to zero + SetPower(POWER_RAGE, 0); + break; + case POWER_FOCUS: // Make it full + SetFullPower(power); + break; + default: + break; + } + } + else + SetInitialPowerValue(power); if (!sendUpdate) return; @@ -5551,25 +5582,18 @@ void Unit::SetPowerType(Powers new_powertype, bool sendUpdate/* = true*/) if (pet->isControlled()) pet->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_POWER_TYPE); }*/ +} - // Update max power - UpdateMaxPower(new_powertype); +void Unit::SetInitialPowerValue(Powers powerType) +{ + PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType); + if (!powerTypeEntry) + return; - // Update current power - switch (new_powertype) - { - case POWER_MANA: // Keep the same (druid form switching...) - case POWER_ENERGY: - break; - case POWER_RAGE: // Reset to zero - SetPower(POWER_RAGE, 0); - break; - case POWER_FOCUS: // Make it full - SetFullPower(new_powertype); - break; - default: - break; - } + if (powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::UnitsUseDefaultPowerOnInit)) + SetPower(powerType, powerTypeEntry->DefaultPower); + else + SetFullPower(powerType); } Powers Unit::CalculateDisplayPowerType() const diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index abcf9c295b5ca..d2a69f575671a 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -797,7 +797,8 @@ class TC_GAME_API Unit : public WorldObject virtual float GetArmorMultiplierForTarget(WorldObject const* /*target*/) const { return 1.0f; } Powers GetPowerType() const { return Powers(*m_unitData->DisplayPower); } - void SetPowerType(Powers power, bool sendUpdate = true); + void SetPowerType(Powers power, bool sendUpdate = true, bool onInit = false); + void SetInitialPowerValue(Powers powerType); void SetOverrideDisplayPowerId(uint32 powerDisplayId) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::OverrideDisplayPowerID), powerDisplayId); } Powers CalculateDisplayPowerType() const; void UpdateDisplayPower(); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 2c1d5c7ae8138..7c933cdd7746b 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2135,9 +2135,9 @@ void ObjectMgr::LoadCreatures() // 0 1 2 3 4 5 6 7 8 9 10 QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, wander_distance, " - // 11 12 13 14 15 16 17 18 19 20 21 - "currentwaypoint, curhealth, curmana, MovementType, spawnDifficulties, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, " - // 22 23 24 25 26 27 + // 11 12 13 14 15 16 17 18 19 20 + "currentwaypoint, curHealthPct, MovementType, spawnDifficulties, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, " + // 21 22 23 24 25 26 "creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName, creature.StringId " "FROM creature " "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid " @@ -2182,27 +2182,26 @@ void ObjectMgr::LoadCreatures() data.equipmentId = fields[8].GetInt8(); data.spawntimesecs = fields[9].GetUInt32(); data.wander_distance = fields[10].GetFloat(); - data.currentwaypoint= fields[11].GetUInt32(); - data.curhealth = fields[12].GetUInt32(); - data.curmana = fields[13].GetUInt32(); - data.movementType = fields[14].GetUInt8(); - data.spawnDifficulties = ParseSpawnDifficulties(fields[15].GetStringView(), "creature", guid, data.mapId, spawnMasks[data.mapId]); - int16 gameEvent = fields[16].GetInt8(); - data.poolId = fields[17].GetUInt32(); + data.currentwaypoint = fields[11].GetUInt32(); + data.curHealthPct = fields[12].GetUInt32(); + data.movementType = fields[13].GetUInt8(); + data.spawnDifficulties = ParseSpawnDifficulties(fields[14].GetStringView(), "creature", guid, data.mapId, spawnMasks[data.mapId]); + int16 gameEvent = fields[15].GetInt8(); + data.poolId = fields[16].GetUInt32(); + if (!fields[17].IsNull()) + data.npcflag = fields[17].GetUInt64(); if (!fields[18].IsNull()) - data.npcflag = fields[18].GetUInt64(); + data.unit_flags = fields[18].GetUInt32(); if (!fields[19].IsNull()) - data.unit_flags = fields[19].GetUInt32(); + data.unit_flags2 = fields[19].GetUInt32(); if (!fields[20].IsNull()) - data.unit_flags2 = fields[20].GetUInt32(); - if (!fields[21].IsNull()) - data.unit_flags3 = fields[21].GetUInt32(); - data.phaseUseFlags = fields[22].GetUInt8(); - data.phaseId = fields[23].GetUInt32(); - data.phaseGroup = fields[24].GetUInt32(); - data.terrainSwapMap = fields[25].GetInt32(); - data.scriptId = GetScriptId(fields[26].GetString()); - data.StringId = fields[27].GetString(); + data.unit_flags3 = fields[20].GetUInt32(); + data.phaseUseFlags = fields[21].GetUInt8(); + data.phaseId = fields[22].GetUInt32(); + data.phaseGroup = fields[23].GetUInt32(); + data.terrainSwapMap = fields[24].GetInt32(); + data.scriptId = GetScriptId(fields[25].GetString()); + data.StringId = fields[26].GetString(); data.spawnGroupData = IsTransportMap(data.mapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId); @@ -2364,6 +2363,13 @@ void ObjectMgr::LoadCreatures() } } + uint32 healthPct = std::clamp(data.curHealthPct, 1, 100); + if (data.curHealthPct != healthPct) + { + TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with invalid `curHealthPct` {}, set to {}.", guid, data.id, data.curHealthPct, healthPct); + data.curHealthPct = healthPct; + } + if (sWorld->getBoolConfig(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA)) { uint32 zoneId = 0; diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index ed6e2014ead86..86686a52ac25d 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -290,9 +290,8 @@ class npc_commandscript : public CommandScript return false; } - creature->SetMaxHealth(100 + 30*lvl); - creature->SetHealth(100 + 30*lvl); creature->SetLevel(lvl); + creature->UpdateLevelDependantStats(); creature->SaveToDB(); return true; diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp index ee9e3cb1d07f0..48844dc0aa9d2 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp @@ -84,6 +84,7 @@ struct boss_vaelastrasz : public BossAI { _Reset(); + me->SetSpawnHealth(); me->SetStandState(UNIT_STAND_STATE_DEAD); Initialize(); } @@ -93,7 +94,6 @@ struct boss_vaelastrasz : public BossAI BossAI::JustEngagedWith(who); DoCast(me, SPELL_ESSENCEOFTHERED); - me->SetHealth(me->CountPctFromMaxHealth(30)); // now drop damage requirement to be able to take loot me->ResetPlayerDamageReq(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp index 3b9e0f2b8de49..aa44e6bb7a727 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp @@ -275,19 +275,10 @@ struct boss_valithria_dreamwalker : public ScriptedAI _done = false; } - void InitializeAI() override - { - if (CreatureData const* data = me->GetCreatureData()) - if (data->curhealth) - _spawnHealth = data->curhealth; - - ScriptedAI::InitializeAI(); - } - void Reset() override { _events.Reset(); - me->SetHealth(_spawnHealth); + me->SetSpawnHealth(); me->SetReactState(REACT_PASSIVE); me->LoadCreaturesAddon(); // immune to percent heals diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index ff12c258c5e1d..8486d30ca9ae7 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -5317,6 +5317,32 @@ class spell_gen_random_aggro_taunt : public SpellScript } }; +// 24931 - 100 Health +// 24959 - 500 Health +// 28838 - 1 Health +// 43645 - 1 Health +// 73342 - 1 Health +// 86562 - 1 Health +class spell_gen_set_health : public SpellScript +{ +public: + spell_gen_set_health(uint64 health) : _health(health) { } + + void HandleHit(SpellEffIndex /*effIndex*/) + { + if (GetHitUnit()->IsAlive() && _health > 0) + GetHitUnit()->SetHealth(_health); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_set_health::HandleHit, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + +private: + uint64 _health; +}; + void AddSC_generic_spell_scripts() { RegisterSpellScript(spell_gen_absorb0_hitlimit1); @@ -5493,4 +5519,7 @@ void AddSC_generic_spell_scripts() RegisterSpellScript(spell_gen_major_healing_cooldown_modifier); RegisterSpellScript(spell_gen_major_healing_cooldown_modifier_aura); RegisterSpellScript(spell_gen_random_aggro_taunt); + RegisterSpellScriptWithArgs(spell_gen_set_health, "spell_gen_set_health_1", 1); + RegisterSpellScriptWithArgs(spell_gen_set_health, "spell_gen_set_health_100", 100); + RegisterSpellScriptWithArgs(spell_gen_set_health, "spell_gen_set_health_500", 500); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index a3db8c19c6b8d..d2294bbd2b765 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -1005,7 +1005,7 @@ class npc_garments_of_quests : public CreatureScript me->SetStandState(UNIT_STAND_STATE_KNEEL); // expect database to have RegenHealth=0 - me->SetHealth(me->CountPctFromMaxHealth(70)); + me->SetSpawnHealth(); } void JustEngagedWith(Unit* /*who*/) override { }