private float handleDamaged(IPlayer player, float damage, DamageSource dmgSource) { EnumDamageType type = dmgSource.Type; double angleProtectionRange = 120 / 2 * GameMath.DEG2RAD; // Reduce damage if player holds a shield ItemSlot[] shieldSlots = new ItemSlot[] { player.Entity.LeftHandItemSlot, player.Entity.RightHandItemSlot }; foreach (var shieldSlot in shieldSlots) { var attr = shieldSlot.Itemstack?.ItemAttributes?["shield"]; if (attr == null || !attr.Exists) { continue; } string usetype = player.Entity.Controls.Sneak ? "active" : "passive"; float dmgabsorb = attr["damageAbsorption"][usetype].AsFloat(0); float chance = attr["protectionChance"][usetype].AsFloat(0); (player as IServerPlayer)?.SendMessage(GlobalConstants.DamageLogChatGroup, Lang.Get("{0:0.#} of {1:0.#} damage blocked by shield", Math.Min(dmgabsorb, damage), damage), EnumChatType.Notification); double dx; double dz; if (dmgSource.HitPosition != null) { dx = dmgSource.HitPosition.X; dz = dmgSource.HitPosition.Z; } else if (dmgSource.SourceEntity != null) { dx = dmgSource.SourceEntity.Pos.X - player.Entity.Pos.X; dz = dmgSource.SourceEntity.Pos.Z - player.Entity.Pos.Z; } else if (dmgSource.SourcePos != null) { dx = dmgSource.SourcePos.X - player.Entity.Pos.X; dz = dmgSource.SourcePos.Z - player.Entity.Pos.Z; } else { break; } double attackYaw = Math.Atan2((double)dx, (double)dz); double playerYaw = player.Entity.Pos.Yaw + GameMath.PIHALF; bool inProtectionRange = Math.Abs(GameMath.AngleRadDistance((float)playerYaw, (float)attackYaw)) < angleProtectionRange; if (inProtectionRange && api.World.Rand.NextDouble() < chance) { damage = Math.Max(0, damage - dmgabsorb); var loc = shieldSlot.Itemstack.ItemAttributes["blockSound"].AsString("held/shieldblock"); api.World.PlaySoundAt(AssetLocation.Create(loc, shieldSlot.Itemstack.Collectible.Code.Domain).WithPathPrefixOnce("sounds/").WithPathAppendixOnce(".ogg"), player, null); if (api.Side == EnumAppSide.Server) { shieldSlot.Itemstack.Collectible.DamageItem(api.World, dmgSource.SourceEntity, shieldSlot, 1); shieldSlot.MarkDirty(); } } } if (damage <= 0) { return(0); } // The code below only the server needs to execute if (api.Side == EnumAppSide.Client) { return(damage); } // Does not protect against non-attack damages if (type != EnumDamageType.BluntAttack && type != EnumDamageType.PiercingAttack && type != EnumDamageType.SlashingAttack) { return(damage); } if (dmgSource.Source == EnumDamageSource.Internal || dmgSource.Source == EnumDamageSource.Suicide) { return(damage); } ItemSlot armorSlot; IInventory inv = player.InventoryManager.GetOwnInventory(GlobalConstants.characterInvClassName); double rnd = api.World.Rand.NextDouble(); int attackTarget; if ((rnd -= 0.2) < 0) { // Head armorSlot = inv[12]; attackTarget = 0; } else if ((rnd -= 0.5) < 0) { // Body armorSlot = inv[13]; attackTarget = 1; } else { // Legs armorSlot = inv[14]; attackTarget = 2; } // Apply full damage if no armor is in this slot if (armorSlot.Empty || !(armorSlot.Itemstack.Item is ItemWearable)) { EnumCharacterDressType[] dressTargets = clothingDamageTargetsByAttackTacket[attackTarget]; EnumCharacterDressType target = dressTargets[api.World.Rand.Next(dressTargets.Length)]; ItemSlot targetslot = player.Entity.GearInventory[(int)target]; if (!targetslot.Empty) { // Wolf: 10 hp damage = 10% condition loss // Ram: 10 hp damage = 2.5% condition loss // Bronze locust: 10 hp damage = 5% condition loss float mul = 0.25f; if (type == EnumDamageType.SlashingAttack) { mul = 1f; } if (type == EnumDamageType.PiercingAttack) { mul = 0.5f; } float diff = -damage / 100 * mul; if (Math.Abs(diff) > 0.05) { api.World.PlaySoundAt(ripSound, player.Entity); } (targetslot.Itemstack.Collectible as ItemWearable)?.ChangeCondition(targetslot, diff); } return(damage); } ProtectionModifiers protMods = (armorSlot.Itemstack.Item as ItemWearable).ProtectionModifiers; int weaponTier = dmgSource.DamageTier; float flatDmgProt = protMods.FlatDamageReduction; float percentProt = protMods.RelativeProtection; for (int tier = 1; tier <= weaponTier; tier++) { bool aboveTier = tier > protMods.ProtectionTier; float flatLoss = aboveTier ? protMods.PerTierFlatDamageReductionLoss[1] : protMods.PerTierFlatDamageReductionLoss[0]; float percLoss = aboveTier ? protMods.PerTierRelativeProtectionLoss[1] : protMods.PerTierRelativeProtectionLoss[0]; if (aboveTier && protMods.HighDamageTierResistant) { flatLoss /= 2; percLoss /= 2; } flatDmgProt -= flatLoss; percentProt *= 1 - percLoss; } // Durability loss is the one before the damage reductions float durabilityLoss = 0.5f + damage * Math.Max(0.5f, (weaponTier - protMods.ProtectionTier) * 3); int durabilityLossInt = GameMath.RoundRandom(api.World.Rand, durabilityLoss); // Now reduce the damage damage = Math.Max(0, damage - flatDmgProt); damage *= 1 - Math.Max(0, percentProt); armorSlot.Itemstack.Collectible.DamageItem(api.World, player.Entity, armorSlot, durabilityLossInt); if (armorSlot.Empty) { api.World.PlaySoundAt(new AssetLocation("sounds/effect/toolbreak"), player); } return(damage); }
public override void OnLoaded(ICoreAPI api) { base.OnLoaded(api); string strdress = Attributes["clothescategory"].AsString(); EnumCharacterDressType dt = EnumCharacterDressType.Unknown; Enum.TryParse(strdress, true, out dt); DressType = dt; JsonObject jsonObj = Attributes?["footStepSound"]; if (jsonObj?.Exists == true) { string soundloc = jsonObj.AsString(null); if (soundloc != null) { AssetLocation loc = AssetLocation.Create(soundloc, Code.Domain).WithPathPrefixOnce("sounds/"); if (soundloc.EndsWith("*")) { loc.Path = loc.Path.TrimEnd('*'); FootStepSounds = api.Assets.GetLocations(loc.Path, loc.Domain).ToArray(); } else { FootStepSounds = new AssetLocation[] { loc }; } } } jsonObj = Attributes?["statModifiers"]; if (jsonObj?.Exists == true) { try { StatModifers = jsonObj.AsObject <StatModifiers>(); } catch (Exception e) { api.World.Logger.Error("Failed loading statModifiers for item/block {0}. Will ignore. Exception: {1}", Code, e); StatModifers = null; } } ProtectionModifiers defMods = null; jsonObj = Attributes?["defaultProtLoss"]; if (jsonObj?.Exists == true) { try { defMods = jsonObj.AsObject <ProtectionModifiers>(); } catch (Exception e) { api.World.Logger.Error("Failed loading defaultProtLoss for item/block {0}. Will ignore. Exception: {1}", Code, e); } } jsonObj = Attributes?["protectionModifiers"]; if (jsonObj?.Exists == true) { try { ProtectionModifiers = jsonObj.AsObject <ProtectionModifiers>(); } catch (Exception e) { api.World.Logger.Error("Failed loading protectionModifiers for item/block {0}. Will ignore. Exception: {1}", Code, e); ProtectionModifiers = null; } } if (ProtectionModifiers != null && ProtectionModifiers.PerTierFlatDamageReductionLoss == null) { ProtectionModifiers.PerTierFlatDamageReductionLoss = defMods?.PerTierFlatDamageReductionLoss; } if (ProtectionModifiers != null && ProtectionModifiers.PerTierRelativeProtectionLoss == null) { ProtectionModifiers.PerTierRelativeProtectionLoss = defMods?.PerTierRelativeProtectionLoss; } }
private float handleDamaged(IPlayer player, float damage, DamageSource dmgSource) { // Does not protect against non-attack damages EnumDamageType type = dmgSource.Type; if (type != EnumDamageType.BluntAttack && type != EnumDamageType.PiercingAttack && type != EnumDamageType.SlashingAttack) { return(damage); } if (dmgSource.Source == EnumDamageSource.Internal || dmgSource.Source == EnumDamageSource.Suicide) { return(damage); } ItemSlot armorSlot; IInventory inv = player.InventoryManager.GetOwnInventory(GlobalConstants.characterInvClassName); double rnd = api.World.Rand.NextDouble(); int attackTarget; if ((rnd -= 0.2) < 0) { // Head armorSlot = inv[12]; attackTarget = 0; } else if ((rnd -= 0.5) < 0) { // Body armorSlot = inv[13]; attackTarget = 1; } else { // Legs armorSlot = inv[14]; attackTarget = 2; } // Apply full damage if no armor is in this slot if (armorSlot.Empty || !(armorSlot.Itemstack.Item is ItemWearable)) { EnumCharacterDressType[] dressTargets = clothingDamageTargetsByAttackTacket[attackTarget]; EnumCharacterDressType target = dressTargets[api.World.Rand.Next(dressTargets.Length)]; ItemSlot targetslot = player.Entity.GearInventory[(int)target]; if (!targetslot.Empty) { // Wolf: 10 hp damage = 10% condition loss // Ram: 10 hp damage = 2.5% condition loss // Bronze locust: 10 hp damage = 5% condition loss float mul = 0.25f; if (type == EnumDamageType.SlashingAttack) { mul = 1f; } if (type == EnumDamageType.PiercingAttack) { mul = 0.5f; } float diff = -damage / 100 * mul; if (Math.Abs(diff) > 0.05) { api.World.PlaySoundAt(ripSound, player.Entity); } (targetslot.Itemstack.Collectible as ItemWearable)?.ChangeCondition(targetslot, diff); } return(damage); } ProtectionModifiers protMods = (armorSlot.Itemstack.Item as ItemWearable).ProtectionModifiers; int weaponTier = dmgSource.DamageTier; float flatDmgProt = protMods.FlatDamageReduction; float percentProt = protMods.RelativeProtection; for (int tier = 1; tier <= weaponTier; tier++) { bool aboveTier = tier > protMods.ProtectionTier; float flatLoss = aboveTier ? protMods.PerTierFlatDamageReductionLoss[1] : protMods.PerTierFlatDamageReductionLoss[0]; float percLoss = aboveTier ? protMods.PerTierRelativeProtectionLoss[1] : protMods.PerTierRelativeProtectionLoss[0]; if (aboveTier && protMods.HighDamageTierResistant) { flatLoss /= 2; percLoss /= 2; } flatDmgProt -= flatLoss; percentProt *= 1 - percLoss; } // Durability loss is the one before the damage reductions float durabilityLoss = 0.5f + damage * Math.Max(0.5f, (weaponTier - protMods.ProtectionTier) * 3); int durabilityLossInt = GameMath.RoundRandom(api.World.Rand, durabilityLoss); // Now reduce the damage damage = Math.Max(0, damage - flatDmgProt); damage *= 1 - Math.Max(0, percentProt); armorSlot.Itemstack.Collectible.DamageItem(api.World, player.Entity, armorSlot, durabilityLossInt); if (armorSlot.Empty) { api.World.PlaySoundAt(new AssetLocation("sounds/effect/toolbreak"), player); } return(damage); }