public void ServerPerformInteraction(PositionalHandApply interaction) { PlayerNetworkActions pna = interaction.Performer.GetComponent <PlayerNetworkActions>(); if (interaction.HandObject == null) { // No item in hand, so let's TEACH THIS RACK A LESSON Chat.AddCombatMsgToChat(interaction.Performer, "You kick the rack. Nice job!", interaction.Performer.ExpensiveName() + " kicks the rack."); integrity.ApplyDamage(Random.Range(4, 8), AttackType.Melee, DamageType.Brute); return; } // If the player is using a wrench on the rack, deconstruct it if (Validations.IsTool(interaction.HandObject, ToolType.Wrench) && !interaction.Performer.Player().Script.playerMove.IsHelpIntent) { SoundManager.PlayNetworkedAtPos("Wrench", interaction.WorldPositionTarget, 1f); PoolManager.PoolNetworkInstantiate(rackParts, interaction.WorldPositionTarget.RoundToInt(), interaction.TargetObject.transform.parent); PoolManager.PoolNetworkDestroy(gameObject); return; } // Like a table, but everything is neatly stacked. Vector3 targetPosition = interaction.WorldPositionTarget.RoundToInt(); targetPosition.z = -0.2f; pna.CmdPlaceItem(interaction.HandSlot.equipSlot, targetPosition, interaction.Performer, true); }
public void ServerPerformInteraction(PositionalHandApply interaction) { performer = interaction.Performer; target = interaction.TargetObject; performerName = interaction.Performer.ExpensiveName(); targetName = interaction.TargetObject.ExpensiveName(); interactionWorldPosition = interaction.WorldPositionTarget; var rng = new System.Random(); if (rng.Next(1, 100) <= KNOCKDOWN_CHANCE) { KnockDown(); } else if (rng.Next(1, 100) <= DISARM_CHANCE) { Disarm(); } else { SoundManager.PlayNetworkedAtPos("PunchMiss", interactionWorldPosition, sourceObj: target); Chat.AddCombatMsgToChat( performer, $"You fail to disarm {targetName}!", $"{performerName} attempts to disarm {targetName}!"); Chat.AddExamineMsgFromServer(target, $"{performerName} attempts to disarm you!"); } }
public void ServerPerformInteraction(PositionalHandApply interaction) { PlayerNetworkActions pna = interaction.Performer.GetComponent <PlayerNetworkActions>(); if (interaction.HandObject == null) { // No item in hand, so let's TEACH THIS RACK A LESSON Chat.AddCombatMsgToChat(interaction.Performer, "You kick the rack. Nice job!", interaction.Performer.ExpensiveName() + " kicks the rack."); integrity.ApplyDamage(Random.Range(4, 8), AttackType.Melee, DamageType.Brute); return; } // If the player is using a wrench on the rack, deconstruct it if (Validations.HasItemTrait(interaction.HandObject, CommonTraits.Instance.Wrench) && interaction.Intent != Intent.Help) { SoundManager.PlayNetworkedAtPos("Wrench", interaction.WorldPositionTarget, 1f); Spawn.ServerPrefab(rackParts, interaction.WorldPositionTarget.RoundToInt(), interaction.TargetObject.transform.parent); Despawn.ServerSingle(gameObject); return; } // Like a table, but everything is neatly stacked. Vector3 targetPosition = interaction.WorldPositionTarget.RoundToInt(); targetPosition.z = -0.2f; pna.CmdPlaceItem(interaction.HandSlot.NamedSlot.GetValueOrDefault(NamedSlot.none), targetPosition, interaction.Performer, true); }
protected override void LethalElectrocution(Electrocution electrocution, float shockPower) { PlayerMove.allowInput = false; // TODO: Add sparks VFX at shockSourcePos. SoundManager.PlayNetworkedAtPos(CommonSounds.Instance.Sparks, electrocution.ShockSourcePos); StartCoroutine(ElectrocutionSequence()); string victimChatString, observerChatString; if (electrocution.ShockSourceName != null) { victimChatString = $"The {electrocution.ShockSourceName} electrocutes you!"; observerChatString = $"{gameObject.ExpensiveName()} is electrocuted by the {electrocution.ShockSourceName}!"; } else { victimChatString = $"Something electrocutes you!"; observerChatString = $"{gameObject.ExpensiveName()} is electrocuted by something!"; } Chat.AddCombatMsgToChat(gameObject, victimChatString, observerChatString); var damage = shockPower / ELECTROCUTION_BURNDAMAGE_MODIFIER; if (ELECTROCUTION_MAX_DAMAGE != -1 && damage > ELECTROCUTION_MAX_DAMAGE) { damage = ELECTROCUTION_MAX_DAMAGE; } DealElectrocutionDamage(damage * 0.4f, electrocutedHand); DealElectrocutionDamage(damage * 0.25f, BodyPartType.Chest); DealElectrocutionDamage(damage * 0.175f, BodyPartType.LeftLeg); DealElectrocutionDamage(damage * 0.175f, BodyPartType.RightLeg); }
public void ServerPerformInteraction(PositionalHandApply interaction) { performer = interaction.Performer; target = interaction.TargetObject; performerName = interaction.Performer.ExpensiveName(); targetName = interaction.TargetObject.ExpensiveName(); interactionWorldPosition = interaction.WorldPositionTarget; if (Cooldowns.TryStart(interaction, this, side: NetworkSide.Server) == false) { return; } var rng = new System.Random(); if (rng.Next(1, 100) <= KNOCKDOWN_CHANCE) { KnockDown(); } else if (rng.Next(1, 100) <= DISARM_CHANCE) { Disarm(); } else { SoundManager.PlayNetworkedAtPos(SingletonSOSounds.Instance.PunchMiss, interactionWorldPosition, sourceObj: target); Chat.AddCombatMsgToChat( performer, $"You fail to disarm {targetName}!", $"{performerName} attempts to disarm {targetName}!"); Chat.AddExamineMsgFromServer(target, $"{performerName} attempts to disarm you!"); } }
public void ServerPerformInteraction(PositionalHandApply interaction) { PlayerNetworkActions pna = interaction.Performer.GetComponent <PlayerNetworkActions>(); if (interaction.HandObject == null) { // No item in hand, so let's TEACH THIS RACK A LESSON Chat.AddCombatMsgToChat(interaction.Performer, "You kick the rack. Nice job!", interaction.Performer.ExpensiveName() + " kicks the rack."); integrity.ApplyDamage(Random.Range(4, 8), AttackType.Melee, DamageType.Brute); return; } // If the player is using a wrench on the rack, deconstruct it if (Validations.HasItemTrait(interaction.HandObject, CommonTraits.Instance.Wrench) && interaction.Intent != Intent.Help) { SoundManager.PlayNetworkedAtPos("Wrench", interaction.WorldPositionTarget, 1f); Spawn.ServerPrefab(rackParts, interaction.WorldPositionTarget.RoundToInt(), interaction.TargetObject.transform.parent); Despawn.ServerSingle(gameObject); return; } //drop it right in the middle of the rack. IN order to do that we have to calculate //that position as an offset from the performer //TODO: Make it less awkward by adding a serverdrop method that accepts absolute position instead of vector. var targetTileWorldPosition = gameObject.TileWorldPosition(); var targetTileVector = (Vector3Int)targetTileWorldPosition - interaction.PerformerPlayerScript.registerTile.WorldPositionServer; Inventory.ServerDrop(interaction.HandSlot, targetTileVector.To2Int()); }
public void CmdRequestHug(string hugger, GameObject huggedPlayer) { //validate that hug can be done if (!Validations.CanApply(playerScript, huggedPlayer, NetworkSide.Server)) { return; } //hugging is kind of a melee-type action, at least we wouldn't want to spam it quickly if (!Cooldowns.TryStartServer(playerScript, CommonCooldowns.Instance.Melee)) { return; } string hugged = huggedPlayer.GetComponent <PlayerScript>().playerName; var lhb = gameObject.GetComponent <LivingHealthBehaviour>(); var lhbOther = huggedPlayer.GetComponent <LivingHealthBehaviour>(); if (lhb != null && lhbOther != null && (lhb.FireStacks > 0 || lhbOther.FireStacks > 0)) { lhb.ApplyDamage(huggedPlayer, 1, AttackType.Fire, DamageType.Burn); lhbOther.ApplyDamage(gameObject, 1, AttackType.Fire, DamageType.Burn); Chat.AddCombatMsgToChat(gameObject, $"You give {hugged} a fiery hug.", $"{hugger} has given {hugged} a fiery hug."); } else { Chat.AddActionMsgToChat(gameObject, $"You hugged {hugged}.", $"{hugger} has hugged {hugged}."); } }
public void CmdRequestDisarm(GameObject playerToDisarm) { if (!Validations.CanApply(playerScript, playerToDisarm, NetworkSide.Server)) { return; } if (!Cooldowns.TryStartServer(playerScript, CommonCooldowns.Instance.Interaction)) { return; } var rng = new System.Random(); string disarmerName = playerScript.gameObject.Player()?.Name; string playerToDisarmName = playerToDisarm.Player()?.Name; var disarmStorage = playerToDisarm.GetComponent <ItemStorage>(); var leftHandSlot = disarmStorage.GetNamedItemSlot(NamedSlot.leftHand); var rightHandSlot = disarmStorage.GetNamedItemSlot(NamedSlot.rightHand); var disarmedPlayerRegister = playerToDisarm.GetComponent <RegisterPlayer>(); var disarmedPlayerNetworkActions = playerToDisarm.GetComponent <PlayerNetworkActions>(); // This is based off the alien/humanoid/attack_hand disarm code of TGStation's codebase. // Disarms have 5% chance to knock down, then it has a 50% chance to disarm. if (5 >= rng.Next(1, 100)) { disarmedPlayerRegister.ServerStun(6f, false); SoundManager.PlayNetworkedAtPos("ThudSwoosh", disarmedPlayerRegister.WorldPositionServer, sourceObj: disarmedPlayerRegister.gameObject); Chat.AddCombatMsgToChat(gameObject, $"You have knocked {playerToDisarmName} down!", $"{disarmerName} has knocked {playerToDisarmName} down!"); } else if (50 >= rng.Next(1, 100)) { // Disarms if (leftHandSlot.Item != null) { Inventory.ServerDrop(leftHandSlot); } if (rightHandSlot.Item != null) { Inventory.ServerDrop(rightHandSlot); } SoundManager.PlayNetworkedAtPos("ThudSwoosh", disarmedPlayerRegister.WorldPositionServer, sourceObj: disarmedPlayerRegister.gameObject); Chat.AddCombatMsgToChat(gameObject, $"You have disarmed {playerToDisarmName}!", $"{disarmerName} has disarmed {playerToDisarmName}!"); } else { SoundManager.PlayNetworkedAtPos("PunchMiss", disarmedPlayerRegister.WorldPositionServer, sourceObj: disarmedPlayerRegister.gameObject); Chat.AddCombatMsgToChat(gameObject, $"You attempted to disarm {playerToDisarmName}!", $"{disarmerName} has attempted to disarm {playerToDisarmName}!"); } }
private void KnockDown() { var targetRegister = target.GetComponent <RegisterPlayer>(); targetRegister.ServerStun(KNOCKDOWN_STUN_TIME, false); SoundManager.PlayNetworkedAtPos(SingletonSOSounds.Instance.ThudSwoosh, interactionWorldPosition, sourceObj: target); Chat.AddCombatMsgToChat( performer, $"You knock {targetName} down!", $"{performerName} knocks {targetName} down!"); Chat.AddExamineMsgFromServer(target, $"{performerName} knocks you down!"); }
public void CmdRequestDisarm(GameObject disarmer, GameObject playerToDisarm) { var rng = new System.Random(); string disarmerName = disarmer.Player()?.Name; string playerToDisarmName = playerToDisarm.Player()?.Name; var leftHandSlot = InventoryManager.GetSlotFromOriginatorHand(playerToDisarm, EquipSlot.leftHand); var rightHandSlot = InventoryManager.GetSlotFromOriginatorHand(playerToDisarm, EquipSlot.rightHand); var disarmedPlayerRegister = playerToDisarm.GetComponent <RegisterPlayer>(); var disarmedPlayerNetworkActions = playerToDisarm.GetComponent <PlayerNetworkActions>(); // This is based off the alien/humanoid/attack_hand disarm code of TGStation's codebase. // Disarms have 5% chance to knock down, then it has a 50% chance to disarm. if (5 >= rng.Next(1, 100)) { disarmedPlayerRegister.Stun(6f, false); SoundManager.PlayNetworkedAtPos("ThudSwoosh", disarmedPlayerRegister.WorldPositionServer); Chat.AddCombatMsgToChat(gameObject, $"You have knocked {playerToDisarmName} down!", $"{disarmerName} has knocked {playerToDisarmName} down!"); } else if (50 >= rng.Next(1, 100)) { // Disarms if (leftHandSlot.Item != null) { disarmedPlayerNetworkActions.DropItem(EquipSlot.leftHand); } if (rightHandSlot.Item != null) { disarmedPlayerNetworkActions.DropItem(EquipSlot.rightHand); } SoundManager.PlayNetworkedAtPos("ThudSwoosh", disarmedPlayerRegister.WorldPositionServer); Chat.AddCombatMsgToChat(gameObject, $"You have disarmed {playerToDisarmName}!", $"{disarmerName} has disarmed {playerToDisarmName}!"); } else { SoundManager.PlayNetworkedAtPos("PunchMiss", disarmedPlayerRegister.WorldPositionServer); Chat.AddCombatMsgToChat(gameObject, $"You attempted to disarm {playerToDisarmName}!", $"{disarmerName} has attempted to disarm {playerToDisarmName}!"); } }
public override void Punish(ConnectedPlayer player) { Chat.AddCombatMsgToChat(player.GameObject, "You suddenly feel very solid!", $"{player.GameObject.ExpensiveName()} goes very still! {player.Script.characterSettings.TheyPronoun()}'s been petrified!"); player.Script.playerMove.allowInput = false; // Piggy-back off IsMiming property to prevent the player from speaking. // TODO: convert to player trait when we have that system. player.Script.mind.IsMiming = true; StartCoroutine(Unpetrify(player.Script)); Chat.AddCombatMsgToChat(player.GameObject, "<size=60><b>Your body freezes up! Can't... move... can't... think...</b></size>", $"{player.GameObject.ExpensiveName()}'s skin rapidly turns to marble!"); }
public void CmdRequestHug(string hugger, GameObject huggedPlayer) { string hugged = huggedPlayer.GetComponent <PlayerScript>().playerName; var lhb = gameObject.GetComponent <LivingHealthBehaviour>(); var lhbOther = huggedPlayer.GetComponent <LivingHealthBehaviour>(); if (lhb != null && lhbOther != null && (lhb.FireStacks > 0 || lhbOther.FireStacks > 0)) { lhb.ApplyDamage(huggedPlayer, 1, AttackType.Fire, DamageType.Burn); lhbOther.ApplyDamage(gameObject, 1, AttackType.Fire, DamageType.Burn); Chat.AddCombatMsgToChat(gameObject, $"You give {hugged} a fiery hug.", $"{hugger} has given {hugged} a fiery hug."); } else { Chat.AddActionMsgToChat(gameObject, $"You hugged {hugged}.", $"{hugger} has hugged {hugged}."); } }
public void CmdRequestPunchAttack(GameObject victim, Vector2 punchDirection, BodyPartType damageZone) { var victimHealth = victim.GetComponent <LivingHealthBehaviour>(); var victimRegisterTile = victim.GetComponent <RegisterTile>(); var rng = new System.Random(); if (!playerScript.IsInReach(victim, true) || !victimHealth) { return; } // If the punch is not self inflicted, do the simple lerp attack animation. if (victim != gameObject) { RpcMeleeAttackLerp(punchDirection, null); playerMove.allowInput = false; } // This is based off the alien/humanoid/attack_hand punch code of TGStation's codebase. // Punches have 90% chance to hit, otherwise it is a miss. if (90 >= rng.Next(1, 100)) { // The punch hit. victimHealth.ApplyDamage(gameObject, (int)fistDamage, AttackType.Melee, DamageType.Brute, damageZone); if (fistDamage > 0) { Chat.AddAttackMsgToChat(gameObject, victim, damageZone); } // Make a random punch hit sound. SoundManager.PlayNetworkedAtPos("Punch#", victimRegisterTile.WorldPosition); StartCoroutine(AttackCoolDown()); } else { // The punch missed. string victimName = victim.Player()?.Name; SoundManager.PlayNetworkedAtPos("PunchMiss", transform.position); Chat.AddCombatMsgToChat(gameObject, $"You attempted to punch {victimName} but missed!", $"{gameObject.Player()?.Name} has attempted to punch {victimName}!"); } }
private void Disarm() { var disarmStorage = target.GetComponent <DynamicItemStorage>(); if (disarmStorage == null) { return; } var leftHandSlots = disarmStorage.GetNamedItemSlots(NamedSlot.leftHand); var rightHandSlots = disarmStorage.GetNamedItemSlots(NamedSlot.rightHand); foreach (var leftHandSlot in leftHandSlots) { if (leftHandSlot.IsEmpty) { continue; } Inventory.ServerDrop(leftHandSlot); } foreach (var rightHandSlot in rightHandSlots) { if (rightHandSlot.IsEmpty) { continue; } Inventory.ServerDrop(rightHandSlot); } SoundManager.PlayNetworkedAtPos(SingletonSOSounds.Instance.ThudSwoosh, interactionWorldPosition, sourceObj: target); Chat.AddCombatMsgToChat( performer, $"You successfully disarm {targetName}!", $"{performerName} disarms {targetName}!"); Chat.AddExamineMsgFromServer(target, $"{performerName} disarms you!"); }
private void Disarm() { var disarmStorage = target.GetComponent <ItemStorage>(); var leftHandSlot = disarmStorage.GetNamedItemSlot(NamedSlot.leftHand); var rightHandSlot = disarmStorage.GetNamedItemSlot(NamedSlot.rightHand); if (leftHandSlot.Item != null) { Inventory.ServerDrop(leftHandSlot); } if (rightHandSlot.Item != null) { Inventory.ServerDrop(rightHandSlot); } SoundManager.PlayNetworkedAtPos("ThudSwoosh", interactionWorldPosition, sourceObj: target); Chat.AddCombatMsgToChat( performer, $"You successfully disarm {targetName}!", $"{performerName} disarms {targetName}!"); Chat.AddExamineMsgFromServer(target, $"{performerName} disarms you!"); }
public void ServerPerformMeleeAttack(GameObject victim, Vector2 attackDirection, BodyPartType damageZone, LayerType layerType) { if (Cooldowns.IsOnServer(playerScript, CommonCooldowns.Instance.Melee)) { return; } var weapon = playerScript.playerNetworkActions.GetActiveHandItem(); var tiles = victim.GetComponent <InteractableTiles>(); if (tiles) { //validate based on position of target vector if (!Validations.CanApply(playerScript, victim, NetworkSide.Server, targetVector: attackDirection)) { return; } } else { //validate based on position of target object if (!Validations.CanApply(playerScript, victim, NetworkSide.Server)) { return; } } if (!playerMove.allowInput || playerScript.IsGhost || !victim || !playerScript.playerHealth.serverPlayerConscious ) { return; } var isWeapon = weapon != null; ItemAttributesV2 weaponAttr = isWeapon ? weapon.GetComponent <ItemAttributesV2>() : null; var damage = isWeapon ? weaponAttr.ServerHitDamage : fistDamage; var damageType = isWeapon ? weaponAttr.ServerDamageType : DamageType.Brute; var attackSoundName = isWeapon ? weaponAttr.ServerHitSound : "Punch#"; LayerTile attackedTile = null; bool didHit = false; ItemAttributesV2 weaponStats = null; if (isWeapon) { weaponStats = weapon.GetComponent <ItemAttributesV2>(); } // If Tilemap LayerType is not None then it is a tilemap being attacked if (layerType != LayerType.None) { var tileChangeManager = victim.GetComponent <TileChangeManager>(); if (tileChangeManager == null) { return; //Make sure its on a matrix that is destructable } //Tilemap stuff: var tileMapDamage = victim.GetComponentInChildren <MetaTileMap>().Layers[layerType].gameObject .GetComponent <TilemapDamage>(); if (tileMapDamage != null) { if (isWeapon && weaponStats != null && weaponStats.hitSoundSettings == SoundItemSettings.OnlyObject) { attackSoundName = ""; } var worldPos = (Vector2)transform.position + attackDirection; attackedTile = tileChangeManager.InteractableTiles.LayerTileAt(worldPos, true); tileMapDamage.DoMeleeDamage(worldPos, gameObject, (int)damage); didHit = true; } } else { //a regular object being attacked LivingHealthBehaviour victimHealth = victim.GetComponent <LivingHealthBehaviour>(); var integrity = victim.GetComponent <Integrity>(); if (integrity != null) { //damaging an object if (isWeapon && weaponStats != null && weaponStats.hitSoundSettings == SoundItemSettings.Both) { SoundManager.PlayNetworkedAtPos(integrity.soundOnHit, gameObject.WorldPosServer(), Random.Range(0.9f, 1.1f)); } else if (isWeapon && weaponStats != null && weaponStats.hitSoundSettings == SoundItemSettings.OnlyObject && integrity.soundOnHit != "") { SoundManager.PlayNetworkedAtPos(integrity.soundOnHit, gameObject.WorldPosServer(), Random.Range(0.9f, 1.1f)); attackSoundName = ""; } integrity.ApplyDamage((int)damage, AttackType.Melee, damageType); didHit = true; } else { //damaging a living thing var rng = new System.Random(); // This is based off the alien/humanoid/attack_hand punch code of TGStation's codebase. // Punches have 90% chance to hit, otherwise it is a miss. if (isWeapon || 90 >= rng.Next(1, 100)) { // The attack hit. victimHealth.ApplyDamageToBodypart(gameObject, (int)damage, AttackType.Melee, damageType, damageZone); didHit = true; } else { // The punch missed. string victimName = victim.Player()?.Name; SoundManager.PlayNetworkedAtPos("PunchMiss", transform.position); Chat.AddCombatMsgToChat(gameObject, $"You attempted to punch {victimName} but missed!", $"{gameObject.Player()?.Name} has attempted to punch {victimName}!"); } } } //common logic to do if we hit something if (didHit) { if (!string.IsNullOrEmpty(attackSoundName)) { SoundManager.PlayNetworkedAtPos(attackSoundName, transform.position); } if (damage > 0) { Chat.AddAttackMsgToChat(gameObject, victim, damageZone, weapon, attackedTile: attackedTile); } if (victim != gameObject) { RpcMeleeAttackLerp(attackDirection, weapon); //playerMove.allowInput = false; } } Cooldowns.TryStartServer(playerScript, CommonCooldowns.Instance.Melee); }
public void CmdRequestMeleeAttack(GameObject victim, Vector2 attackDirection, BodyPartType damageZone, LayerType layerType) { var weapon = playerScript.playerNetworkActions.GetActiveHandItem(); var tiles = victim.GetComponent <InteractableTiles>(); if (tiles) { //validate based on position of target vector if (!Validations.CanApply(playerScript, victim, NetworkSide.Server, targetVector: attackDirection)) { return; } } else { //validate based on position of target object if (!Validations.CanApply(playerScript, victim, NetworkSide.Server)) { return; } } if (!playerMove.allowInput || playerScript.IsGhost || !victim || !playerScript.playerHealth.serverPlayerConscious ) { return; } if (!allowAttack) { return; } var isWeapon = weapon != null; ItemAttributesV2 weaponAttr = isWeapon ? weapon.GetComponent <ItemAttributesV2>() : null; var damage = isWeapon ? weaponAttr.ServerHitDamage : fistDamage; var damageType = isWeapon ? weaponAttr.ServerDamageType : DamageType.Brute; var attackSoundName = isWeapon ? weaponAttr.ServerHitSound : "Punch#"; LayerTile attackedTile = null; bool didHit = false; // If Tilemap LayerType is not None then it is a tilemap being attacked if (layerType != LayerType.None) { var tileChangeManager = victim.GetComponent <TileChangeManager>(); if (tileChangeManager == null) { return; //Make sure its on a matrix that is destructable } //Tilemap stuff: var tileMapDamage = victim.GetComponentInChildren <MetaTileMap>().Layers[layerType].gameObject .GetComponent <TilemapDamage>(); if (tileMapDamage != null) { var worldPos = (Vector2)transform.position + attackDirection; attackedTile = tileChangeManager.InteractableTiles.LayerTileAt(worldPos); tileMapDamage.DoMeleeDamage(worldPos, gameObject, (int)damage); didHit = true; } } else { //a regular object being attacked //butchering //TODO: Move butchering logic to IF2, it should be a progress action done on corpses (make a Corpse component probably) LivingHealthBehaviour victimHealth = victim.GetComponent <LivingHealthBehaviour>(); if (victimHealth != null && victimHealth.IsDead && isWeapon && weaponAttr.HasTrait(KnifeTrait)) { if (victim.GetComponent <SimpleAnimal>()) { SimpleAnimal attackTarget = victim.GetComponent <SimpleAnimal>(); RpcMeleeAttackLerp(attackDirection, weapon); playerMove.allowInput = false; attackTarget.Harvest(); SoundManager.PlayNetworkedAtPos("BladeSlice", transform.position); } else { PlayerHealth attackTarget = victim.GetComponent <PlayerHealth>(); RpcMeleeAttackLerp(attackDirection, weapon); playerMove.allowInput = false; attackTarget.Harvest(); SoundManager.PlayNetworkedAtPos("BladeSlice", transform.position); } } var integrity = victim.GetComponent <Integrity>(); if (integrity != null) { //damaging an object integrity.ApplyDamage((int)damage, AttackType.Melee, damageType); didHit = true; } else { //damaging a living thing var rng = new System.Random(); // This is based off the alien/humanoid/attack_hand punch code of TGStation's codebase. // Punches have 90% chance to hit, otherwise it is a miss. if (isWeapon || 90 >= rng.Next(1, 100)) { // The attack hit. victimHealth.ApplyDamageToBodypart(gameObject, (int)damage, AttackType.Melee, damageType, damageZone); didHit = true; } else { // The punch missed. string victimName = victim.Player()?.Name; SoundManager.PlayNetworkedAtPos("PunchMiss", transform.position); Chat.AddCombatMsgToChat(gameObject, $"You attempted to punch {victimName} but missed!", $"{gameObject.Player()?.Name} has attempted to punch {victimName}!"); } } } //common logic to do if we hit something if (didHit) { SoundManager.PlayNetworkedAtPos(attackSoundName, transform.position); if (damage > 0) { Chat.AddAttackMsgToChat(gameObject, victim, damageZone, weapon, attackedTile: attackedTile); } if (victim != gameObject) { RpcMeleeAttackLerp(attackDirection, weapon); playerMove.allowInput = false; } } //no matter what, start a cooldown for attacking again so they can't spam attack requests StartCoroutine(AttackCoolDown()); }