Files
CypherCore/Source/Game/Entities/TemporarySummon.cs
T

1052 lines
43 KiB
C#

/*
* Copyright (C) 2012-2018 CypherCore <http://github.com/CypherCore>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Framework.Dynamic;
using Game.DataStorage;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace Game.Entities
{
public class TempSummon : Creature
{
public TempSummon(SummonPropertiesRecord properties, Unit owner, bool isWorldObject) : base(isWorldObject)
{
m_Properties = properties;
m_type = TempSummonType.ManualDespawn;
m_summonerGUID = owner != null ? owner.GetGUID() : ObjectGuid.Empty;
m_unitTypeMask |= UnitTypeMask.Summon;
}
public Unit GetSummoner()
{
return !m_summonerGUID.IsEmpty() ? Global.ObjAccessor.GetUnit(this, m_summonerGUID) : null;
}
public Creature GetSummonerCreatureBase()
{
return !m_summonerGUID.IsEmpty() ? ObjectAccessor.GetCreature(this, m_summonerGUID) : null;
}
public override void Update(uint diff)
{
base.Update(diff);
if (m_deathState == DeathState.Dead)
{
UnSummon();
return;
}
switch (m_type)
{
case TempSummonType.ManualDespawn:
break;
case TempSummonType.TimedDespawn:
{
if (m_timer <= diff)
{
UnSummon();
return;
}
m_timer -= diff;
break;
}
case TempSummonType.TimedDespawnOOC:
{
if (!IsInCombat())
{
if (m_timer <= diff)
{
UnSummon();
return;
}
m_timer -= diff;
}
else if (m_timer != m_lifetime)
m_timer = m_lifetime;
break;
}
case TempSummonType.CorpseTimedDespawn:
{
if (m_deathState == DeathState.Corpse)
{
if (m_timer <= diff)
{
UnSummon();
return;
}
m_timer -= diff;
}
break;
}
case TempSummonType.CorpseDespawn:
{
// if m_deathState is DEAD, CORPSE was skipped
if (m_deathState == DeathState.Corpse || m_deathState == DeathState.Dead)
{
UnSummon();
return;
}
break;
}
case TempSummonType.DeadDespawn:
{
if (m_deathState == DeathState.Dead)
{
UnSummon();
return;
}
break;
}
case TempSummonType.TimedOrCorpseDespawn:
{
// if m_deathState is DEAD, CORPSE was skipped
if (m_deathState == DeathState.Corpse || m_deathState == DeathState.Dead)
{
UnSummon();
return;
}
if (!IsInCombat())
{
if (m_timer <= diff)
{
UnSummon();
return;
}
else
m_timer -= diff;
}
else if (m_timer != m_lifetime)
m_timer = m_lifetime;
break;
}
case TempSummonType.TimedOrDeadDespawn:
{
// if m_deathState is DEAD, CORPSE was skipped
if (m_deathState == DeathState.Dead)
{
UnSummon();
return;
}
if (!IsInCombat() && IsAlive())
{
if (m_timer <= diff)
{
UnSummon();
return;
}
else
m_timer -= diff;
}
else if (m_timer != m_lifetime)
m_timer = m_lifetime;
break;
}
default:
UnSummon();
Log.outError(LogFilter.Unit, "Temporary summoned creature (entry: {0}) have unknown type {1} of ", GetEntry(), m_type);
break;
}
}
public virtual void InitStats(uint duration)
{
Contract.Assert(!IsPet());
m_timer = duration;
m_lifetime = duration;
if (m_type == TempSummonType.ManualDespawn)
m_type = (duration == 0) ? TempSummonType.DeadDespawn : TempSummonType.TimedDespawn;
Unit owner = GetSummoner();
if (owner != null && IsTrigger() && m_spells[0] != 0)
{
SetFaction(owner.getFaction());
SetLevel(owner.getLevel());
if (owner.IsTypeId(TypeId.Player))
m_ControlledByPlayer = true;
}
if (m_Properties == null)
return;
if (owner != null)
{
int slot = m_Properties.Slot;
if (slot > 0)
{
if (!owner.m_SummonSlot[slot].IsEmpty() && owner.m_SummonSlot[slot] != GetGUID())
{
Creature oldSummon = GetMap().GetCreature(owner.m_SummonSlot[slot]);
if (oldSummon != null && oldSummon.IsSummon())
oldSummon.ToTempSummon().UnSummon();
}
owner.m_SummonSlot[slot] = GetGUID();
}
}
if (m_Properties.Faction != 0)
SetFaction(m_Properties.Faction);
else if (IsVehicle() && owner != null) // properties should be vehicle
SetFaction(owner.getFaction());
}
public virtual void InitSummon()
{
Unit owner = GetSummoner();
if (owner != null)
{
if (owner.IsTypeId(TypeId.Unit) && owner.ToCreature().IsAIEnabled)
owner.ToCreature().GetAI().JustSummoned(this);
if (IsAIEnabled)
GetAI().IsSummonedBy(owner);
}
}
public override void UpdateObjectVisibilityOnCreate()
{
base.UpdateObjectVisibility(true);
}
public void SetTempSummonType(TempSummonType type)
{
m_type = type;
}
public virtual void UnSummon(uint msTime = 0)
{
if (msTime != 0)
{
ForcedUnsummonDelayEvent pEvent = new ForcedUnsummonDelayEvent(this);
m_Events.AddEvent(pEvent, m_Events.CalculateTime(msTime));
return;
}
Contract.Assert(!IsPet());
if (IsPet())
{
ToPet().Remove(PetSaveMode.NotInSlot);
Contract.Assert(!IsInWorld);
return;
}
Unit owner = GetSummoner();
if (owner != null && owner.IsTypeId(TypeId.Unit) && owner.ToCreature().IsAIEnabled)
owner.ToCreature().GetAI().SummonedCreatureDespawn(this);
AddObjectToRemoveList();
}
public override void RemoveFromWorld()
{
if (!IsInWorld)
return;
if (m_Properties != null)
{
int slot = m_Properties.Slot;
if (slot > 0)
{
Unit owner = GetSummoner();
if (owner != null)
if (owner.m_SummonSlot[slot] == GetGUID())
owner.m_SummonSlot[slot].Clear();
}
}
if (!GetOwnerGUID().IsEmpty())
Log.outError(LogFilter.Unit, "Unit {0} has owner guid when removed from world", GetEntry());
base.RemoveFromWorld();
}
public override void SaveToDB(uint mapid, ulong spawnMask) { }
public ObjectGuid GetSummonerGUID() { return m_summonerGUID; }
TempSummonType GetSummonType() { return m_type; }
public uint GetTimer() { return m_timer; }
public SummonPropertiesRecord m_Properties;
TempSummonType m_type;
uint m_timer;
uint m_lifetime;
ObjectGuid m_summonerGUID;
}
public class Minion : TempSummon
{
public Minion(SummonPropertiesRecord properties, Unit owner, bool isWorldObject)
: base(properties, owner, isWorldObject)
{
m_owner = owner;
Contract.Assert(m_owner);
m_unitTypeMask |= UnitTypeMask.Minion;
m_followAngle = SharedConst.PetFollowAngle;
}
public override void InitStats(uint duration)
{
base.InitStats(duration);
SetReactState(ReactStates.Passive);
SetCreatorGUID(GetOwner().GetGUID());
SetFaction(GetOwner().getFaction());
GetOwner().SetMinion(this, true);
}
public override void RemoveFromWorld()
{
if (!IsInWorld)
return;
GetOwner().SetMinion(this, false);
base.RemoveFromWorld();
}
public bool IsGuardianPet()
{
return IsPet() || (m_Properties != null && m_Properties.Control == SummonCategory.Pet);
}
public override Unit GetOwner() { return m_owner; }
public override float GetFollowAngle() { return m_followAngle; }
public void SetFollowAngle(float angle) { m_followAngle = angle; }
// Warlock pets
public bool IsPetImp() { return GetEntry() == (uint)PetEntry.Imp; }
public bool IsPetFelhunter() { return GetEntry() == (uint)PetEntry.FelHunter; }
public bool IsPetVoidwalker() { return GetEntry() == (uint)PetEntry.VoidWalker; }
public bool IsPetSuccubus() { return GetEntry() == (uint)PetEntry.Succubus; }
public bool IsPetDoomguard() { return GetEntry() == (uint)PetEntry.Doomguard; }
public bool IsPetFelguard() { return GetEntry() == (uint)PetEntry.Felguard; }
// Death Knight pets
public bool IsPetGhoul() { return GetEntry() == (uint)PetEntry.Ghoul; } // Ghoul may be guardian or pet
public bool IsPetAbomination() { return GetEntry() == (uint)PetEntry.Abomination; } // Sludge Belcher dk talent
// Shaman pet
public bool IsSpiritWolf() { return GetEntry() == (uint)PetEntry.SpiritWolf; } // Spirit wolf from feral spirits
Unit m_owner;
float m_followAngle;
}
public class Guardian : Minion
{
public Guardian(SummonPropertiesRecord properties, Unit owner, bool isWorldObject)
: base(properties, owner, isWorldObject)
{
m_bonusSpellDamage = 0;
m_unitTypeMask |= UnitTypeMask.Guardian;
if (properties != null && (properties.Title == SummonType.Pet || properties.Control == SummonCategory.Pet))
{
m_unitTypeMask |= UnitTypeMask.ControlableGuardian;
InitCharmInfo();
}
}
public override void InitStats(uint duration)
{
base.InitStats(duration);
InitStatsForLevel(GetOwner().getLevel());
if (GetOwner().IsTypeId(TypeId.Player) && HasUnitTypeMask(UnitTypeMask.ControlableGuardian))
GetCharmInfo().InitCharmCreateSpells();
SetReactState(ReactStates.Aggressive);
}
public override void InitSummon()
{
base.InitSummon();
if (GetOwner().IsTypeId(TypeId.Player) && GetOwner().GetMinionGUID() == GetGUID()
&& GetOwner().GetCharmGUID().IsEmpty())
{
GetOwner().ToPlayer().CharmSpellInitialize();
}
}
// @todo Move stat mods code to pet passive auras
public bool InitStatsForLevel(uint petlevel)
{
CreatureTemplate cinfo = GetCreatureTemplate();
Contract.Assert(cinfo != null);
SetLevel(petlevel);
//Determine pet type
PetType petType = PetType.Max;
if (IsPet() && GetOwner().IsTypeId(TypeId.Player))
{
if (GetOwner().GetClass() == Class.Warlock
|| GetOwner().GetClass() == Class.Shaman // Fire Elemental
|| GetOwner().GetClass() == Class.Deathknight) // Risen Ghoul
{
petType = PetType.Summon;
}
else if (GetOwner().GetClass() == Class.Hunter)
{
petType = PetType.Hunter;
m_unitTypeMask |= UnitTypeMask.HunterPet;
}
else
{
Log.outError(LogFilter.Unit, "Unknown type pet {0} is summoned by player class {1}", GetEntry(), GetOwner().GetClass());
}
}
uint creature_ID = (petType == PetType.Hunter) ? 1 : cinfo.Entry;
SetMeleeDamageSchool((SpellSchools)cinfo.DmgSchool);
SetModifierValue(UnitMods.Armor, UnitModifierType.BaseValue, (float)petlevel * 50);
SetBaseAttackTime(WeaponAttackType.BaseAttack, SharedConst.BaseAttackTime);
SetBaseAttackTime(WeaponAttackType.OffAttack, SharedConst.BaseAttackTime);
SetBaseAttackTime(WeaponAttackType.RangedAttack, SharedConst.BaseAttackTime);
SetFloatValue(UnitFields.ModCastSpeed, 1.0f);
SetFloatValue(UnitFields.ModCastHaste, 1.0f);
//scale
var cFamily = CliDB.CreatureFamilyStorage.LookupByKey(cinfo.Family);
if (cFamily != null && cFamily.MinScale > 0.0f && petType == PetType.Hunter)
{
float scale;
if (getLevel() >= cFamily.MaxScaleLevel)
scale = cFamily.MaxScale;
else if (getLevel() <= cFamily.MinScaleLevel)
scale = cFamily.MinScale;
else
scale = cFamily.MinScale + (float)(getLevel() - cFamily.MinScaleLevel) / cFamily.MaxScaleLevel * (cFamily.MaxScale - cFamily.MinScale);
SetObjectScale(scale);
}
// Resistance
for (int i = (int)SpellSchools.Holy; i < (int)SpellSchools.Max; ++i)
SetModifierValue(UnitMods.ResistanceStart + i, UnitModifierType.BaseValue, cinfo.Resistance[i]);
// Health, Mana or Power, Armor
PetLevelInfo pInfo = Global.ObjectMgr.GetPetLevelInfo(creature_ID, petlevel);
if (pInfo != null) // exist in DB
{
SetCreateHealth(pInfo.health);
SetCreateMana(pInfo.mana);
if (pInfo.armor > 0)
SetModifierValue(UnitMods.Armor, UnitModifierType.BaseValue, pInfo.armor);
for (byte stat = 0; stat < (int)Stats.Max; ++stat)
SetCreateStat((Stats)stat, pInfo.stats[stat]);
}
else // not exist in DB, use some default fake data
{
// remove elite bonuses included in DB values
CreatureBaseStats stats = Global.ObjectMgr.GetCreatureBaseStats(petlevel, cinfo.UnitClass);
SetCreateHealth(stats.BaseHealth[cinfo.HealthScalingExpansion]);
SetCreateMana(stats.BaseMana);
SetCreateStat(Stats.Strength, 22);
SetCreateStat(Stats.Agility, 22);
SetCreateStat(Stats.Stamina, 25);
SetCreateStat(Stats.Intellect, 28);
}
// Power
if (petType == PetType.Hunter) // Hunter pets have focus
SetPowerType(PowerType.Focus);
else if (IsPetGhoul() || IsPetAbomination()) // DK pets have energy
SetPowerType(PowerType.Energy);
else if (IsPetImp() || IsPetFelhunter() || IsPetVoidwalker() || IsPetSuccubus() || IsPetDoomguard() || IsPetFelguard()) // Warlock pets have energy (since 5.x)
SetPowerType(PowerType.Energy);
else
SetPowerType(PowerType.Mana);
// Damage
SetBonusDamage(0);
switch (petType)
{
case PetType.Summon:
{
// the damage bonus used for pets is either fire or shadow damage, whatever is higher
int fire = GetOwner().GetInt32Value(PlayerFields.ModDamageDonePos + (int)SpellSchools.Fire);
int shadow = GetOwner().GetInt32Value(PlayerFields.ModDamageDonePos + (int)SpellSchools.Shadow);
int val = (fire > shadow) ? fire : shadow;
if (val < 0)
val = 0;
SetBonusDamage((int)(val * 0.15f));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, petlevel - (petlevel / 4));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, petlevel + (petlevel / 4));
break;
}
case PetType.Hunter:
{
SetUInt32Value(UnitFields.PetNextLevelExp, (uint)(Global.ObjectMgr.GetXPForLevel(petlevel) * 0.05f));
//these formula may not be correct; however, it is designed to be close to what it should be
//this makes dps 0.5 of pets level
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, petlevel - (petlevel / 4));
//damage range is then petlevel / 2
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, petlevel + (petlevel / 4));
//damage is increased afterwards as strength and pet scaling modify attack power
break;
}
default:
{
switch (GetEntry())
{
case 510: // mage Water Elemental
{
SetBonusDamage((int)(GetOwner().SpellBaseDamageBonusDone(SpellSchoolMask.Frost) * 0.33f));
break;
}
case 1964: //force of nature
{
if (pInfo == null)
SetCreateHealth(30 + 30 * petlevel);
float bonusDmg = GetOwner().SpellBaseDamageBonusDone(SpellSchoolMask.Nature) * 0.15f;
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, petlevel * 2.5f - (petlevel / 2) + bonusDmg);
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, petlevel * 2.5f + (petlevel / 2) + bonusDmg);
break;
}
case 15352: //earth elemental 36213
{
if (pInfo == null)
SetCreateHealth(100 + 120 * petlevel);
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, petlevel - (petlevel / 4));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, petlevel + (petlevel / 4));
break;
}
case 15438: //fire elemental
{
if (pInfo == null)
{
SetCreateHealth(40 * petlevel);
SetCreateMana(28 + 10 * petlevel);
}
SetBonusDamage((int)(GetOwner().SpellBaseDamageBonusDone(SpellSchoolMask.Fire) * 0.5f));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, petlevel * 4 - petlevel);
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, petlevel * 4 + petlevel);
break;
}
case 19668: // Shadowfiend
{
if (pInfo == null)
{
SetCreateMana(28 + 10 * petlevel);
SetCreateHealth(28 + 30 * petlevel);
}
int bonus_dmg = (int)(GetOwner().SpellBaseDamageBonusDone(SpellSchoolMask.Shadow) * 0.3f);
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, (petlevel * 4 - petlevel) + bonus_dmg);
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, (petlevel * 4 + petlevel) + bonus_dmg);
break;
}
case 19833: //Snake Trap - Venomous Snake
{
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, (petlevel / 2) - 25);
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, (petlevel / 2) - 18);
break;
}
case 19921: //Snake Trap - Viper
{
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, petlevel / 2 - 10);
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, petlevel / 2);
break;
}
case 29264: // Feral Spirit
{
if (pInfo == null)
SetCreateHealth(30 * petlevel);
// wolf attack speed is 1.5s
SetBaseAttackTime(WeaponAttackType.BaseAttack, cinfo.BaseAttackTime);
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, (petlevel * 4 - petlevel));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, (petlevel * 4 + petlevel));
SetModifierValue(UnitMods.Armor, UnitModifierType.BaseValue, GetOwner().GetArmor() * 0.35f); // Bonus Armor (35% of player armor)
SetModifierValue(UnitMods.StatStamina, UnitModifierType.BaseValue, GetOwner().GetStat(Stats.Stamina) * 0.3f); // Bonus Stamina (30% of player stamina)
if (!HasAura(58877))//prevent apply twice for the 2 wolves
AddAura(58877, this);//Spirit Hunt, passive, Spirit Wolves' attacks heal them and their master for 150% of damage done.
break;
}
case 31216: // Mirror Image
{
SetBonusDamage((int)(GetOwner().SpellBaseDamageBonusDone(SpellSchoolMask.Frost) * 0.33f));
SetDisplayId(GetOwner().GetDisplayId());
if (pInfo == null)
{
SetCreateMana(28 + 30 * petlevel);
SetCreateHealth(28 + 10 * petlevel);
}
break;
}
case 27829: // Ebon Gargoyle
{
if (pInfo == null)
{
SetCreateMana(28 + 10 * petlevel);
SetCreateHealth(28 + 30 * petlevel);
}
SetBonusDamage((int)(GetOwner().GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * 0.5f));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, petlevel - (petlevel / 4));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, petlevel + (petlevel / 4));
break;
}
case 28017: // Bloodworms
{
SetCreateHealth(4 * petlevel);
SetBonusDamage((int)(GetOwner().GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * 0.006f));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage, petlevel - 30 - (petlevel / 4));
SetBaseWeaponDamage(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage, petlevel - 30 + (petlevel / 4));
}
break;
}
break;
}
}
UpdateAllStats();
SetFullHealth();
SetFullPower(PowerType.Mana);
return true;
}
const int ENTRY_IMP = 416;
const int ENTRY_VOIDWALKER = 1860;
const int ENTRY_SUCCUBUS = 1863;
const int ENTRY_FELHUNTER = 417;
const int ENTRY_FELGUARD = 17252;
const int ENTRY_WATER_ELEMENTAL = 510;
const int ENTRY_TREANT = 1964;
const int ENTRY_FIRE_ELEMENTAL = 15438;
const int ENTRY_GHOUL = 26125;
const int ENTRY_BLOODWORM = 28017;
public override bool UpdateStats(Stats stat)
{
float value = GetTotalStatValue(stat);
ApplyStatBuffMod(stat, m_statFromOwner[(int)stat], false);
float ownersBonus = 0.0f;
Unit owner = GetOwner();
// Handle Death Knight Glyphs and Talents
float mod = 0.75f;
if (IsPetGhoul() && (stat == Stats.Stamina || stat == Stats.Strength))
{
switch (stat)
{
case Stats.Stamina:
mod = 0.3f;
break; // Default Owner's Stamina scale
case Stats.Strength:
mod = 0.7f;
break; // Default Owner's Strength scale
default: break;
}
ownersBonus = owner.GetStat(stat) * mod;
value += ownersBonus;
}
else if (stat == Stats.Stamina)
{
ownersBonus = MathFunctions.CalculatePct(owner.GetStat(Stats.Stamina), 30);
value += ownersBonus;
}
//warlock's and mage's pets gain 30% of owner's intellect
else if (stat == Stats.Intellect)
{
if (owner.GetClass() == Class.Warlock || owner.GetClass() == Class.Mage)
{
ownersBonus = MathFunctions.CalculatePct(owner.GetStat(stat), 30);
value += ownersBonus;
}
}
SetStat(stat, (int)value);
m_statFromOwner[(int)stat] = ownersBonus;
ApplyStatBuffMod(stat, m_statFromOwner[(int)stat], true);
switch (stat)
{
case Stats.Strength:
UpdateAttackPowerAndDamage();
break;
case Stats.Agility:
UpdateArmor();
break;
case Stats.Stamina:
UpdateMaxHealth();
break;
case Stats.Intellect:
UpdateMaxPower(PowerType.Mana);
break;
default:
break;
}
return true;
}
public override bool UpdateAllStats()
{
UpdateMaxHealth();
for (var i = Stats.Strength; i < Stats.Max; ++i)
UpdateStats(i);
for (var i = PowerType.Mana; i < PowerType.Max; ++i)
UpdateMaxPower(i);
UpdateAllResistances();
return true;
}
public override void UpdateResistances(SpellSchools school)
{
if (school > SpellSchools.Normal)
{
float value = GetTotalAuraModValue(UnitMods.ResistanceStart + (int)school);
// hunter and warlock pets gain 40% of owner's resistance
if (IsPet())
value += MathFunctions.CalculatePct(GetOwner().GetResistance(school), 40);
SetResistance(school, (int)value);
}
else
UpdateArmor();
}
public override void UpdateArmor()
{
float value = 0.0f;
float bonus_armor = 0.0f;
UnitMods unitMod = UnitMods.Armor;
// hunter pets gain 35% of owner's armor value, warlock pets gain 100% of owner's armor
if (IsHunterPet())
bonus_armor = MathFunctions.CalculatePct(GetOwner().GetArmor(), 70);
else if (IsPet())
bonus_armor = GetOwner().GetArmor();
value = GetModifierValue(unitMod, UnitModifierType.BaseValue);
value *= GetModifierValue(unitMod, UnitModifierType.BasePCT);
value += GetModifierValue(unitMod, UnitModifierType.TotalValue) + bonus_armor;
value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT);
SetArmor((int)value);
}
public override void UpdateMaxHealth()
{
UnitMods unitMod = UnitMods.Health;
float stamina = GetStat(Stats.Stamina) - GetCreateStat(Stats.Stamina);
float multiplicator;
switch (GetEntry())
{
case ENTRY_IMP:
multiplicator = 8.4f;
break;
case ENTRY_VOIDWALKER:
multiplicator = 11.0f;
break;
case ENTRY_SUCCUBUS:
multiplicator = 9.1f;
break;
case ENTRY_FELHUNTER:
multiplicator = 9.5f;
break;
case ENTRY_FELGUARD:
multiplicator = 11.0f;
break;
case ENTRY_BLOODWORM:
multiplicator = 1.0f;
break;
default:
multiplicator = 10.0f;
break;
}
float value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetCreateHealth();
value *= GetModifierValue(unitMod, UnitModifierType.BasePCT);
value += GetModifierValue(unitMod, UnitModifierType.TotalValue) + stamina * multiplicator;
value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT);
SetMaxHealth((uint)value);
}
public override void UpdateMaxPower(PowerType power)
{
if (GetPowerIndex(power) == (uint)PowerType.Max)
return;
UnitMods unitMod = UnitMods.PowerStart + (int)power;
float value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetCreatePowers(power);
value *= GetModifierValue(unitMod, UnitModifierType.BasePCT);
value += GetModifierValue(unitMod, UnitModifierType.TotalValue);
value *= GetModifierValue(unitMod, UnitModifierType.TotalPCT);
SetMaxPower(power, (int)value);
}
public override void UpdateAttackPowerAndDamage(bool ranged = false)
{
if (ranged)
return;
float val = 0.0f;
float bonusAP = 0.0f;
UnitMods unitMod = UnitMods.AttackPower;
if (GetEntry() == ENTRY_IMP) // imp's attack power
val = GetStat(Stats.Strength) - 10.0f;
else
val = 2 * GetStat(Stats.Strength) - 20.0f;
Unit owner = GetOwner();
if (owner != null && owner.IsTypeId(TypeId.Player))
{
if (IsHunterPet()) //hunter pets benefit from owner's attack power
{
float mod = 1.0f; //Hunter contribution modifier
bonusAP = owner.GetTotalAttackPowerValue(WeaponAttackType.RangedAttack) * 0.22f * mod;
SetBonusDamage((int)(owner.GetTotalAttackPowerValue(WeaponAttackType.RangedAttack) * 0.1287f * mod));
}
else if (IsPetGhoul()) //ghouls benefit from deathknight's attack power (may be summon pet or not)
{
bonusAP = owner.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * 0.22f;
SetBonusDamage((int)(owner.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * 0.1287f));
}
else if (IsSpiritWolf()) //wolf benefit from shaman's attack power
{
float dmg_multiplier = 0.31f;
bonusAP = owner.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * dmg_multiplier;
SetBonusDamage((int)(owner.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * dmg_multiplier));
}
//demons benefit from warlocks shadow or fire damage
else if (IsPet())
{
int fire = owner.GetInt32Value(PlayerFields.ModDamageDonePos + (int)SpellSchools.Fire) - owner.GetInt32Value(PlayerFields.ModDamageDoneNeg + (int)SpellSchools.Fire);
int shadow = owner.GetInt32Value(PlayerFields.ModDamageDonePos + (int)SpellSchools.Shadow) - owner.GetInt32Value(PlayerFields.ModDamageDoneNeg + (int)SpellSchools.Shadow);
int maximum = (fire > shadow) ? fire : shadow;
if (maximum < 0)
maximum = 0;
SetBonusDamage((int)(maximum * 0.15f));
bonusAP = maximum * 0.57f;
}
//water elementals benefit from mage's frost damage
else if (GetEntry() == ENTRY_WATER_ELEMENTAL)
{
int frost = owner.GetInt32Value(PlayerFields.ModDamageDonePos + (int)SpellSchools.Frost) - owner.GetInt32Value(PlayerFields.ModDamageDoneNeg + (int)SpellSchools.Frost);
if (frost < 0)
frost = 0;
SetBonusDamage((int)(frost * 0.4f));
}
}
SetModifierValue(UnitMods.AttackPower, UnitModifierType.BaseValue, val + bonusAP);
//in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
float base_attPower = GetModifierValue(unitMod, UnitModifierType.BaseValue) * GetModifierValue(unitMod, UnitModifierType.BasePCT);
float attPowerMultiplier = GetModifierValue(unitMod, UnitModifierType.TotalPCT) - 1.0f;
//UNIT_FIELD_(RANGED)_ATTACK_POWER field
SetInt32Value(UnitFields.AttackPower, (int)base_attPower);
//UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
SetFloatValue(UnitFields.AttackPowerMultiplier, attPowerMultiplier);
//automatically update weapon damage after attack power modification
UpdateDamagePhysical(WeaponAttackType.BaseAttack);
}
public override void UpdateDamagePhysical(WeaponAttackType attType)
{
if (attType > WeaponAttackType.BaseAttack)
return;
float bonusDamage = 0.0f;
if (GetOwner().IsTypeId(TypeId.Player))
{
//force of nature
if (GetEntry() == ENTRY_TREANT)
{
int spellDmg = GetOwner().GetInt32Value(PlayerFields.ModDamageDonePos + (int)SpellSchools.Nature) - GetOwner().GetInt32Value(PlayerFields.ModDamageDoneNeg + (int)SpellSchools.Nature);
if (spellDmg > 0)
bonusDamage = spellDmg * 0.09f;
}
//greater fire elemental
else if (GetEntry() == ENTRY_FIRE_ELEMENTAL)
{
int spellDmg = GetOwner().GetInt32Value(PlayerFields.ModDamageDonePos + (int)SpellSchools.Fire) - GetOwner().GetInt32Value(PlayerFields.ModDamageDoneNeg + (int)SpellSchools.Fire);
if (spellDmg > 0)
bonusDamage = spellDmg * 0.4f;
}
}
UnitMods unitMod = UnitMods.DamageMainHand;
float att_speed = GetBaseAttackTime(WeaponAttackType.BaseAttack) / 1000.0f;
float base_value = GetModifierValue(unitMod, UnitModifierType.BaseValue) + GetTotalAttackPowerValue(attType) / 3.5f * att_speed + bonusDamage;
float base_pct = GetModifierValue(unitMod, UnitModifierType.BasePCT);
float total_value = GetModifierValue(unitMod, UnitModifierType.TotalValue);
float total_pct = GetModifierValue(unitMod, UnitModifierType.TotalPCT);
float weapon_mindamage = GetWeaponDamageRange(WeaponAttackType.BaseAttack, WeaponDamageRange.MinDamage);
float weapon_maxdamage = GetWeaponDamageRange(WeaponAttackType.BaseAttack, WeaponDamageRange.MaxDamage);
float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
SetStatFloatValue(UnitFields.MinDamage, mindamage);
SetStatFloatValue(UnitFields.MaxDamage, maxdamage);
}
void SetBonusDamage(int damage)
{
m_bonusSpellDamage = damage;
if (GetOwner().IsTypeId(TypeId.Player))
GetOwner().SetUInt32Value(PlayerFields.PetSpellPower, (uint)damage);
}
public int GetBonusDamage() { return m_bonusSpellDamage; }
int m_bonusSpellDamage;
float[] m_statFromOwner = new float[(int)Stats.Max];
}
public class Puppet : Minion
{
public Puppet(SummonPropertiesRecord properties, Unit owner)
: base(properties, owner, false)
{
Contract.Assert(owner.IsTypeId(TypeId.Player));
m_unitTypeMask |= UnitTypeMask.Puppet;
}
public override void InitStats(uint duration)
{
base.InitStats(duration);
SetLevel(GetOwner().getLevel());
SetReactState(ReactStates.Passive);
}
public override void InitSummon()
{
base.InitSummon();
if (!SetCharmedBy(GetOwner(), CharmType.Possess))
Contract.Assert(false);
}
public override void Update(uint diff)
{
base.Update(diff);
//check if caster is channelling?
if (IsInWorld)
{
if (!IsAlive())
{
UnSummon();
// @todo why long distance .die does not remove it
}
}
}
public override void RemoveFromWorld()
{
if (!IsInWorld)
return;
RemoveCharmedBy(null);
base.RemoveFromWorld();
}
}
public class ForcedUnsummonDelayEvent : BasicEvent
{
public ForcedUnsummonDelayEvent(TempSummon owner)
{
m_owner = owner;
}
public override bool Execute(ulong e_time, uint p_time)
{
m_owner.UnSummon();
return true;
}
TempSummon m_owner;
}
public class TempSummonData
{
public uint entry; // Entry of summoned creature
public Position pos; // Position, where should be creature spawned
public TempSummonType type; // Summon type, see TempSummonType for available types
public uint time; // Despawn time, usable only with certain temp summon types
}
enum PetEntry
{
// Warlock pets
Imp = 416,
FelHunter = 691,
VoidWalker = 1860,
Succubus = 1863,
Doomguard = 18540,
Felguard = 30146,
// Death Knight pets
Ghoul = 26125,
Abomination = 106848,
// Shaman pet
SpiritWolf = 29264
}
}