public void TakeDamage(ActorHitInfo data) { if (MetaState.Instance.SessionFlags.Contains("GodMode") || GameState.Instance.PlayerFlags.Contains(PlayerFlags.Invulnerable) || IsDying) { return; } CharacterModel playerModel = GameState.Instance.PlayerRpgState; //damage model is very stupid right now, we will make it better later var(dt, dr) = playerModel.GetDamageThresholdAndResistance(data.DamageType); float damageTaken = RpgValues.DamageTaken(data.Damage, data.DamagePierce, dt, dr); if (data.HitLocation == (int)ActorBodyPart.Head) { damageTaken *= 2.0f; } else if (data.HitLocation == (int)ActorBodyPart.LeftArm || data.HitLocation == (int)ActorBodyPart.LeftLeg || data.HitLocation == (int)ActorBodyPart.RightArm || data.HitLocation == (int)ActorBodyPart.RightLeg) { damageTaken *= 0.75f; } if (damageTaken > 0) { if (PainSound != null && !PainSound.isPlaying) { PainSound.Play(); } } playerModel.Health -= damageTaken; }
private void DoRadiusDamage() { //do radius damage var bulletHitInfo = BulletScript.HitInfo; ActorHitInfo hitInfo = new ActorHitInfo(Damage, 0, bulletHitInfo.DamageType, (int)DefaultDamageEffectors.Explosion, false, 0, 0, bulletHitInfo.Originator, bulletHitInfo.OriginatorFaction, HitPuff, null, bulletHitInfo.HitFlags); //TODO we copy flags, should we also copy ExtraFlags and ExtraData? WorldUtils.RadiusDamage(transform.position, Radius, UseFalloff, true, false, false, false, hitInfo); Triggered = true; }
/// <summary> /// Calculates applied damage given hitinfo and armor values /// </summary> public static float DamageTaken(ActorHitInfo hitInfo, float threshold, float resistance) { float damage = hitInfo.Damage; float damagePierce = hitInfo.DamagePierce; if (hitInfo.HitFlags.HasFlag(BuiltinHitFlags.IgnoreArmor)) { return(damage + damagePierce); } if (hitInfo.HitFlags.HasFlag(BuiltinHitFlags.PierceConsiderArmor)) { damage += damagePierce; damagePierce = 0; } float d1 = damage * ((100f - Mathf.Min(resistance, 99f)) / 100f); float dt = Mathf.Max(0, threshold); //threshold-pierce float d2 = Mathf.Max(d1 - dt, damage * 0.1f); float dp = damagePierce; return(d2 + dp); }
public void TakeDamage(ActorHitInfo data) { if (MetaState.Instance.SessionFlags.Contains("GodMode") || GameState.Instance.PlayerFlags.Contains(PlayerFlags.Invulnerable) || IsDying) { return; } if (!data.HarmFriendly) { string hitFaction = data.OriginatorFaction; if (!string.IsNullOrEmpty(hitFaction)) { FactionRelationStatus relation = FactionModel.GetRelation(hitFaction, PredefinedFaction.Player.ToString()); //this looks backwards but it's because we're checking if the Bullet is-friendly-to the Actor if (relation == FactionRelationStatus.Friendly) { return; //no friendly fire } } } if (DamageHandler != null) { var hitOut = DamageHandler(data); if (hitOut.HasValue) { data = hitOut.Value; } else { return; } } CharacterModel playerModel = GameState.Instance.PlayerRpgState; var(damageToShields, damageToArmor, damageToCharacter) = RpgValues.DamageRatio(data.Damage, data.DamagePierce, playerModel); float oldShields = playerModel.Shields; playerModel.Shields -= damageToShields; if (oldShields > 0 && playerModel.Shields <= 0) { MessageInterface.PushToBus(new QdmsFlagMessage("PlayerShieldsLost")); ShieldComponent.Ref()?.SignalLostShields(); } var(dt, dr) = playerModel.GetDamageThresholdAndResistance(data.DamageType); float damageTaken = RpgValues.DamageTaken(damageToArmor, damageToCharacter, dt, dr); if (data.HitLocation == (int)ActorBodyPart.Head) { damageTaken *= 2.0f; } else if (data.HitLocation == (int)ActorBodyPart.LeftArm || data.HitLocation == (int)ActorBodyPart.LeftLeg || data.HitLocation == (int)ActorBodyPart.RightArm || data.HitLocation == (int)ActorBodyPart.RightLeg) { damageTaken *= 0.75f; } playerModel.Health -= damageTaken; if (damageTaken > PainSoundThreshold) { if (PainSound != null && !PainSound.isPlaying) { PainSound.Play(); } } if (damageToShields > 0 || damageTaken > 0) { ShieldComponent.Ref()?.SignalTookDamage(damageToShields, damageTaken); var damageValues = new Dictionary <string, object>() { { "DamageTaken", damageTaken }, { "DamageToShields", damageToShields }, { "DamageToArmor", damageToArmor }, { "DamageToCharacter", damageToCharacter } }; MessageInterface.PushToBus(new QdmsKeyValueMessage(damageValues, "PlayerTookDamage")); } }
public static (float damageToShields, float damageToArmor, float damageToCharacter) DamageRatio(ActorHitInfo hitInfo, CharacterModel character) { //for now we'll keep returning all 3 values but we'll probably combine the functionality of DamageTaken into this as well and just spit out "damage to shields" and "raw damage" float damage = hitInfo.Damage; float damagePierce = hitInfo.DamagePierce; if (character.DerivedStats.ShieldParams.MaxShields == 0 || character.Shields == 0 || hitInfo.HitFlags.HasFlag(BuiltinHitFlags.IgnoreShields)) { if (hitInfo.HitFlags.HasFlag(BuiltinHitFlags.PierceConsiderArmor)) { damage += damagePierce; damagePierce = 0; } return(0, damage, damagePierce); } //keep it simple for now float shields = character.Shields; float leakRate = character.DerivedStats.ShieldParams.LeakRate; float damageToShieldsFromPierce = 0; if (hitInfo.HitFlags.HasFlag(BuiltinHitFlags.PierceConsiderShields)) { damageToShieldsFromPierce = Mathf.Min(shields, damagePierce - (damagePierce * leakRate)); damagePierce -= damageToShieldsFromPierce; } float damageToShields = Mathf.Min(Mathf.Max(0, shields - damageToShieldsFromPierce), (damage - (damage * leakRate))); float damageToArmor = damage - damageToShields; if (hitInfo.HitFlags.HasFlag(BuiltinHitFlags.PierceConsiderArmor)) { damageToArmor += damagePierce; damagePierce = 0; } return(damageToShields, damageToArmor, damagePierce); }
public void TakeDamage(ActorHitInfo data) { //damage model is very stupid right now, we will make it better later float dt = data.DamageType < DamageThreshold.Length ? DamageThreshold[(int)data.DamageType] : 0f; float dr = data.DamageType < DamageThreshold.Length ? DamageResistance[(int)data.DamageType] : 0f; float damageTaken = RpgValues.DamageTaken(data.Damage, data.DamagePierce, dt, dr); if (data.HitLocation == (int)ActorBodyPart.Head) { damageTaken *= 2.0f; } else if (data.HitLocation == (int)ActorBodyPart.LeftArm || data.HitLocation == (int)ActorBodyPart.LeftLeg || data.HitLocation == (int)ActorBodyPart.RightArm || data.HitLocation == (int)ActorBodyPart.RightLeg) { damageTaken *= 0.75f; } damageTaken *= (1f / ConfigState.Instance.GetGameplayConfig().Difficulty.ActorStrength); if (!Invincible) { Health -= damageTaken; } /* * if(!string.IsNullOrEmpty(data.HitPuff)) * { * Debug.Log($"Spawning hitpuff \"{data.HitPuff}\" at {data.HitCoords}"); * Vector3 hitCoords = data.HitCoords.HasValue ? data.HitCoords.Value : transform.position; * WorldUtils.SpawnEffect(data.HitPuff, hitCoords, transform.eulerAngles, null); * } * else if(!string.IsNullOrEmpty(DefaultHitPuff)) * { * Vector3 hitCoords = data.HitCoords.HasValue ? data.HitCoords.Value : transform.position; * WorldUtils.SpawnEffect(DefaultHitPuff, hitCoords, transform.eulerAngles, null); * } */ //we no longer spawn hitpuffs here if (CurrentAiState == ActorAiState.Dead) //abort if we're already dead { return; } bool didTakePain = UnityEngine.Random.Range(0f, 1f) < PainChance; if (Defensive && data.Originator != null && data.Originator != this) { FactionRelationStatus relation = FactionRelationStatus.Neutral; if (data.Originator is PlayerController) { relation = FactionModel.GetRelation(Faction, "Player"); } else if (data.Originator is ActorController) { relation = FactionModel.GetRelation(Faction, ((ActorController)data.Originator).Faction); } if (relation != FactionRelationStatus.Friendly || Infighting) { Target = data.Originator.transform; BeenHit = true; if (DisableInteractionOnHit && InteractionComponent != null) { InteractionComponent.InteractionDisabledByHit = true; } if (FeelPain && didTakePain) { EnterState(ActorAiState.Hurting); } else { EnterState(ActorAiState.Chasing); } } else { EnterState(ActorAiState.Hurting); } } else if (FeelPain && didTakePain) { EnterState(ActorAiState.Hurting); } }
public void TakeDamage(ActorHitInfo data) { if (!data.HarmFriendly) { string hitFaction = data.OriginatorFaction; if (!string.IsNullOrEmpty(hitFaction)) { FactionRelationStatus relation = FactionModel.GetRelation(hitFaction, Faction); //this looks backwards but it's because we're checking if the Bullet is-friendly-to the Actor if (relation == FactionRelationStatus.Friendly) { return; //no friendly fire } } } //damage model is very stupid right now, we will make it better later float dt = data.DamageType < DamageThreshold.Length ? DamageThreshold[(int)data.DamageType] : 0f; float dr = data.DamageType < DamageThreshold.Length ? DamageResistance[(int)data.DamageType] : 0f; float damageTaken = RpgValues.DamageTaken(data.Damage, data.DamagePierce, dt, dr); if (data.HitLocation == (int)ActorBodyPart.Head) { damageTaken *= 2.0f; } else if (data.HitLocation == (int)ActorBodyPart.LeftArm || data.HitLocation == (int)ActorBodyPart.LeftLeg || data.HitLocation == (int)ActorBodyPart.RightArm || data.HitLocation == (int)ActorBodyPart.RightLeg) { damageTaken *= 0.75f; } damageTaken *= (1f / ConfigState.Instance.GetGameplayConfig().Difficulty.ActorStrength); if (!Invincible) { Health -= damageTaken; } if (CurrentAiState == ActorAiState.Dead) //abort if we're already dead { return; } bool didTakePain = UnityEngine.Random.Range(0f, 1f) < PainChance; //shouldn't this be weighted by damage? if (Defensive && data.Originator != null && data.Originator != this) { FactionRelationStatus relation = FactionRelationStatus.Neutral; if (data.Originator is PlayerController) { relation = FactionModel.GetRelation(Faction, "Player"); } else if (data.Originator is ActorController) { relation = FactionModel.GetRelation(Faction, ((ActorController)data.Originator).Faction); } if (relation != FactionRelationStatus.Friendly || Infighting) { Target = data.Originator.transform; BeenHit = true; if (DisableInteractionOnHit && InteractionComponent != null) { InteractionComponent.InteractionDisabledByHit = true; } if (FeelPain && didTakePain) { if (CurrentAiState != ActorAiState.Hurting) { EnterState(ActorAiState.Hurting); } else { EnterState(ActorAiState.Chasing); } } } else if (CurrentAiState != ActorAiState.Hurting) { EnterState(ActorAiState.Hurting); } } else if (FeelPain && didTakePain && CurrentAiState != ActorAiState.Hurting) { EnterState(ActorAiState.Hurting); } }
/// <summary> /// Calculates damage to shields, armor, and character given damage and a character model /// </summary> public static (float damageToShields, float damageToArmor, float damageToCharacter) DamageRatio(ActorHitInfo hitInfo, CharacterModel character) => DamageRatioImpl(hitInfo, character);
//both the damage methods below need to be reworked/replaced to handle flags among other things /// <summary> /// Calculates applied damage given input damage and resistance /// </summary> public static float DamageTaken(ActorHitInfo hitInfo, float threshold, float resistance) //this is a dumb spot and we will move it later => DamageTakenImpl(hitInfo, threshold, resistance);
public void TakeDamage(ActorHitInfo data) { LastHit = null; LastHitDamage = 0; if (!data.HarmFriendly) { string hitFaction = data.OriginatorFaction; if (!string.IsNullOrEmpty(hitFaction)) { FactionRelationStatus relation = GameState.Instance.FactionState.GetRelation(hitFaction, Faction); //this looks backwards but it's because we're checking if the Bullet is-friendly-to the Actor if (relation == FactionRelationStatus.Friendly) { return; //no friendly fire } } } ActorDamageHandlerResult?damageHandlerResult = null; if (DamageHandler != null) { damageHandlerResult = DamageHandler(data); if (damageHandlerResult.Value.HitInfo.HasValue) { data = damageHandlerResult.Value.HitInfo.Value; } else { return; } } LastHit = data; float damageTaken; if (damageHandlerResult?.DamageTaken != null) { damageTaken = damageHandlerResult.Value.DamageTaken.Value; } else { //new way of doing dr/dt float dt = 0, dr = 0; foreach (var dNode in DamageResistances) { if ((int)dNode.DamageType == data.DamageType) { dt = dNode.DamageThreshold; dr = dNode.DamageResistance; } } damageTaken = RpgValues.DamageTaken(data, dt, dr); if (!data.HitFlags.HasFlag(BuiltinHitFlags.IgnoreHitLocation)) { if (data.HitLocation == (int)ActorBodyPart.Head) { damageTaken *= 2.0f; //do we want more flexibility here? } else if (data.HitLocation == (int)ActorBodyPart.LeftArm || data.HitLocation == (int)ActorBodyPart.LeftLeg || data.HitLocation == (int)ActorBodyPart.RightArm || data.HitLocation == (int)ActorBodyPart.RightLeg) { damageTaken *= 0.75f; } } damageTaken *= (1f / ConfigState.Instance.GetGameplayConfig().Difficulty.ActorStrength); } if (!Invincible) { LastHitDamage = damageTaken; Health -= damageTaken; } //handle extreme death if (damageHandlerResult?.ExtremeDeath != null) { WasExtremeDeath = damageHandlerResult.Value.ExtremeDeath.Value; } else { if (data.HitFlags.HasFlag(BuiltinHitFlags.AlwaysExtremeDeath)) { WasExtremeDeath = true; } else if (data.HitFlags.HasFlag(BuiltinHitFlags.NeverExtremeDeath)) { WasExtremeDeath = false; } else { if (ExtremeDeathThreshold > 0) { //interpret as -(maxhealth * threshold) WasExtremeDeath = Health < (-(MaxHealth * ExtremeDeathThreshold)); } else if (ExtremeDeathThreshold < 0) { //interpret as absolute value WasExtremeDeath = Health < ExtremeDeathThreshold; } else { //ExtremeDeathThreshold == 0, no extreme death WasExtremeDeath = false; } } } //TODO do we force into death state here? //TODO consider moving this, but probably wait until we start thinking about abuse of corpses if (CurrentAiState == ActorAiState.Dead) //abort if we're already dead { return; } bool didTakePain; if (damageHandlerResult?.TookPain != null) { didTakePain = damageHandlerResult.Value.TookPain.Value; } else { float derivedPainChance = PainChance; if (PainGuaranteeThreshold != 0) { float damageForMaxPain = Mathf.Abs(PainGuaranteeRelative ? (PainGuaranteeThreshold * MaxHealth) : PainGuaranteeThreshold); derivedPainChance = MathUtils.ScaleRange(damageTaken, 0, damageForMaxPain, PainChance, 1); } derivedPainChance = Mathf.Min(derivedPainChance, PainMaxChance); didTakePain = (data.HitFlags.HasFlag(BuiltinHitFlags.AlwaysPain) || UnityEngine.Random.Range(0f, 1f) < derivedPainChance) && !data.HitFlags.HasFlag(BuiltinHitFlags.NoPain); //TODO shouldn't this be weighted by damage? } if (Defensive && data.Originator != null && data.Originator != this && !data.HitFlags.HasFlag(BuiltinHitFlags.NeverAlert)) { FactionRelationStatus relation = FactionRelationStatus.Neutral; if (data.Originator is PlayerController) { relation = GameState.Instance.FactionState.GetRelation(Faction, "Player"); } else if (data.Originator is ActorController) { relation = GameState.Instance.FactionState.GetRelation(Faction, ((ActorController)data.Originator).Faction); } if (relation != FactionRelationStatus.Friendly || Infighting) { Target = data.Originator.transform; BeenHit = true; if (DisableInteractionOnHit && InteractionComponent != null) { InteractionComponent.InteractionDisabledByHit = true; } if (FeelPain && didTakePain && CurrentAiState != ActorAiState.ScriptedAction && CurrentAiState != ActorAiState.ScriptedMoveTo) { if (PainStateAllowRestart || CurrentAiState != ActorAiState.Hurting) { EnterState(ActorAiState.Hurting); } } else if (CurrentAiState != ActorAiState.Chasing && CurrentAiState != ActorAiState.Attacking && CurrentAiState != ActorAiState.ScriptedAction && CurrentAiState != ActorAiState.ScriptedMoveTo) { EnterState(ActorAiState.Chasing); } } else if ((PainStateAllowRestart || CurrentAiState != ActorAiState.Hurting) && FeelPain && CurrentAiState != ActorAiState.ScriptedAction && CurrentAiState != ActorAiState.ScriptedMoveTo) { EnterState(ActorAiState.Hurting); } } else if (FeelPain && didTakePain && (PainStateAllowRestart || CurrentAiState != ActorAiState.Hurting) && CurrentAiState != ActorAiState.ScriptedAction && CurrentAiState != ActorAiState.ScriptedMoveTo) { EnterState(ActorAiState.Hurting); } }
public void DoAttack() { Vector3 shootPos = ShootPoint == null ? (transform.position + (transform.forward * 0.6f) + (transform.up * 1.25f)) : ShootPoint.position; if (UseSuicide) { //ActorController.TakeDamage(new ActorHitInfo(0, Mathf.Min(ActorController.MaxHealth * 100, float.MaxValue), 0, 0, 0, ActorController)); ActorController.Health = 0; } else { var target = ActorController.Target; Vector3 aimPoint = target.position; var targetAC = target.GetComponent <ActorController>(); if (targetAC != null && targetAC.TargetPoint != null) { aimPoint = targetAC.TargetPoint.position; } var targetPC = target.GetComponent <PlayerController>(); if (targetPC != null && targetPC.TargetPoint != null) { aimPoint = targetPC.TargetPoint.position; } aimPoint.y += UnityEngine.Random.Range(-AttackSpread, AttackSpread); aimPoint.x += UnityEngine.Random.Range(-AttackSpread, AttackSpread); aimPoint.z += UnityEngine.Random.Range(-AttackSpread, AttackSpread); Vector3 shootVec = (aimPoint - shootPos).normalized; //I screwed this up the first time float randomFactor = Mathf.Max(0, 1 + UnityEngine.Random.Range(-AttackRandomFactor, AttackRandomFactor)); var modHit = new ActorHitInfo(AttackHit); var gameplayConfig = ConfigState.Instance.GetGameplayConfig(); modHit.Damage *= gameplayConfig.Difficulty.ActorStrength * randomFactor; modHit.DamagePierce *= gameplayConfig.Difficulty.ActorStrength * randomFactor; modHit.Originator = ActorController; if (FriendlyFire == FriendlyFireMode.Always) { modHit.HarmFriendly = true; } else if (FriendlyFire == FriendlyFireMode.Never) { modHit.HarmFriendly = false; } else { modHit.HarmFriendly = GameParams.UseFriendlyFire; } if (string.IsNullOrEmpty(modHit.OriginatorFaction)) { modHit.OriginatorFaction = ActorController.Faction; } modHit.HitFlags = TypeUtils.FlagsFromCollection(AttackHitFlags); if (UseMelee) { if (AutoDamageEffector) { modHit.DamageEffector = (int)DamageEffector.Melee; } //melee path (raycast) LayerMask lm = WorldUtils.GetAttackLayerMask(); //TODO 2D/3D attack range, or just increase attack range? var rc = Physics.RaycastAll(shootPos, shootVec, AttackRange, lm, QueryTriggerInteraction.Collide); BaseController ac = null; foreach (var r in rc) { var go = r.collider.gameObject; var ahgo = go.GetComponent <IHitboxComponent>(); if (ahgo != null) { ac = ahgo.ParentController; break; } var acgo = go.GetComponent <ActorController>(); if (acgo != null) { ac = acgo; break; } } if (ac != null) { if (ac is ITakeDamage itd) { itd.TakeDamage(modHit); } } } else if (BulletPrefab != null) { if (AutoDamageEffector) { modHit.DamageEffector = (int)DamageEffector.Projectile; } //bullet path (shoot) //var bullet = Instantiate<GameObject>(BulletPrefab, shootPos + (shootVec * 0.25f), Quaternion.identity, transform.root); Quaternion bulletRotation = Quaternion.LookRotation(shootVec.normalized, Vector3.up); var bullet = WorldUtils.SpawnEffect(BulletPrefab, shootPos + (shootVec * 0.25f), bulletRotation.eulerAngles, transform.root); var bulletRigidbody = bullet.GetComponent <Rigidbody>(); bulletRigidbody.velocity = (shootVec * BulletSpeed); var bulletScript = bullet.GetComponent <BulletScript>(); bulletScript.HitInfo = modHit; bulletScript.Target = target; } else { CDebug.LogEx(string.Format("{0} tried to shoot a bullet, but has no prefab defined!", name), LogLevel.Error, this); } } //show the effect, if applicable if (!string.IsNullOrEmpty(AttackEffectPrefab)) { WorldUtils.SpawnEffect(AttackEffectPrefab, shootPos, Vector3.zero, ParentAttackEffect ? (ShootPoint == null ? transform : ShootPoint) : null); //Instantiate(AttackEffectPrefab, shootPos, Quaternion.identity, (ShootPoint == null ? transform : ShootPoint)); } if (AttackSound != null) { AttackSound.Play(); } DidAttack = true; LastAttackTime = Time.time; }