/// <summary> /// Applies some amount of damage to this monster from source /// </summary> /// <param name="source">The attacker / source of damage</param> /// <param name="amount">The amount of damage rounded</param> public virtual uint TakeDamage(WorldObject source, DamageType damageType, float amount, bool crit = false) { var tryDamage = (int)Math.Round(amount); var damage = -UpdateVitalDelta(Health, -tryDamage); // TODO: update monster stamina? // source should only be null for combined DoT ticks from multiple sources if (source != null) { if (damage >= 0) { DamageHistory.Add(source, damageType, (uint)damage); } else { DamageHistory.OnHeal((uint)-damage); } } if (Health.Current <= 0) { OnDeath(DamageHistory.LastDamager, damageType, crit); Die(); } return((uint)Math.Max(0, damage)); }
/// <summary> /// Applies damages to a player from a physical damage source /// </summary> public void TakeDamage(WorldObject source, DamageType damageType, float _amount, BodyPart bodyPart, bool crit = false) { if (Invincible ?? false) { return; } // check lifestone protection if (UnderLifestoneProtection) { HandleLifestoneProtection(); return; } var amount = (uint)Math.Round(_amount); var percent = (float)amount / Health.MaxValue; // update health var damageTaken = (uint)-UpdateVitalDelta(Health, (int)-amount); DamageHistory.Add(source, damageType, damageTaken); // update stamina UpdateVitalDelta(Stamina, -1); if (Fellowship != null) { Fellowship.OnVitalUpdate(this); } if (Health.Current == 0) { OnDeath(source, damageType, crit); Die(); return; } var damageLocation = (DamageLocation)BodyParts.Indices[bodyPart]; // send network messages var creature = source as Creature; var hotspot = source as Hotspot; if (creature != null) { var text = new GameEventDefenderNotification(Session, creature.Name, damageType, percent, amount, damageLocation, crit, AttackConditions.None); Session.Network.EnqueueSend(text); var hitSound = new GameMessageSound(Guid, GetHitSound(source, bodyPart), 1.0f); var splatter = new GameMessageScript(Guid, (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + creature.GetSplatterHeight() + creature.GetSplatterDir(this))); EnqueueBroadcast(hitSound, splatter); } if (percent >= 0.1f) { EnqueueBroadcast(new GameMessageSound(Guid, Sound.Wound1, 1.0f)); } }
/// <summary> /// Applies damages to a player from a physical damage source /// </summary> public void TakeDamage(WorldObject source, DamageType damageType, float _amount, BodyPart bodyPart, bool crit = false) { if (Invincible ?? false) { return; } var amount = (uint)Math.Round(_amount); var percent = (float)amount / Health.MaxValue; // update health var damageTaken = (uint)-UpdateVitalDelta(Health, (int)-amount); DamageHistory.Add(source, damageType, damageTaken); if (Health.Current == 0) { OnDeath(source, damageType, crit); Die(); return; } // update stamina UpdateVitalDelta(Stamina, -1); var damageLocation = (DamageLocation)BodyParts.Indices[bodyPart]; // send network messages var creature = source as Creature; var hotspot = source as Hotspot; if (creature != null) { var text = new GameEventDefenderNotification(Session, creature.Name, damageType, percent, amount, damageLocation, crit, AttackConditions.None); Session.Network.EnqueueSend(text); var hitSound = new GameMessageSound(Guid, GetHitSound(source, bodyPart), 1.0f); var splatter = new GameMessageScript(Guid, (PlayScript)Enum.Parse(typeof(PlayScript), "Splatter" + creature.GetSplatterHeight() + creature.GetSplatterDir(this))); EnqueueBroadcast(hitSound, splatter); } else if (hotspot != null) { if (!string.IsNullOrWhiteSpace(hotspot.ActivationTalkString)) { Session.Network.EnqueueSend(new GameMessageSystemChat(hotspot.ActivationTalkString.Replace("%i", amount.ToString()), ChatMessageType.Craft)); } if (!hotspot.Visibility) { hotspot.EnqueueBroadcast(new GameMessageSound(hotspot.Guid, Sound.TriggerActivated, 1.0f)); } } if (percent >= 0.1f) { EnqueueBroadcast(new GameMessageSound(Guid, Sound.Wound1, 1.0f)); } }
/// <summary> /// Applies some amount of damage to this monster from source /// </summary> /// <param name="source">The attacker / source of damage</param> /// <param name="amount">The amount of damage rounded</param> public virtual void TakeDamage(WorldObject source, DamageType damageType, float amount, bool crit = false) { var tryDamage = (uint)Math.Round(amount); var damage = (uint)-UpdateVitalDelta(Health, (int)-tryDamage); // TODO: update monster stamina? // source should only be null for combined DoT ticks from multiple sources if (source != null) { DamageHistory.Add(source, damageType, damage); } if (Health.Current <= 0) { OnDeath(); Die(); // this should only probably go to the last damager var lastDamager = DamageHistory.LastDamager as Player; if (lastDamager != null) { var deathMessage = Strings.GetDeathMessage(damageType, crit); lastDamager.Session.Network.EnqueueSend(new GameMessageSystemChat(string.Format(deathMessage.Killer, Name), ChatMessageType.Broadcast)); } // split xp between players in damage history? foreach (var kvp in DamageHistory.TotalDamage) { var damager = kvp.Key; var totalDamage = kvp.Value; var playerDamager = damager as Player; if (playerDamager == null) { continue; } var damagePercent = totalDamage / Health.MaxValue; var totalXP = (XpOverride ?? 0) * damagePercent; playerDamager.EarnXP((long)Math.Round(totalXP)); } } }
/// <summary> /// Applies some amount of damage to this monster from source /// </summary> /// <param name="source">The attacker / source of damage</param> /// <param name="amount">The amount of damage rounded</param> public virtual void TakeDamage(WorldObject source, float amount, bool crit = false) { var tryDamage = (uint)Math.Round(amount); var damage = (uint)-UpdateVitalDelta(Health, (int)-tryDamage); DamageHistory.Add(source, damage); if (Health.Current <= 0) { OnDeath(); Die(); var player = source as Player; if (player != null) { var deathMessage = GetDeathMessage(source, crit); player.Session.Network.EnqueueSend(new GameMessageSystemChat(string.Format(deathMessage, Name), ChatMessageType.Broadcast)); player.EarnXP((long)XpOverride); } } }
public void TakeDamage_Falling(float amount) { if (IsDead || Invincible) { return; } // handle lifestone protection? if (UnderLifestoneProtection) { HandleLifestoneProtection(); return; } // scale by bludgeon protection var resistance = EnchantmentManager.GetResistanceMod(DamageType.Bludgeon); var damage = (uint)Math.Round(amount * resistance); // update health var damageTaken = (uint)-UpdateVitalDelta(Health, (int)-damage); DamageHistory.Add(this, DamageType.Bludgeon, damageTaken); var msg = Strings.GetFallMessage(damageTaken, Health.MaxValue); SendMessage(msg, ChatMessageType.Combat); if (Health.Current <= 0) { OnDeath(new DamageHistoryInfo(this), DamageType.Bludgeon, false); Die(); } else { EnqueueBroadcast(new GameMessageSound(Guid, Sound.Wound3, 1.0f)); } }
/// <summary> /// Updates a particular vital according to regeneration rate /// </summary> /// <param name="vital">The vital stat to update (health/stamina/mana)</param> /// <returns>TRUE if vital has changed</returns> public bool 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 && vital.RegenRate > 0) { return(false); } if (vitalCurrent > vitalMax) { UpdateVital(vital, vitalMax); return(true); } if (vital.RegenRate == 0.0) { return(false); } // 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); var augMod = 1.0f; if (this is Player player && player.AugmentationFasterRegen > 0) { augMod += player.AugmentationFasterRegen; } // cap rate? var currentTick = vital.RegenRate * attributeMod * stanceMod * enchantmentMod * augMod; // 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) { //if (this is Player) //Console.WriteLine($"VitalTick({vital.Vital.ToSentence()}): attributeMod={attributeMod}, stanceMod={stanceMod}, enchantmentMod={enchantmentMod}, regenRate={vital.RegenRate}, currentTick={currentTick}, totalTick={totalTick}, accumulated={vital.PartialRegen}"); UpdateVitalDelta(vital, intTick); if (vital.Vital == PropertyAttribute2nd.MaxHealth) { if (intTick > 0) { DamageHistory.OnHeal((uint)intTick); } else { DamageHistory.Add(this, DamageType.Health, (uint)intTick); if (Health.Current <= 0) { OnDeath(DamageHistory.LastDamager, DamageType.Health); Die(); } } return(true); } } return(false); }