/// <summary> /// Take damage from an attacker /// </summary> /// <param name="attacker">Stats controller of the attacking entity</param> /// <param name="damageValue">Approximate damage value to apply to enemy health</param> /// <param name="timeDelta">Time since last damage calculation</param> public override void TakeDamage(EntityStatsController attacker, float damageValue, float timeDelta = 1f) { // Ignore attacks if already dead or invincible if (isDead || invincible) { return; } float colourDamagePercentage = characterColour == CharacterColour.All || attacker.characterColour == characterColour ? 1 : colourResistanceModifier; // Calculate any changes based on stats and modifiers here first float hitValue = Mathf.Max(colourDamagePercentage * (damageValue - ComputeDefenseModifier()), 0) * timeDelta; health.Subtract(hitValue); ShowDamage(hitValue); Anim.SetTrigger("TakeDamage"); // Pass damage information to brain Brain.OnDamageTaken(attacker.gameObject, hitValue); if (Mathf.Approximately(health.CurrentValue, 0f)) { Die(); } }
public void Interactable_CanInteract_FalseIfOutOfRange() { GameObject gameObject = new GameObject(); Transform objPos = gameObject.GetComponent <Transform>(); objPos.position = Vector3.up; //position (0,1,0) Interactable interactable = gameObject.AddComponent <Interactable>(); interactable.colour = CharacterColour.Red; //set target and 'player' colour to RED //variables for 'player' GameObject player = new GameObject(); Transform playerPos = player.GetComponent <Transform>(); playerPos.position = Vector3.zero; //position (0,0,0) EntityStatsController playerControl = player.AddComponent <EntityStatsController>(); playerControl.characterColour = CharacterColour.Red;//set player colour to Red to start Assert.False(interactable.CanInteract(playerPos)); objPos.position = new Vector3(0, 2, 0); //position (0,2,0) //interactible radius is still 1, test it is now out of range Assert.False(interactable.CanInteract(playerPos)); objPos.position = new Vector3(235463, 235463, 235463); //arbitrarily far //interactible radius is still 1, test it is now out of range Assert.False(interactable.CanInteract(playerPos)); Object.DestroyImmediate(gameObject); //Object.Destroy(player); }
/// <summary> /// Take damage from an attacker /// </summary> /// <param name="attacker">Stats controller of the attacking entity</param> /// <param name="damageValue">Approximate damage value to apply to enemy health</param> /// <param name="timeDelta">Time since last damage calculation</param> public override void TakeDamage(EntityStatsController attacker, float damageValue, float timeDelta = 1f) { // Ignore attacks if already dead if (isDead) { return; } if (characterColour != CharacterColour.All && attacker.characterColour != characterColour) { return; } // Calculate any changes based on stats and modifiers here first float hitValue = Mathf.Max(damageValue - ComputeDefenseModifier(), 0) * timeDelta; health.Subtract(hitValue); ShowDamage(hitValue); Anim.SetTrigger("TakeDamage"); // Pass damage information to brain _brain.OnDamageTaken(attacker.gameObject, hitValue); if (Mathf.Approximately(health.CurrentValue, 0f)) { Die(); } }
private void OnTriggerEnter(Collider other) { GameObject col = other.gameObject; // if other object is a player then deal damage if (col.CompareTag("Player")) { EntityStatsController target = col.GetComponent <EntityStatsController>(); target.TakeDamage(Random.Range(minDamage, maxDamage)); gameObject.SetActive(false); } else if (col.CompareTag("Ground")) { // Damage any players within impact radius float damage = Random.Range(minDamage, maxDamage); foreach (GameObject player in PlayerManager.Instance.AlivePlayers) { if (Vector3.Distance(transform.position, col.transform.position) <= impactRadius) { player.GetComponent <EntityStatsController>().TakeDamage(damage); } } gameObject.SetActive(false); } }
public void Collectable_StartInteract_ObjectDeletedAfterInteract() { GameObject gameObject = new GameObject(); Transform objPos = gameObject.GetComponent <Transform>(); objPos.position = Vector3.zero; //position (0,0,0) Collectable collectable = gameObject.AddComponent <Collectable>(); collectable.radius = 0f; //will not test as many for this collectable.colour = CharacterColour.Red; //set target and 'player' colour to same colour //variables for 'player' GameObject player = new GameObject(); Transform playerPos = player.GetComponent <Transform>(); playerPos.position = Vector3.zero; //position (0,0,0) EntityStatsController playerControl = player.AddComponent <EntityStatsController>(); playerControl.characterColour = CharacterColour.Red;//set player colour to Red to start Assert.IsNotNull(gameObject); //we will have the player and collectable interact collectable.StartInteract(playerPos); Assert.IsNull(gameObject); Object.Destroy(gameObject); Object.Destroy(player); }
public void Interactable_CanInteract_FalseIfOutOfRangeAndWrongColour() //test a few to make sure double negative does not result in positive { GameObject gameObject = new GameObject(); Transform objPos = gameObject.GetComponent <Transform>(); objPos.position = Vector3.up; //position (0,1,0) Interactable interactable = gameObject.AddComponent <Interactable>(); interactable.colour = CharacterColour.Red; //set target and 'player' colour to different colours //variables for 'player' GameObject player = new GameObject(); Transform playerPos = player.GetComponent <Transform>(); playerPos.position = Vector3.zero; //position (0,0,0) EntityStatsController playerControl = player.AddComponent <EntityStatsController>(); playerControl.characterColour = CharacterColour.Green;//set player colour to Green to start Assert.False(interactable.CanInteract(playerPos)); objPos.position = new Vector3(0, 2, 0); //position (0,2,0) //interactible radius is still 1, test it is out of range Assert.False(interactable.CanInteract(playerPos)); //arbitrarily large number 235463 objPos.position = new Vector3(732363, 732363, 732363); //arbitrarily far //interactible radius is still 0.9999, test it is now out of range Assert.False(interactable.CanInteract(playerPos)); Object.DestroyImmediate(gameObject); Object.DestroyImmediate(player); }
public void Launch(EntityStatsController launcherStats, Vector3 direction, float launchForce, float range, float damageAmount, float healAmount) { // Store the damage amount and call the base launch function _damageAmount = damageAmount; _healAmount = healAmount; Launch(launcherStats, direction, launchForce, range); }
protected override void OnTriggerEnter(Collider other) { GameObject col = other.gameObject; // Ignore collisions with launcher or if currently being held if (col == LauncherStats.gameObject || _interactable.IsPickedUp) { return; } // if other object is a player, deal damage if (col.CompareTag("Player")) { EntityStatsController target = col.GetComponent <EntityStatsController>(); target.TakeDamage(LauncherStats, Damage); } else if (col.CompareTag("Enemy") && LaunchedByPlayer) { RhakStatsController target = col.GetComponent <RhakStatsController>(); // Ensure that the boss was hit if (target) { target.TakeDamageFromOrb(_colour); } } // Don't worry about collisions with colliders that are triggers if (!other.isTrigger) { gameObject.SetActive(false); } }
/// <summary> /// Perform damage on a target /// </summary> /// <param name="targetStats">Target's stats</param> /// <param name="damageValue">Damage value to apply</param> /// <param name="damageDelay">Time to wait before damage is applied</param> protected IEnumerator PerformDamage(EntityStatsController targetStats, float damageValue, float damageDelay = 0f) { if (damageDelay > 0f) { yield return(new WaitForSeconds(damageDelay)); } // Applies damage to targetStats targetStats.TakeDamage(Stats, damageValue); }
public void Interactable_CanInteract_TrueIfInRangeAndColourMatches() {//set up testing variables for 'target' GameObject gameObject = new GameObject(); Transform objPos = gameObject.GetComponent <Transform>(); objPos.position = Vector3.one; //set both this and the 'player' to the same position Interactable interactable = gameObject.AddComponent <Interactable>(); interactable.colour = CharacterColour.Red; //set target and 'player' colour to red //target.GetComponent<EntityStatsController>().characterColour //variables for 'player' GameObject player = new GameObject(); Transform playerPos = player.GetComponent <Transform>(); playerPos.position = Vector3.one; EntityStatsController playerControl = player.AddComponent <EntityStatsController>(); playerControl.characterColour = CharacterColour.Red;//set player colour to red as well //assert that we can pick up an object in the same position as us, which matches colour Assert.True(interactable.CanInteract(playerPos)); //assert for each additional colour interactable.colour = CharacterColour.Green; playerControl.characterColour = CharacterColour.Green; Assert.True(interactable.CanInteract(playerPos)); interactable.colour = CharacterColour.Yellow; playerControl.characterColour = CharacterColour.Yellow; Assert.True(interactable.CanInteract(playerPos)); interactable.colour = CharacterColour.Blue; playerControl.characterColour = CharacterColour.Blue; Assert.True(interactable.CanInteract(playerPos)); //assert for COLOURLESS object, with each player colour //at this point, player colour is still purple, so we don't need to set it again interactable.colour = CharacterColour.All; Assert.True(interactable.CanInteract(playerPos)); //then check for each other colour with the interactable being NONE playerControl.characterColour = CharacterColour.Red; Assert.True(interactable.CanInteract(playerPos)); playerControl.characterColour = CharacterColour.Yellow; Assert.True(interactable.CanInteract(playerPos)); playerControl.characterColour = CharacterColour.Green; Assert.True(interactable.CanInteract(playerPos)); Object.DestroyImmediate(gameObject); Object.DestroyImmediate(player); }
/// <summary> /// Perform explosive damage on a target /// </summary> /// <param name="targetStats">Target's stats</param> /// <param name="maxDamage">Maximum damage able to be dealt</param> /// <param name="stunTime">Number of seconds to stun damaged targets for</param> /// <param name="explosionForce">Force of the explosion</param> /// <param name="explosionPoint">Explosion origin</param> /// <param name="explosionRadius">Explosion effect radius</param> /// <param name="explosionDelay">Number of seconds before explosion affects targets</param> protected IEnumerator PerformExplosiveDamage(EntityStatsController targetStats, float maxDamage, float stunTime, float explosionForce, Vector3 explosionPoint, float explosionRadius, float explosionDelay = 0f) { if (explosionDelay > 0f) { yield return(new WaitForSeconds(explosionDelay)); } // Applies damage to targetStats targetStats.TakeExplosionDamage(Stats, maxDamage, stunTime, explosionForce, explosionPoint, explosionRadius); }
/// <summary> /// Launch the damage projectile /// </summary> /// <param name="launcherStats">Stats of the entity that launched the projectile</param> /// <param name="direction">Direction to launch in</param> /// <param name="launchForce">Force to apply to the projectile upon launch</param> /// <param name="range">Maximum range that the projectile can fly</param> /// <param name="damage">Damage of the projectile</param> /// <param name="targetTag">The tag of the target type</param> public virtual void Launch(EntityStatsController launcherStats, Vector3 direction, float launchForce, float range, float damage, string targetTag = "Enemy") { // Store the damage amount and call the base launch function Damage = damage; TargetTag = targetTag; if (targetTag == "Player") { AllyTag = "Enemy"; } Launch(launcherStats, direction, launchForce, range); }
/// <summary> /// Place the explosive trap /// </summary> /// <param name="thrower">Stats of the entity that placed the trap</param> /// <param name="position">Position to place the trap</param> /// <returns></returns> public void PlaceTrap(EntityStatsController thrower, Vector3 position) { _thrower = thrower; _isDetonated = false; transform.position = position; _isArmed = false; // Set self active gameObject.SetActive(true); StartCoroutine("ArmTrap"); }
public void Setup() { entityStats = new GameObject().AddComponent <EntityStatsController>(); entityStats.damage = new Stat(); entityStats.defense = new Stat(); entityStats.health = new RegenerableStat(); entityStats.damage.BaseValue = 10; entityStats.defense.BaseValue = 10; entityStats.health.maxValue = 100f; entityStats.health.Init(); }
protected override void OnTriggerEnter(Collider other) { GameObject col = other.gameObject; // if other object is an enemy, deal damage if (col.CompareTag(TargetTag)) { EntityStatsController target = col.GetComponent <EntityStatsController>(); target.TakeDamage(LauncherStats, Damage); } // Don't worry about collisions with the launcher or colliders that are triggers if (col != LauncherStats.gameObject && !other.isTrigger && other.tag != AllyTag) { gameObject.SetActive(false); } }
protected virtual void Awake() { Stats = GetComponent <EntityStatsController>(); Anim = GetComponentInChildren <Animator>(); Audio = GetComponents <AudioSource>(); // Assign audio sources for weapon and vocal SFX // There should be 2 audio sources to handle both SFX types playing concurrently if (Audio.Length == 2) { WeaponAudio = Audio[0]; VocalAudio = Audio[1]; } else if (Audio.Length < 2) { WeaponAudio = Audio[0]; VocalAudio = Audio[0]; } }
protected override void OnTriggerEnter(Collider other) { GameObject col = other.gameObject; // if other object is an enemy, deal damage if (col.CompareTag(TargetTag)) { EntityStatsController target = col.GetComponent <EntityStatsController>(); target.StartCoroutine(other.transform.GetComponent <PlayerMotorController>().ApplyTimedMovementModifier(0.35f, 1.5f)); target.TakeDamage(LauncherStats, Damage); } // Don't worry about collisions with the launcher or colliders that are triggers if (col != LauncherStats.gameObject && !other.isTrigger && other.tag != AllyTag) { InstantiateExplosion(); gameObject.SetActive(false); } }
/// <summary> /// Launch the damage projectile /// </summary> /// <param name="launcherStats">Stats of the entity that launched the projectile</param> /// <param name="direction">Direction to launch in</param> /// <param name="launchForce">Force to apply to the projectile upon launch</param> /// <param name="range">Maximum range that the projectile can fly</param> public void Launch(EntityStatsController launcherStats, Vector3 direction, float launchForce, float range) { LauncherStats = launcherStats; _range = range; // Set position just in front of launcher _initialPosition = launcherStats.transform.position + launcherStats.transform.forward; _initialPosition.y = launchHeight; transform.position = _initialPosition; // Set rotation to launch direction transform.rotation = Quaternion.LookRotation(direction); // Set self active and begin launch gameObject.SetActive(true); // Apply launch force to the rigidbody _rb.velocity = Vector3.zero; _rb.angularVelocity = Vector3.zero; _rb.AddForce(launchForce * transform.forward); }
/// <summary> /// Take damage from an attacker /// </summary> /// <param name="attacker">Stats controller of the attacking entity</param> /// <param name="maxDamage">Maximum amount of damage able to be dealt</param> /// <param name="stunTime">Amount of time to stun the enemy</param> /// <param name="explosionForce">Value to display</param> /// <param name="explosionPoint">Where the explosion originates from</param> /// <param name="explosionRadius">Explosion effect radius</param> public virtual void TakeExplosionDamage(EntityStatsController attacker, float maxDamage, float stunTime, float explosionForce, Vector3 explosionPoint, float explosionRadius) { // Ignore explosions if already dead or invincible if (isDead || invincible) { return; } // Calculate damage based on distance from the explosion point float proximity = (col.ClosestPoint(explosionPoint) - explosionPoint).magnitude; float effect = 1 - (proximity / explosionRadius); if (effect < 0f) { return; } TakeDamage(attacker, maxDamage * effect); StartCoroutine(ApplyExplosiveForce(explosionForce, explosionPoint, explosionRadius, stunTime)); }
protected override void OnTriggerEnter(Collider other) { GameObject col = other.gameObject; // if collision is with a player then deal damage if (col.CompareTag("Player")) { EntityStatsController target = col.GetComponent <EntityStatsController>(); target.StartCoroutine(other.transform.GetComponent <PlayerMotorController>().ApplyTimedMovementModifier(0.5f, 1.5f)); target.TakeDamage(Damage); gameObject.SetActive(false); } else if (col.CompareTag("Ground")) { // Possibly spawn a random plant if (Random.Range(0f, 1f) < spawnPlantProbability) { int maxIndex = 1; float healthPercentage = LauncherStats.health.CurrentValue / LauncherStats.health.maxValue; if (healthPercentage < 0.35f) { maxIndex = 4; } else if (healthPercentage < 0.7f) { maxIndex = 3; } TreantBossCombatController treatCombat = LauncherStats.GetComponent <TreantBossCombatController>(); if (treatCombat && treatCombat.CanSpawnNewPlant()) { GameObject plantPrefab = plantPrefabs[Random.Range(0, maxIndex)]; GameObject plant = Instantiate(plantPrefab, new Vector3(transform.position.x, 0, transform.position.z), Quaternion.identity); treatCombat.AddPlantToSpawnList(plant); } } gameObject.SetActive(false); } }
/// <summary> /// Take damage from an attacker /// </summary> /// <param name="attacker">Stats controller of the attacking entity</param> /// <param name="damageValue">Approximate damage value to apply to enemy health</param> /// <param name="timeDelta">Time since last damage calculation</param> public virtual void TakeDamage(EntityStatsController attacker, float damageValue, float timeDelta = 1f) { // Ignore attacks if already dead if (isDead) { return; } Anim.ResetTrigger("TakeDamage"); Anim.SetTrigger("TakeDamage"); if (takeDamageVocalSFX != null) { StartCoroutine(AudioHelper.PlayAudioOverlap(VocalAudio, takeDamageVocalSFX)); } // Calculate any changes based on stats and modifiers here first float hitValue = (damageValue - ComputeDefenseModifier()) * timeDelta; health.Subtract(hitValue < 0 ? 0 : hitValue); if (Mathf.Approximately(health.CurrentValue, 0f)) { Die(); } }
/// <summary> /// Take damage from an attacker /// </summary> /// <param name="attacker">Stats controller of the attacking entity</param> /// <param name="maxDamage">Maximum amount of damage able to be dealt</param> /// <param name="stunTime">Amount of time to stun the enemy</param> /// <param name="explosionForce">Value to display</param> /// <param name="explosionPoint">Where the explosion originates from</param> /// <param name="explosionRadius">Explosion effect radius</param> public virtual void TakeExplosionDamage(EntityStatsController attacker, float maxDamage, float stunTime, float explosionForce, Vector3 explosionPoint, float explosionRadius) { // Ignore explosions if already dead if (isDead) { return; } // Calculate damage based on distance from the explosion point float proximity = (col.ClosestPoint(explosionPoint) - explosionPoint).magnitude; float effect = 1 - (proximity / explosionRadius); // TODO slightly strange bug where enemies just beyond the explosion take negative damage? This is a temp fix. if (effect < 0f) { return; } TakeDamage(attacker, maxDamage * effect); StartCoroutine(ApplyExplosiveForce(explosionForce, explosionPoint, explosionRadius, stunTime)); }
public void Interactable_CanInteract_FalseIfInRangeAndWrongColour() {//set up testing variables for 'target' GameObject gameObject = new GameObject(); Transform objPos = gameObject.GetComponent <Transform>(); objPos.position = Vector3.one; //set both this and the 'player' to the same position Interactable interactable = gameObject.AddComponent <Interactable>(); interactable.colour = CharacterColour.Red; //set target and 'player' colour to NOT MATCH //target.GetComponent<EntityStatsController>().characterColour //variables for 'player' GameObject player = new GameObject(); Transform playerPos = player.GetComponent <Transform>(); playerPos.position = Vector3.one; EntityStatsController playerControl = player.AddComponent <EntityStatsController>(); playerControl.characterColour = CharacterColour.Green;//set player colour to Green to start //assert that we cannot pick up an object in the same position as us, if it doesn't match colour //Red object, Green Player Assert.False(interactable.CanInteract(playerPos)); //Blue object, Green Player interactable.colour = CharacterColour.Blue; Assert.False(interactable.CanInteract(playerPos)); //Yellow object, Green Player interactable.colour = CharacterColour.Yellow; Assert.False(interactable.CanInteract(playerPos)); playerControl.characterColour = CharacterColour.Red;//set player colour to red now //Yellow object, Red player Assert.False(interactable.CanInteract(playerPos)); //Blue object, Red Player interactable.colour = CharacterColour.Blue; Assert.False(interactable.CanInteract(playerPos)); //Green object, Red Player interactable.colour = CharacterColour.Green; Assert.False(interactable.CanInteract(playerPos)); playerControl.characterColour = CharacterColour.Blue;//set player colour to Blue now //Green object, Blue Player Assert.False(interactable.CanInteract(playerPos)); //Yellow object, Blue Player interactable.colour = CharacterColour.Yellow; Assert.False(interactable.CanInteract(playerPos)); //Red object, Blue Player interactable.colour = CharacterColour.Red; Assert.False(interactable.CanInteract(playerPos)); playerControl.characterColour = CharacterColour.Yellow;//Finally set player colour to Yellow //Red object, Yellow Player Assert.False(interactable.CanInteract(playerPos)); //Blue object, Yellow Player interactable.colour = CharacterColour.Blue; Assert.False(interactable.CanInteract(playerPos)); //Green object, Yellow Player interactable.colour = CharacterColour.Green; Assert.False(interactable.CanInteract(playerPos)); Object.DestroyImmediate(gameObject); Object.DestroyImmediate(player); }
/// <summary> /// Take damage from an attacker, with reference to who the attacker is /// </summary> /// <param name="attacker">Stats controller of the attacking entity</param> /// <param name="damageValue">Approximate damage value to apply to enemy health</param> /// <param name="timeDelta">Time since last damage calculation</param> public virtual void TakeDamage(EntityStatsController attacker, float damageValue, float timeDelta = 1f) { // By default ignore the launcher stats TakeDamage(damageValue, timeDelta); }
public override void Launch(EntityStatsController launcherStats, Vector3 direction, float launchForce, float range, float damage, string targetTag = "Enemy") { ObjectPooler.Instance.StartCoroutine(timedExplosion()); // Just need something that always exists, don't know whats a good choice base.Launch(launcherStats, direction, launchForce, range, damage, targetTag); }