/// <summary> /// Called every ~5 seconds for WorldObject base /// </summary> public virtual void Heartbeat(double currentUnixTime) { if (EnchantmentManager.HasEnchantments) { EnchantmentManager.HeartBeat(); } SetProperty(PropertyFloat.HeartbeatTimestamp, currentUnixTime); NextHeartbeatTime = currentUnixTime + CachedHeartbeatInterval; }
public int GetHealingResistRating() { // debuff? var healResistRating = HealingResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.HealingResistRating); return(healResistRating + enchantments); }
public int GetDotResistanceRating() { // get from base properties (monsters)? var dotResistRating = DotResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.DotResistRating); return(dotResistRating + enchantments); }
/// <summary> /// Called every ~5 seconds for WorldObject base /// </summary> public virtual void HeartBeat(double currentUnixTime) { Generator_HeartBeat(); EmoteManager.HeartBeat(); EnchantmentManager.HeartBeat(); cachedHeartbeatTimestamp = currentUnixTime; SetProperty(PropertyFloat.HeartbeatTimestamp, currentUnixTime); }
/// <summary> /// Called every ~5 seconds for Players /// </summary> public override void HeartBeat() { NotifyLandblocks(); EnchantmentManager.HeartBeat(); VitalTick(); ManaConsumersTick(); ItemEnchantmentTick(); QueueNextHeartBeat(); }
/// <summary> /// Broadcasts the player death animation, updates vitae, and sends network messages for player death /// Queues the action to call TeleportOnDeath and enter portal space soon /// </summary> protected override void Die(WorldObject lastDamager, WorldObject topDamager) { UpdateVital(Health, 0); NumDeaths++; DeathLevel = Level; // for calculating vitae XP VitaeCpPool = 0; // reset vitae XP earned // killer = top damager for looting rights if (topDamager != null) { Killer = topDamager.Guid.Full; } // broadcast death animation var deathAnim = new Motion(MotionStance.NonCombat, MotionCommand.Dead); EnqueueBroadcastMotion(deathAnim); // killer death message = last damager var killerMsg = lastDamager != null ? " to " + lastDamager.Name : ""; var currentDeathMessage = $"died{killerMsg}."; // create network messages for player death var msgHealthUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, 0); // TODO: death sounds? seems to play automatically in client // var msgDeathSound = new GameMessageSound(Guid, Sound.Death1, 1.0f); var msgNumDeaths = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.NumDeaths, NumDeaths ?? 0); var msgDeathLevel = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.DeathLevel, DeathLevel ?? 0); var msgVitaeCpPool = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.VitaeCpPool, VitaeCpPool.Value); var msgPurgeEnchantments = new GameEventMagicPurgeEnchantments(Session); // update vitae var vitae = EnchantmentManager.UpdateVitae(); var spellID = (uint)SpellId.Vitae; var spell = new Spell(spellID); var vitaeEnchantment = new Enchantment(this, Guid, spellID, spell.Duration, 0, (EnchantmentMask)spell.StatModType, vitae); var msgVitaeEnchantment = new GameEventMagicUpdateEnchantment(Session, vitaeEnchantment); // send network messages for player death Session.Network.EnqueueSend(msgHealthUpdate, msgNumDeaths, msgDeathLevel, msgVitaeCpPool, msgPurgeEnchantments, msgVitaeEnchantment); // wait for the death animation to finish var dieChain = new ActionChain(); var animLength = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.Dead); dieChain.AddDelaySeconds(animLength + 1.0f); // enter portal space dieChain.AddAction(this, CreateCorpse); dieChain.AddAction(this, TeleportOnDeath); dieChain.EnqueueChain(); }
public int GetNetherResistRating() { // wiki calls this dot resistance, does this affect dirty fighting bleed attack? // get from base properties (monsters)? var netherResistRating = NetherResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.NetherResistRating); return(netherResistRating + enchantments); }
/// <summary> /// Updates a particular vital according to regeneration rate /// </summary> /// <param name="vital">The vital stat to update (health/stamina/mana)</param> public void VitalHeartBeat(CreatureVital vital) { // Current and MaxValue are properties and include overhead in getting their values. We cache them so we only hit the overhead once. var vitalCurrent = vital.Current; var vitalMax = vital.MaxValue; if (vitalCurrent == vitalMax) { return; } if (vitalCurrent > vitalMax) { UpdateVital(vital, vitalMax); return; } if (vital.RegenRate == 0.0) { return; } // take attributes into consideration (strength, endurance) var attributeMod = GetAttributeMod(vital); // take stance into consideration (combat, crouch, sitting, sleeping) var stanceMod = GetStanceMod(vital); // take enchantments into consideration: // (regeneration / rejuvenation / mana renewal / etc.) var enchantmentMod = EnchantmentManager.GetRegenerationMod(vital); // cap rate? var currentTick = vital.RegenRate * attributeMod * stanceMod * enchantmentMod; // add in partially accumulated / rounded vitals from previous tick(s) var totalTick = currentTick + vital.PartialRegen; // accumulate partial vital rates between ticks var intTick = (int)totalTick; vital.PartialRegen = totalTick - intTick; if (intTick > 0) { UpdateVitalDelta(vital, intTick); if (vital.Vital == PropertyAttribute2nd.MaxHealth) { DamageHistory.OnHeal((uint)intTick); } } //Console.WriteLine($"VitalTick({vital.Vital.ToSentence()}): attributeMod={attributeMod}, stanceMod={stanceMod}, enchantmentMod={enchantmentMod}, regenRate={vital.RegenRate}, currentTick={currentTick}, totalTick={totalTick}, accumulated={vital.PartialRegen}"); }
public int GetPKDamageResistRating() { var pkDamageResistRating = PKDamageResistRating ?? 0; // additive enchantments? var enchantments = EnchantmentManager.GetRating(PropertyInt.PKDamageResistRating); // equipment ratings var equipment = GetEquippedItemsRatingSum(PropertyInt.GearPKDamageResistRating); return(pkDamageResistRating + equipment + enchantments); }
public void AuditItemSpells() { // cleans up bugged chars with dangling item set spells // from previous bugs // get active item enchantments var enchantments = Biota.GetEnchantments(BiotaDatabaseLock).Where(i => i.Duration == -1 && i.SpellId != (int)SpellId.Vitae).ToList(); foreach (var enchantment in enchantments) { // if this item is not equipped, remove enchantment if (!EquippedObjects.TryGetValue(new ObjectGuid(enchantment.CasterObjectId), out var item)) { var spell = new Spell(enchantment.SpellId, false); log.Error($"{Name}.AuditItemSpells(): removing spell {spell.Name} from non-equipped item"); EnchantmentManager.Dispel(enchantment); continue; } // is this item part of a set? if (!item.HasItemSet) { continue; } // get all of the equipped items in this set var setItems = EquippedObjects.Values.Where(i => i.HasItemSet && i.EquipmentSetId == item.EquipmentSetId).ToList(); // get all of the spells currently active from this set var currentSpells = GetSpellSet((EquipmentSet)item.EquipmentSetId, setItems); // get all of the spells possible for this item set var possibleSpells = GetSpellSetAll((EquipmentSet)item.EquipmentSetId); // get the difference between them var inactiveSpells = possibleSpells.Except(currentSpells).ToList(); // remove any item set spells that shouldn't be active foreach (var inactiveSpell in inactiveSpells) { var removeSpells = enchantments.Where(i => i.SpellSetId == (uint)item.EquipmentSetId && i.SpellId == inactiveSpell.Id).ToList(); foreach (var removeSpell in removeSpells) { log.Error($"{Name}.AuditItemSpells(): removing spell {inactiveSpell.Name} from {item.EquipmentSetId}"); EnchantmentManager.Dispel(removeSpell); } } } }
public int GetNetherResistRating() { // there is a property defined for this, // but does anything use this? // get from base properties (monsters)? var netherResistRating = NetherResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.NetherResistRating); return(netherResistRating + enchantments); }
public int GetCritResistRating() { // crit resist chance // get from base properties (monsters)? var critResistRating = CritResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.CritResistRating); // no augs / lum augs? return(critResistRating + enchantments); }
public int GetLifeResistRating() { // only affects health drain? // only cast by Sigil of Perserverance (Aetheria)? // get from base properties (monsters)? var lifeResistRating = LifeResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.LifeResistRating); return(lifeResistRating + enchantments); }
public int GetLifeResistRating() { // drain resistance? // Drain Resistances - allows one to partially resist drain health/stamina/mana and harm attacks (not including other life transfer spells). // get from base properties (monsters)? var lifeResistRating = LifeResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.LifeResistRating); return(lifeResistRating + enchantments); }
public int GetCritResistRating() { // crit resist chance // get from base properties (monsters)? var critResistRating = CritResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.CritResistRating); // equipment ratings var equipment = GetEquippedItemsRatingSum(PropertyInt.GearCritResist); // no augs / lum augs? return(critResistRating + equipment + enchantments); }
/// <summary> /// Handles updating the vitae penalty through earned XP /// </summary> /// <param name="amount">The amount of XP to apply to the vitae penalty</param> private void UpdateXpVitae(long amount) { var vitaePenalty = EnchantmentManager.GetVitae().StatModValue; var startPenalty = vitaePenalty; var maxPool = (int)VitaeCPPoolThreshold(vitaePenalty, DeathLevel.Value); var curPool = VitaeCpPool + amount; while (curPool >= maxPool) { curPool -= maxPool; vitaePenalty = EnchantmentManager.ReduceVitae(); if (vitaePenalty == 1.0f) { break; } maxPool = (int)VitaeCPPoolThreshold(vitaePenalty, DeathLevel.Value); } VitaeCpPool = (int)curPool; Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.VitaeCpPool, VitaeCpPool.Value)); if (vitaePenalty != startPenalty) { Session.Network.EnqueueSend(new GameMessageSystemChat("Your experience has reduced your Vitae penalty!", ChatMessageType.Magic)); EnchantmentManager.SendUpdateVitae(); } if (vitaePenalty.EpsilonEquals(1.0f) || vitaePenalty > 1.0f) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(2.0f); actionChain.AddAction(this, () => { var vitae = EnchantmentManager.GetVitae(); if (vitae != null) { var curPenalty = vitae.StatModValue; if (curPenalty.EpsilonEquals(1.0f) || curPenalty > 1.0f) { EnchantmentManager.RemoveVitae(); } } }); actionChain.EnqueueChain(); } }
public int GetCritResistRating() { // crit resist chance // get from base properties (monsters)? var critResistRating = CritResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.CritResistRating); // equipment ratings // TODO: caching? var equipment = EquippedObjects.Values.Sum(i => i.GearCritResist ?? 0); // no augs / lum augs? return(critResistRating + equipment + enchantments); }
/// <summary> /// Returns the multiplier to XP and Luminance from Trinkets and Augmentations /// </summary> public float GetXPAndLuminanceModifier(XpType xpType) { var enchantmentBonus = EnchantmentManager.GetXPBonus(); var augBonus = 0.0f; if (xpType == XpType.Kill && AugmentationBonusXp > 0) { augBonus = AugmentationBonusXp * 0.05f; } var modifier = 1.0f + enchantmentBonus + augBonus; //Console.WriteLine($"XPAndLuminanceModifier: {modifier}"); return(modifier); }
public int GetCritDamageResistRating() { // get from base properties (monsters)? var critDamageResistRating = CritDamageResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.CritDamageResistRating); var lumAugBonus = 0; if (this is Player player) { lumAugBonus = player.LumAugCritReductionRating; } return(critDamageResistRating + enchantments + lumAugBonus); }
public int GetHealingBoostRating() { // get from base properties (monsters)? var healBoostRating = HealingBoostRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.HealingBoostRating); var lumAugBonus = 0; if (this is Player player) { lumAugBonus = player.LumAugHealingRating; } return(healBoostRating + enchantments + lumAugBonus); }
/// <summary> /// Inflicts vitae /// </summary> public void InflictVitaePenalty(int amount = 5) { DeathLevel = Level; // for calculating vitae XP VitaeCpPool = 0; // reset vitae XP earned var msgDeathLevel = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.DeathLevel, DeathLevel ?? 0); var msgVitaeCpPool = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.VitaeCpPool, VitaeCpPool.Value); Session.Network.EnqueueSend(msgDeathLevel, msgVitaeCpPool); var vitae = EnchantmentManager.UpdateVitae(); var spellID = (uint)SpellId.Vitae; var spell = new Spell(spellID); var vitaeEnchantment = new Enchantment(this, Guid.Full, spellID, 0, (EnchantmentMask)spell.StatModType, vitae); Session.Network.EnqueueSend(new GameEventMagicUpdateEnchantment(Session, vitaeEnchantment)); }
/// <summary> /// A player earns XP through natural progression, ie. kills and quests completed /// </summary> /// <param name="amount">The amount of XP being added</param> /// <param name="xpType">The source of XP being added</param> /// <param name="shareable">True if this XP can be shared with Fellowship</param> public void EarnXP(long amount, XpType xpType, ShareType shareType = ShareType.All) { //Console.WriteLine($"{Name}.EarnXP({amount}, {sharable}, {fixedAmount})"); // apply xp modifier var modifier = PropertyManager.GetDouble("xp_modifier").Item; var enchantment = EnchantmentManager.GetXPMod(); var m_amount = (long)Math.Round(amount * enchantment * modifier); if (m_amount < 0) { log.Warn($"{Name}.EarnXP({amount}, {shareType})"); log.Warn($"modifier: {modifier}, enchantment: {enchantment}, m_amount: {m_amount}"); return; } GrantXP(m_amount, xpType, shareType); }
public int GetArmorVsType(DamageType damageType, int armorVsType) { // TODO: refactor this class var preScaled = (float)BaseArmorMod / Biota.BaseArmor; //var resistance = (float)armorVsType / Biota.BaseArmor; var resistance = (float)armorVsType / Biota.BaseArmor * preScaled; if (double.IsNaN(resistance)) { resistance = 1.0f; } float mod; var spellVuln = IgnoreMagicResist ? 1.0f : EnchantmentManager.GetVulnerabilityResistanceMod(damageType); var spellProt = IgnoreMagicResist ? 1.0f : EnchantmentManager.GetProtectionResistanceMod(damageType); if (WeaponResistanceMod > spellVuln) { mod = WeaponResistanceMod * spellProt; } else { mod = spellVuln * spellProt; } var baseArmorMod = BaseArmorMod; var resistanceMod = resistance / mod; if (baseArmorMod < 0) { resistanceMod = 1.0f + (1.0f - resistanceMod); } /*Console.WriteLine("BaseArmor: " + Biota.BaseArmor); * Console.WriteLine("BaseArmorMod: " + baseArmorMod); * Console.WriteLine("Resistance: " + resistance); * Console.WriteLine("ResistanceMod: " + resistanceMod);*/ return((int)Math.Round(baseArmorMod * resistanceMod)); }
public void EquipDequipItemFromSet(WorldObject item, List <Spell> spells, List <Spell> prevSpells) { // compare these 2 spell sets - // see which spells are being added, and which are being removed var addSpells = spells.Except(prevSpells); var removeSpells = prevSpells.Except(spells); // set spells are not affected by mana // if it's equipped, it's active. foreach (var spell in removeSpells) { EnchantmentManager.Dispel(EnchantmentManager.GetEnchantment(spell.Id, item.EquipmentSetId.Value)); } foreach (var spell in addSpells) { CreateItemSpell(item, spell.Id); } }
/// <summary> /// Called every ~5 seconds for WorldObject base /// </summary> public virtual void Heartbeat(double currentUnixTime) { if (EnchantmentManager.HasEnchantments) { EnchantmentManager.HeartBeat(CachedHeartbeatInterval); } if (RemainingLifespan != null) { RemainingLifespan -= (int)CachedHeartbeatInterval; if (RemainingLifespan <= 0) { DeleteObject(); } } SetProperty(PropertyFloat.HeartbeatTimestamp, currentUnixTime); NextHeartbeatTime = currentUnixTime + CachedHeartbeatInterval; }
public int GetCritDamageRating() { // get from base properties (monsters)? var critDamageRating = CritDamageRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.CritDamageRating); // augmentations var augBonus = 0; var lumAugBonus = 0; if (this is Player player) { augBonus = player.AugmentationCriticalPower * 3; lumAugBonus = player.LumAugCritDamageRating; } return(critDamageRating + enchantments + augBonus + lumAugBonus); }
public int GetCritRating() { // crit chance // get from base properties (monsters)? var critChanceRating = CritRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.CritRating); // augmentations var augBonus = 0; if (this is Player player) { augBonus = player.AugmentationCriticalExpertise; } return(critChanceRating + enchantments + augBonus); }
public int GetCritDamageResistRating() { // get from base properties (monsters)? var critDamageResistRating = CritDamageResistRating ?? 0; // additive enchantments var enchantments = EnchantmentManager.GetRating(PropertyInt.CritDamageResistRating); // equipment ratings // TODO: caching? var equipment = EquippedObjects.Values.Sum(i => i.GearCritDamageResist ?? 0); var lumAugBonus = 0; if (this is Player player) { lumAugBonus = player.LumAugCritReductionRating; } return(critDamageResistRating + equipment + enchantments + lumAugBonus); }
public float GetEffectiveArmorVsType(DamageType damageType, List <WorldObject> armorLayers, WorldObject damageSource, float armorRendingMod = 1.0f) { var ignoreMagicArmor = damageSource != null ? damageSource.IgnoreMagicArmor : false; var ignoreMagicResist = damageSource != null ? damageSource.IgnoreMagicResist : false; // get base AL / RL var enchantmentMod = ignoreMagicResist ? 0 : EnchantmentManager.GetBodyArmorMod(); var baseArmorMod = (float)(Biota.BaseArmor + enchantmentMod); // handle armor rending mod here? //if (baseArmorMod > 0) //baseArmorMod *= armorRendingMod; // for creatures, can this be modified via enchantments? var armorVsType = Creature.GetArmorVsType(damageType); // handle negative baseArmorMod? if (baseArmorMod < 0) { armorVsType = 1.0f + (1.0f - armorVsType); } var effectiveAL = (float)(baseArmorMod * armorVsType); // handle monsters w/ multiple layers of armor foreach (var armorLayer in armorLayers) { effectiveAL += GetArmorMod(armorLayer, damageSource, damageType); } // Armor Rending reduces physical armor too? if (effectiveAL > 0) { effectiveAL *= armorRendingMod; } return(effectiveAL); }
/// <summary> /// Player Death/Kill, use this to kill a session's player /// </summary> /// <remarks> /// TODO: /// 1. Find the best vitae formula and add vitae /// 2. Generate the correct death message, or have it passed in as a parameter. /// 3. Find the correct player death noise based on the player model and play on death. /// 4. Determine if we need to Send Queued Action for Lifestone Materialize, after Action Location. /// 5. Find the health after death formula and Set the correct health /// </remarks> private void OnKill(Session killerSession) { ObjectGuid killerId = killerSession.Player.Guid; IsAlive = false; Health.Current = 0; // Set the health to zero NumDeaths++; // Increase the NumDeaths counter DeathLevel = Level; // For calculating vitae XP VitaeCpPool = 0; // Set vitae XP // TODO: Generate a death message based on the damage type to pass in to each death message: string currentDeathMessage = $"died to {killerSession.Player.Name}."; // Send Vicitim Notification, or "Your Death" event to the client: // create and send the client death event, GameEventYourDeath var msgYourDeath = new GameEventYourDeath(Session, $"You have {currentDeathMessage}"); var msgNumDeaths = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.NumDeaths, NumDeaths ?? 0); var msgDeathLevel = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.DeathLevel, DeathLevel ?? 0); var msgVitaeCpPool = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.VitaeCpPool, VitaeCpPool.Value); var msgPurgeEnchantments = new GameEventPurgeAllEnchantments(Session); // var msgDeathSound = new GameMessageSound(Guid, Sound.Death1, 1.0f); // handle vitae var vitae = EnchantmentManager.UpdateVitae(); var spellID = (uint)Network.Enum.Spell.Vitae; var spellBase = DatManager.PortalDat.SpellTable.Spells[spellID]; var spell = DatabaseManager.World.GetCachedSpell(spellID); var vitaeEnchantment = new Enchantment(this, spellID, (double)spell.Duration, 0, spell.StatModType, vitae); var msgVitaeEnchantment = new GameEventMagicUpdateEnchantment(Session, vitaeEnchantment); var msgHealthUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, 0); // Send first death message group Session.Network.EnqueueSend(msgHealthUpdate, msgYourDeath, msgNumDeaths, msgDeathLevel, msgVitaeCpPool, msgPurgeEnchantments, msgVitaeEnchantment); // Broadcast the 019E: Player Killed GameMessage ActionBroadcastKill($"{Name} has {currentDeathMessage}", Guid, killerId); }