/// <summary> /// honk on world click /// </summary> public void ServerPerformInteraction(PositionalHandApply interaction) { bool inCloseRange = Validations.IsInReach(interaction.TargetVector); var targetObject = interaction.TargetObject; var targetHealth = targetObject != null?targetObject.GetComponent <LivingHealthBehaviour>() : null; bool isCrit = Random.Range(0f, 1f) <= CritChance; // honking in someone's face if (inCloseRange && (targetHealth != null)) { interaction.Performer.GetComponent <WeaponNetworkActions>().RpcMeleeAttackLerp(interaction.TargetVector, gameObject); Chat.AddAttackMsgToChat(interaction.Performer, targetObject, BodyPartType.None, gameObject); ClassicHonk(); if (isCrit) { StartCoroutine(CritHonk(interaction, targetHealth)); } } else { ClassicHonk(); } StartCoroutine(StartCooldown()); }
private void AttackFlesh(Vector2 dir, LivingHealthBehaviour healthBehaviour) { healthBehaviour.ApplyDamageToBodypart(gameObject, hitDamage, AttackType.Melee, DamageType.Brute, defaultTarget.Randomize()); Chat.AddAttackMsgToChat(gameObject, healthBehaviour.gameObject, defaultTarget, null, attackVerb); SoundManager.PlayNetworkedAtPos("BladeSlice", transform.position, sourceObj: gameObject); ServerDoLerpAnimation(dir); }
//We need to slow the attack down because clients are behind server IEnumerator AttackFleshRoutine(Vector2 dir, LivingHealthBehaviour healthBehaviour) { if (healthBehaviour.connectionToClient == null) { yield break; } ServerDoLerpAnimation(dir); Debug.Log( $"CONN CLIENT TIME: {healthBehaviour.connectionToClient.lastMessageTime} Network time: {(float) NetworkTime.time}"); if (PlayerManager.LocalPlayerScript != null && PlayerManager.LocalPlayerScript.playerHealth != null && PlayerManager.LocalPlayerScript.playerHealth == healthBehaviour || healthBehaviour.RTT == 0f) { yield return(WaitFor.EndOfFrame); } else { Debug.Log($"WAIT FOR ATTACK: {healthBehaviour.RTT / 2f}"); yield return(WaitFor.Seconds(healthBehaviour.RTT / 2f)); } if (Vector3.Distance(transform.position, healthBehaviour.transform.position) < 1.5f) { healthBehaviour.ApplyDamageToBodypart(gameObject, hitDamage, AttackType.Melee, DamageType.Brute, defaultTarget.Randomize()); Chat.AddAttackMsgToChat(gameObject, healthBehaviour.gameObject, defaultTarget, null, attackVerb); SoundManager.PlayNetworkedAtPos("BladeSlice", transform.position, sourceObj: gameObject); } }
/// <summary> /// honk on world click /// </summary> public void ServerPerformInteraction(PositionalHandApply interaction) { Vector3 performerWorldPos = interaction.PerformerPlayerScript.WorldPos; bool inCloseRange = Validations.IsReachableByPositions(performerWorldPos, performerWorldPos + (Vector3)interaction.TargetVector, true, context: interaction.TargetObject); var targetObject = interaction.TargetObject; var targetHealth = targetObject != null?targetObject.GetComponent <LivingHealthMasterBase>() : null; bool isCrit = Random.Range(0f, 1f) <= CritChance; // honking in someone's face if (inCloseRange && (targetHealth != null)) { interaction.Performer.GetComponent <WeaponNetworkActions>().RpcMeleeAttackLerp(interaction.TargetVector, gameObject); Chat.AddAttackMsgToChat(interaction.Performer, targetObject, BodyPartType.None, gameObject); ClassicHonk(); if (isCrit && interaction.Intent == Intent.Harm) { StartCoroutine(CritHonk(interaction, targetHealth)); } } else { ClassicHonk(); } StartCoroutine(StartCooldown()); }
private bool TryDamage(RaycastHit2D hit) { var coll = hit.collider; var integrity = coll.GetComponent <Integrity>(); if (integrity == null) { return(false); } float newDamage = projectileKineticDamage.DamageByPressureModifier(damageData.Damage); integrity.ApplyDamage(newDamage, damageData.AttackType, damageData.DamageType); Chat.AddAttackMsgToChat( shooter, coll.gameObject, BodyPartType.None, weapon.gameObject); Logger.LogTraceFormat( "Hit {0} for {1} with Integrity! bullet absorbed", Category.Firearms, integrity.gameObject.name, newDamage); return(true); }
//We need to slow the attack down because clients are behind server private async Task AttackFleshRoutine(Vector2 dir, LivingHealthBehaviour targetHealth, LivingHealthMasterBase livingHealth, Vector3 worldPos, NetworkConnection ctc, float rtt) { if (targetHealth == null && livingHealth == null) { return; } if (ctc == null) { return; } ServerDoLerpAnimation(dir); if (PlayerManager.LocalPlayerScript != null && PlayerManager.LocalPlayerScript.playerHealth != null && PlayerManager.LocalPlayerScript.playerHealth == livingHealth || rtt < 0.02f) { //Wait until the end of the frame await Task.Delay(1); } else { //Wait until RTT/2 seconds? Logger.Log($"WAIT FOR ATTACK: {rtt / 2f}", Category.Mobs); await Task.Delay((int)(rtt * 500)); } if (Vector3.Distance(mobTile.WorldPositionServer, worldPos) < 1.5f) { var bodyPartTarget = defaultTarget.Randomize(); if (targetHealth != null) { targetHealth.ApplyDamageToBodyPart(gameObject, hitDamage, AttackType.Melee, DamageType.Brute, bodyPartTarget); Chat.AddAttackMsgToChat(gameObject, targetHealth.gameObject, bodyPartTarget, null, attackVerb); } else { livingHealth.ApplyDamageToBodyPart(gameObject, hitDamage, AttackType.Melee, DamageType.Brute, bodyPartTarget); Chat.AddAttackMsgToChat(gameObject, livingHealth.gameObject, bodyPartTarget, null, attackVerb); } SoundManager.PlayNetworkedAtPos(attackSound, mobTile.WorldPositionServer, sourceObj: gameObject); } }
private bool TryStun(RaycastHit2D hit) { var coll = hit.collider; var player = coll.GetComponent <RegisterPlayer>(); if (player == null) { return(false); } player.ServerStun(stunTime, willDisarm); Chat.AddAttackMsgToChat(shooter, coll.gameObject, targetZone, weapon.gameObject); Logger.LogTraceFormat($"{shooter} stunned {player.gameObject.name} for {stunTime} seconds with {weapon.name}", Category.Firearms); return(true); }
private void TryFacehug(Vector3 dir, LivingHealthBehaviour player) { var playerInventory = player.gameObject.GetComponent <PlayerScript>()?.Equipment; if (playerInventory == null) { return; } string verb; bool success; if (HasAntihuggerItem(playerInventory)) { verb = "tried to hug"; success = false; } else { verb = "hugged"; success = true; } mobMeleeAction.ServerDoLerpAnimation(dir); Chat.AddAttackMsgToChat( gameObject, player.gameObject, BodyPartType.Head, null, verb); SoundManager.PlayNetworkedAtPos( "bite", player.gameObject.RegisterTile().WorldPositionServer, 1f, true, player.gameObject); if (success) { RegisterPlayer registerPlayer = player.gameObject.GetComponent <RegisterPlayer>(); Facehug(playerInventory, registerPlayer); } }
private bool TryDamage(RaycastHit2D hit) { var coll = hit.collider; var integrity = coll.GetComponent <Integrity>(); if (integrity == null) { return(false); } integrity.ApplyDamage(damageData.Damage, damageData.AttackType, damageData.DamageType); Chat.AddAttackMsgToChat(shooter, coll.gameObject, BodyPartType.None, weapon.gameObject); Logger.LogTraceFormat("Hit {0} for {1} with HealthBehaviour! bullet absorbed", Category.Firearms, integrity.gameObject.name, damageData.Damage); return(true); }
/// <summary> /// Invoked when BulletColliderBehavior passes the event up to us. /// </summary> public void HandleTriggerEnter2D(Collider2D coll) { //only harm others if it's not a suicide if (coll.gameObject == shooter && !isSuicide) { return; } //only harm the shooter if it's a suicide if (coll.gameObject != shooter && isSuicide) { return; } //body or object? var livingHealth = coll.GetComponent <LivingHealthBehaviour>(); var integrity = coll.GetComponent <Integrity>(); if (integrity != null) { //damage object integrity.ApplyDamage(damage, attackType, damageType); Chat.AddAttackMsgToChat(shooter, coll.gameObject, BodyPartType.None, weapon.gameObject); Logger.LogTraceFormat("Hit {0} for {1} with HealthBehaviour! bullet absorbed", Category.Firearms, integrity.gameObject.name, damage); } else { //damage human if there is one if (livingHealth == null || livingHealth.IsDead) { return; } // Trigger for things like stuns GetComponent <BulletHitTrigger>()?.BulletHitInteract(coll.gameObject); var aim = isSuicide ? bodyAim : bodyAim.Randomize(); livingHealth.ApplyDamageToBodypart(shooter, damage, attackType, damageType, aim); Chat.AddAttackMsgToChat(shooter, coll.gameObject, aim, weapon.gameObject); Logger.LogTraceFormat("Hit {0} for {1} with HealthBehaviour! bullet absorbed", Category.Firearms, livingHealth.gameObject.name, damage); } DespawnThis(); }
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 TryFacehug(Vector3 dir, LivingHealthMasterBase livingHealth) { var playerInventory = livingHealth.gameObject.GetComponent <PlayerScript>()?.Equipment; if (playerInventory == null) { return; } string verb; bool success; if (HasAntihuggerItem(playerInventory)) { verb = "tried to hug"; success = false; } else { verb = "hugged"; success = true; } ServerDoLerpAnimation(dir); Chat.AddAttackMsgToChat( gameObject, livingHealth.gameObject, BodyPartType.Head, null, verb); AudioSourceParameters audioSourceParameters = new AudioSourceParameters(pitch: 1f); SoundManager.PlayNetworkedAtPos(bite, livingHealth.gameObject.RegisterTile().WorldPositionServer, audioSourceParameters, true, livingHealth.gameObject); if (success) { RegisterPlayer registerPlayer = livingHealth.gameObject.GetComponent <RegisterPlayer>(); Facehug(playerInventory, registerPlayer); } }
private bool TryDamage(RaycastHit2D hit) { var coll = hit.collider; var livingHealth = coll.GetComponent <LivingHealthBehaviour>(); if (livingHealth == null) { return(false); } livingHealth.ApplyDamageToBodypart(shooter, damageData.Damage, damageData.AttackType, damageData.DamageType, targetZone); Chat.AddAttackMsgToChat(shooter, coll.gameObject, targetZone, weapon.gameObject); Logger.LogTraceFormat("Hit {0} for {1} with HealthBehaviour! bullet absorbed", Category.Firearms, livingHealth.gameObject.name, damageData.Damage); return(true); }
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()); }
public void CmdRequestMeleeAttack(GameObject victim, GameObject weapon, Vector2 stabDirection, BodyPartType damageZone, LayerType layerType) { if (!playerMove.allowInput || playerScript.IsGhost || !victim || !playerScript.playerHealth.serverPlayerConscious ) { return; } if (!allowAttack) { return; } ItemAttributes weaponAttr = weapon.GetComponent <ItemAttributes>(); // 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) { //Wire cutters should snip the grills instead: if (weaponAttr.itemName == "wirecutters" && tileMapDamage.Layer.LayerType == LayerType.Grills) { tileMapDamage.WireCutGrill((Vector2)transform.position + stabDirection); StartCoroutine(AttackCoolDown()); return; } tileMapDamage.DoMeleeDamage((Vector2)transform.position + stabDirection, gameObject, (int)weaponAttr.hitDamage); playerMove.allowInput = false; RpcMeleeAttackLerp(stabDirection, weapon); StartCoroutine(AttackCoolDown()); return; } return; } //This check cannot be used with TilemapDamage as the transform position is always far away if (!playerScript.IsInReach(victim, true)) { return; } // Consider moving this into a MeleeItemTrigger for knifes //Meaty bodies: LivingHealthBehaviour victimHealth = victim.GetComponent <LivingHealthBehaviour>(); if (victimHealth != null && victimHealth.IsDead && weaponAttr.itemType == ItemType.Knife) { if (victim.GetComponent <SimpleAnimal>()) { SimpleAnimal attackTarget = victim.GetComponent <SimpleAnimal>(); RpcMeleeAttackLerp(stabDirection, weapon); playerMove.allowInput = false; attackTarget.Harvest(); SoundManager.PlayNetworkedAtPos("BladeSlice", transform.position); } else { PlayerHealth attackTarget = victim.GetComponent <PlayerHealth>(); RpcMeleeAttackLerp(stabDirection, weapon); playerMove.allowInput = false; attackTarget.Harvest(); SoundManager.PlayNetworkedAtPos("BladeSlice", transform.position); } } if (victim != gameObject) { RpcMeleeAttackLerp(stabDirection, weapon); playerMove.allowInput = false; } var integrity = victim.GetComponent <Integrity>(); if (integrity != null) { //damaging an object integrity.ApplyDamage((int)weaponAttr.hitDamage, AttackType.Melee, weaponAttr.damageType); } else { //damaging a living thing victimHealth.ApplyDamage(gameObject, (int)weaponAttr.hitDamage, AttackType.Melee, weaponAttr.damageType, damageZone); } SoundManager.PlayNetworkedAtPos(weaponAttr.hitSound, transform.position); if (weaponAttr.hitDamage > 0) { Chat.AddAttackMsgToChat(gameObject, victim, damageZone, weapon); } StartCoroutine(AttackCoolDown()); }