diff --git a/src/game/Object/Creature.cpp b/src/game/Object/Creature.cpp index 51f9f9d38..af532fa74 100644 --- a/src/game/Object/Creature.cpp +++ b/src/game/Object/Creature.cpp @@ -393,78 +393,85 @@ bool Creature::InitEntry(uint32 Entry, Team team, CreatureData const* data /*=NU bool Creature::UpdateEntry(uint32 Entry, Team team, const CreatureData* data /*=NULL*/, GameEventCreatureData const* eventData /*=NULL*/, bool preserveHPAndPower /*=true*/) { - if (!InitEntry(Entry, team, data, eventData)) - { return false; } - - // creatures always have melee weapon ready if any - SetSheath(SHEATH_STATE_MELEE); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_AURAS); - - SelectLevel(GetCreatureInfo(), preserveHPAndPower ? GetHealthPercent() : 100.0f); - - if (team == HORDE) - { setFaction(GetCreatureInfo()->FactionHorde); } - else - { setFaction(GetCreatureInfo()->FactionAlliance); } - - SetUInt32Value(UNIT_NPC_FLAGS, GetCreatureInfo()->NpcFlags); - - uint32 attackTimer = GetCreatureInfo()->MeleeBaseAttackTime; - - SetAttackTime(BASE_ATTACK, attackTimer); - SetAttackTime(OFF_ATTACK, attackTimer - attackTimer / 4); - SetAttackTime(RANGED_ATTACK, GetCreatureInfo()->RangedBaseAttackTime); - - uint32 unitFlags = GetCreatureInfo()->UnitFlags; - - // we may need to append or remove additional flags - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT)) - { unitFlags |= UNIT_FLAG_IN_COMBAT; } - - if (m_movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING) && (GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_HAVE_NO_SWIM_ANIMATION) == 0) - unitFlags |= UNIT_FLAG_UNK_15; - else - unitFlags &= ~UNIT_FLAG_UNK_15; - - SetUInt32Value(UNIT_FIELD_FLAGS, unitFlags); - - // preserve all current dynamic flags if exist - uint32 dynFlags = GetUInt32Value(UNIT_DYNAMIC_FLAGS); - SetUInt32Value(UNIT_DYNAMIC_FLAGS, dynFlags ? dynFlags : GetCreatureInfo()->DynamicFlags); - - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(GetCreatureInfo()->Armor)); - SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(GetCreatureInfo()->ResistanceHoly)); - SetModifierValue(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(GetCreatureInfo()->ResistanceFire)); - SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(GetCreatureInfo()->ResistanceNature)); - SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(GetCreatureInfo()->ResistanceFrost)); - SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(GetCreatureInfo()->ResistanceShadow)); - SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(GetCreatureInfo()->ResistanceArcane)); - - SetCanModifyStats(true); - UpdateAllStats(); - - // checked and error show at loading templates - if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(GetCreatureInfo()->FactionAlliance)) - { - if (factionTemplate->factionFlags & FACTION_TEMPLATE_FLAG_PVP) - { SetPvP(true); } - else - { SetPvP(false); } - } - - // Try difficulty dependend version before falling back to base entry - CreatureTemplateSpells const* templateSpells = sCreatureTemplateSpellsStorage.LookupEntry(GetCreatureInfo()->Entry); - if (!templateSpells) - { templateSpells = sCreatureTemplateSpellsStorage.LookupEntry(GetEntry()); } - if (templateSpells) - for (int i = 0; i < CREATURE_MAX_SPELLS; ++i) - { m_spells[i] = templateSpells->spells[i]; } - - // if eventData set then event active and need apply spell_start - if (eventData) - { ApplyGameEventSpells(eventData, true); } - - return true; + if (!InitEntry(Entry, team, data, eventData)) + return false; + + // creatures always have melee weapon ready if any + SetSheath(SHEATH_STATE_MELEE); + SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_AURAS); + + if (preserveHPAndPower) + { + uint32 healthPercent = GetHealthPercent(); + SelectLevel(); + SetHealthPercent(healthPercent); + } + else + SelectLevel(); + + if (team == HORDE) + setFaction(GetCreatureInfo()->FactionHorde); + else + setFaction(GetCreatureInfo()->FactionAlliance); + + SetUInt32Value(UNIT_NPC_FLAGS, GetCreatureInfo()->NpcFlags); + + uint32 attackTimer = GetCreatureInfo()->MeleeBaseAttackTime; + + SetAttackTime(BASE_ATTACK, attackTimer); + SetAttackTime(OFF_ATTACK, attackTimer - attackTimer / 4); + SetAttackTime(RANGED_ATTACK, GetCreatureInfo()->RangedBaseAttackTime); + + uint32 unitFlags = GetCreatureInfo()->UnitFlags; + + // we may need to append or remove additional flags + if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT)) + unitFlags |= UNIT_FLAG_IN_COMBAT; + + if (m_movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING) && (GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_HAVE_NO_SWIM_ANIMATION) == 0) + unitFlags |= UNIT_FLAG_UNK_15; + else + unitFlags &= ~UNIT_FLAG_UNK_15; + + SetUInt32Value(UNIT_FIELD_FLAGS, unitFlags); + + // preserve all current dynamic flags if exist + uint32 dynFlags = GetUInt32Value(UNIT_DYNAMIC_FLAGS); + SetUInt32Value(UNIT_DYNAMIC_FLAGS, dynFlags ? dynFlags : GetCreatureInfo()->DynamicFlags); + + SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(GetCreatureInfo()->Armor)); + SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(GetCreatureInfo()->ResistanceHoly)); + SetModifierValue(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(GetCreatureInfo()->ResistanceFire)); + SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(GetCreatureInfo()->ResistanceNature)); + SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(GetCreatureInfo()->ResistanceFrost)); + SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(GetCreatureInfo()->ResistanceShadow)); + SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(GetCreatureInfo()->ResistanceArcane)); + + SetCanModifyStats(true); + UpdateAllStats(); + + // checked and error show at loading templates + if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(GetCreatureInfo()->FactionAlliance)) + { + if (factionTemplate->factionFlags & FACTION_TEMPLATE_FLAG_PVP) + SetPvP(true); + else + SetPvP(false); + } + + // Try difficulty dependend version before falling back to base entry + CreatureTemplateSpells const* templateSpells = sCreatureTemplateSpellsStorage.LookupEntry(GetCreatureInfo()->Entry); + if (!templateSpells) + templateSpells = sCreatureTemplateSpellsStorage.LookupEntry(GetEntry()); + if (templateSpells) + for (int i = 0; i < CREATURE_MAX_SPELLS; ++i) + m_spells[i] = templateSpells->spells[i]; + + // if eventData set then event active and need apply spell_start + if (eventData) + ApplyGameEventSpells(eventData, true); + + return true; } uint32 Creature::ChooseDisplayId(const CreatureInfo* cinfo, const CreatureData* data /*= NULL*/, GameEventCreatureData const* eventData /*=NULL*/) @@ -537,7 +544,7 @@ void Creature::Update(uint32 update_diff, uint32 diff) CreatureInfo const* cinfo = GetCreatureInfo(); - SelectLevel(cinfo); + SelectLevel(); UpdateAllStats(); // to be sure stats is correct regarding level of the creature SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE); if (m_IsDeadByDefault) @@ -1207,114 +1214,126 @@ void Creature::SaveToDB(uint32 mapid) WorldDatabase.CommitTransaction(); } -void Creature::SelectLevel(const CreatureInfo* cinfo, float percentHealth /*= 100.0f*/) -{ - uint32 rank = IsPet() ? 0 : cinfo->Rank; // TODO :: IsPet probably not needed here - - // level - uint32 const minlevel = cinfo->MinLevel; - uint32 const maxlevel = cinfo->MaxLevel; - uint32 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel); - SetLevel(level); - - ////////////////////////////////////////////////////////////////////////// - // Calculate level dependent stats - ////////////////////////////////////////////////////////////////////////// - - uint32 health; - uint32 mana; - - // TODO: Remove cinfo->ArmorMultiplier test workaround to disable classlevelstats when DB is ready - CreatureClassLvlStats const* cCLS = sObjectMgr.GetCreatureClassLvlStats(level, cinfo->UnitClass); - if (cinfo->ArmorMultiplier > 0 && cCLS) - { - // Use Creature Stats to calculate stat values - - // health - health = cCLS->BaseHealth * cinfo->HealthMultiplier; - - // mana - mana = cCLS->BaseMana * cinfo->PowerMultiplier; - } - else - { - // Use old style to calculate stat values - float rellevel = maxlevel == minlevel ? 0 : (float(level - minlevel)) / (maxlevel - minlevel); - - // health - uint32 minhealth = std::min(cinfo->MaxLevelHealth, cinfo->MinLevelHealth); - uint32 maxhealth = std::max(cinfo->MaxLevelHealth, cinfo->MinLevelHealth); - health = uint32(minhealth + uint32(rellevel * (maxhealth - minhealth))); - - // mana - uint32 minmana = std::min(cinfo->MaxLevelMana, cinfo->MinLevelMana); - uint32 maxmana = std::max(cinfo->MaxLevelMana, cinfo->MinLevelMana); - mana = minmana + uint32(rellevel * (maxmana - minmana)); - } - - health *= _GetHealthMod(rank); // Apply custom config setting - if (health < 1) - health = 1; - - ////////////////////////////////////////////////////////////////////////// - // Set values - ////////////////////////////////////////////////////////////////////////// - - // health - SetCreateHealth(health); - SetMaxHealth(health); - - if (percentHealth == 100.0f) - { SetHealth(health); } - else - { SetHealthPercent(percentHealth); } - - ResetPlayerDamageReq(); - - SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, float(health)); - - // all power types - for (uint8 i = POWER_MANA; i <= POWER_HAPPINESS; ++i) - { - uint32 maxValue; - - switch (i) - { - case POWER_MANA: maxValue = mana; break; - case POWER_RAGE: maxValue = 0; break; - case POWER_FOCUS: maxValue = POWER_FOCUS; break; - case POWER_ENERGY: maxValue = POWER_ENERGY* cinfo->PowerMultiplier; break; - case POWER_HAPPINESS: maxValue = POWER_HAPPINESS; break; - } - - uint32 value = maxValue; - - // For non regenerating powers set 0 - if ((i == POWER_ENERGY || i == POWER_MANA) && !IsRegeneratingPower()) - value = 0; - - // Mana requires an extra field to be set - if (i == POWER_MANA) - SetCreateMana(value); - - SetMaxPower(Powers(i), maxValue); - SetPower(Powers(i), value); - SetModifierValue(UnitMods(UNIT_MOD_POWER_START + i), BASE_VALUE, float(value)); - } - - // damage - float damagemod = _GetDamageMod(rank); - - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->MinMeleeDmg * damagemod); - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->MaxMeleeDmg * damagemod); - - SetBaseWeaponDamage(OFF_ATTACK, MINDAMAGE, cinfo->MinMeleeDmg * damagemod); - SetBaseWeaponDamage(OFF_ATTACK, MAXDAMAGE, cinfo->MaxMeleeDmg * damagemod); - - SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, cinfo->MinRangedDmg * damagemod); - SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, cinfo->MaxRangedDmg * damagemod); - - SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, cinfo->MeleeAttackPower * damagemod); +void Creature::SelectLevel(uint32 forcedLevel /*= USE_DEFAULT_DATABASE_LEVEL*/) +{ + CreatureInfo const* cinfo = GetCreatureInfo(); + if (!cinfo) + return; + + uint32 rank = IsPet() ? 0 : cinfo->Rank; // TODO :: IsPet probably not needed here + + // level + uint32 level = forcedLevel; + uint32 const minlevel = cinfo->MinLevel; + uint32 const maxlevel = cinfo->MaxLevel; + + if (level == USE_DEFAULT_DATABASE_LEVEL) + level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel); + + SetLevel(level); + + ////////////////////////////////////////////////////////////////////////// + // Calculate level dependent stats + ////////////////////////////////////////////////////////////////////////// + + uint32 health; + uint32 mana; + + // TODO: Remove cinfo->ArmorMultiplier test workaround to disable classlevelstats when DB is ready + CreatureClassLvlStats const* cCLS = sObjectMgr.GetCreatureClassLvlStats(level, cinfo->UnitClass); + if (cinfo->ArmorMultiplier > 0 && cCLS) + { + // Use Creature Stats to calculate stat values + + // health + health = cCLS->BaseHealth * cinfo->HealthMultiplier; + + // mana + mana = cCLS->BaseMana * cinfo->PowerMultiplier; + } + else + { + if (forcedLevel == USE_DEFAULT_DATABASE_LEVEL || (forcedLevel >= minlevel && forcedLevel <= maxlevel)) + { + // Use old style to calculate stat values + float rellevel = maxlevel == minlevel ? 0 : (float(level - minlevel)) / (maxlevel - minlevel); + + // health + uint32 minhealth = std::min(cinfo->MaxLevelHealth, cinfo->MinLevelHealth); + uint32 maxhealth = std::max(cinfo->MaxLevelHealth, cinfo->MinLevelHealth); + health = uint32(minhealth + uint32(rellevel * (maxhealth - minhealth))); + + // mana + uint32 minmana = std::min(cinfo->MaxLevelMana, cinfo->MinLevelMana); + uint32 maxmana = std::max(cinfo->MaxLevelMana, cinfo->MinLevelMana); + mana = minmana + uint32(rellevel * (maxmana - minmana)); + } + else + { + sLog.outError("Creature::SelectLevel> Error trying to set level(%u) for creature %s without enough data to do it!", level, GetGuidStr().c_str()); + // probably wrong + health = (cinfo->MaxLevelHealth / cinfo->MaxLevel) * level; + mana = (cinfo->MaxLevelMana / cinfo->MaxLevel) * level; + } + } + + health *= _GetHealthMod(rank); // Apply custom config setting + if (health < 1) + health = 1; + + ////////////////////////////////////////////////////////////////////////// + // Set values + ////////////////////////////////////////////////////////////////////////// + + // health + SetCreateHealth(health); + SetMaxHealth(health); + SetHealth(health); + + SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, float(health)); + + // all power types + for (int i = POWER_MANA; i <= POWER_HAPPINESS; ++i) + { + uint32 maxValue; + + switch (i) + { + case POWER_MANA: maxValue = mana; break; + case POWER_RAGE: maxValue = 0; break; + case POWER_FOCUS: maxValue = POWER_FOCUS_DEFAULT; break; + case POWER_ENERGY: maxValue = POWER_ENERGY_DEFAULT * cinfo->PowerMultiplier; break; + case POWER_HAPPINESS: maxValue = POWER_HAPPINESS_DEFAULT; break; + } + + uint32 value = maxValue; + + // For non regenerating powers set 0 + if ((i == POWER_ENERGY || i == POWER_MANA) && !IsRegeneratingPower()) + value = 0; + + // Mana requires an extra field to be set + if (i == POWER_MANA) + SetCreateMana(value); + + SetMaxPower(Powers(i), maxValue); + SetPower(Powers(i), value); + SetModifierValue(UnitMods(UNIT_MOD_POWER_START + i), BASE_VALUE, float(value)); + } + + // damage + float damagemod = _GetDamageMod(rank); + + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->MinMeleeDmg * damagemod); + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->MaxMeleeDmg * damagemod); + + SetBaseWeaponDamage(OFF_ATTACK, MINDAMAGE, cinfo->MinMeleeDmg * damagemod); + SetBaseWeaponDamage(OFF_ATTACK, MAXDAMAGE, cinfo->MaxMeleeDmg * damagemod); + + SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, cinfo->MinRangedDmg * damagemod); + SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, cinfo->MaxRangedDmg * damagemod); + + SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, cinfo->MeleeAttackPower * damagemod); } float Creature::_GetHealthMod(int32 Rank) diff --git a/src/game/Object/Creature.h b/src/game/Object/Creature.h index 72d909fde..480c952d5 100644 --- a/src/game/Object/Creature.h +++ b/src/game/Object/Creature.h @@ -74,6 +74,7 @@ enum CreatureFlagsExtra #define MAX_KILL_CREDIT 2 #define MAX_CREATURE_MODEL 4 // only single send to client in static data +#define USE_DEFAULT_DATABASE_LEVEL 0 // just used to show we don't want to force the new creature level and use the level stored in db // from `creature_template` table struct CreatureInfo @@ -507,7 +508,7 @@ class Creature : public Unit bool Create(uint32 guidlow, CreatureCreatePos& cPos, CreatureInfo const* cinfo, Team team = TEAM_NONE, const CreatureData* data = NULL, GameEventCreatureData const* eventData = NULL); bool LoadCreatureAddon(bool reload); - void SelectLevel(const CreatureInfo* cinfo, float percentHealth = 100.0f); + void SelectLevel(uint32 forcedLevel = USE_DEFAULT_DATABASE_LEVEL); void LoadEquipment(uint32 equip_entry, bool force = false); bool HasStaticDBSpawnData() const; // listed in `creature` table and have fixed in DB guid diff --git a/src/game/WorldHandlers/SpellEffects.cpp b/src/game/WorldHandlers/SpellEffects.cpp index 94fd2368a..5b9992298 100644 --- a/src/game/WorldHandlers/SpellEffects.cpp +++ b/src/game/WorldHandlers/SpellEffects.cpp @@ -4839,15 +4839,14 @@ void Spell::EffectSummonCritter(SpellEffectIndex eff_idx) critter->SetRespawnCoord(pos); - // critter->SetName(""); // generated by client - critter->SetOwnerGuid(m_caster->GetObjectGuid()); - critter->SetCreatorGuid(m_caster->GetObjectGuid()); - critter->setFaction(m_caster->getFaction()); - critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - critter->SelectLevel(critter->GetCreatureInfo()); // some summoned creaters have different from 1 DB data for level/hp - critter->SetUInt32Value(UNIT_NPC_FLAGS, critter->GetCreatureInfo()->NpcFlags); - // some mini-pets have quests + // critter->SetName(""); // generated by client + critter->SetOwnerGuid(m_caster->GetObjectGuid()); + critter->SetCreatorGuid(m_caster->GetObjectGuid()); + critter->setFaction(m_caster->getFaction()); + critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + critter->SelectLevel(); // some summoned critters have different from 1 DB data for level/hp + critter->SetUInt32Value(UNIT_NPC_FLAGS, critter->GetCreatureInfo()->NpcFlags); + // some mini-pets have quests // set timer for unsummon int32 duration = GetSpellDuration(m_spellInfo);