context: using UnityEngine;
  using System;
  using System.Collections;
  using System.Collections.Generic;
  
  
  // ReSharper disable once CheckNamespace
  namespace DarkTonic.CoreGameKit {
      /// <summary>
      /// This class is used to set up Killable, used for combat objects with attack points and hit points. Also can be used for pickups such as coins and health packs.
      /// </summary>
      [AddComponentMenu("Dark Tonic/Core GameKit/Combat/Killable")]
      // ReSharper disable once CheckNamespace
      public class Killable : MonoBehaviour {
          /*! \cond PRIVATE */
          public const string DestroyedText = "Destroyed";
          public const float CoroutineInterval = .2f;
          public const int MaxHitPoints = 100000;
          public const int MaxAttackPoints = 100000;
          public const int MinAttackPoints = -100000;
  
          #region Members
  
          // ReSharper disable InconsistentNaming
          public TriggeredSpawner.GameOverBehavior gameOverBehavior = TriggeredSpawner.GameOverBehavior.Disable;
          public bool syncHitPointWorldVariable = false;
          public KillerInt hitPoints = new KillerInt(1, 1, MaxHitPoints);
          public KillerInt maxHitPoints = new KillerInt(MaxAttackPoints, MinAttackPoints, MaxAttackPoints);
          public KillerInt atckPoints = new KillerInt(1, MinAttackPoints, MaxAttackPoints);
          public Transform ExplosionPrefab;
          public KillableListener listener;
  
          public bool invincibilityExpanded = false;
          public bool isInvincible;
          public bool invincibleWhileChildrenKillablesExist = true;
          public bool disableCollidersWhileChildrenKillablesExist = false;
  
          public bool invincibleOnSpawn = false;
          public KillerFloat invincibleTimeSpawn = new KillerFloat(2f, 0f, float.MaxValue);
  
          public bool enableLogging = false;
          public bool filtersExpanded = true;
  
          public bool ignoreKillablesSpawnedByMe = true;
          public bool useLayerFilter = false;
          public bool useTagFilter = false;
          public bool showVisibilitySettings = true;
          public bool despawnWhenOffscreen = false;
          public bool despawnOnClick = false;
          public bool despawnOnMouseClick = false;
          public bool despawnIfNotVisible = false;
          public KillerFloat despawnIfNotVisibleForSec = new KillerFloat(5f, .1f, float.MaxValue);
          public bool ignoreOffscreenHits = false;
          public List<string> matchingTags = new List<string>() { "Untagged" };
          public List<int> matchingLayers = new List<int>() { 0 };
          public DespawnMode despawnMode = DespawnMode.ZeroHitPoints;
  
          public bool damageFireEvents = false;
          public List<CGKCustomEventToFire> damageCustomEvents = new List<CGKCustomEventToFire>();
  
          public bool deathFireEvents = false;
          public List<CGKCustomEventToFire> deathCustomEvents = new List<CGKCustomEventToFire>();
  
          // death player stat mods
          public bool despawnStatModifiersExpanded = false;
          public WorldVariableCollection playerStatDespawnModifiers = new WorldVariableCollection();
          public List<WorldVariableCollection> alternateModifiers = new List<WorldVariableCollection>();
  
          // damage prefab settings
          public bool damagePrefabExpanded = false;
          public SpawnSource damagePrefabSource = SpawnSource.None;
          public int damagePrefabPoolIndex = 0;
          public string damagePrefabPoolName = null;
          public Transform damagePrefabSpecific;
          public DamagePrefabSpawnMode damagePrefabSpawnMode = DamagePrefabSpawnMode.None;
          public KillerInt damagePrefabSpawnQuantity = new KillerInt(1, 1, 100);
          public KillerInt damageGroupsize = new KillerInt(1, 1, 500);
          public Vector3 damagePrefabOffset = Vector3.zero;
          public bool damagePrefabRandomizeXRotation = false;
          public bool damagePrefabRandomizeYRotation = false;
          public bool damagePrefabRandomizeZRotation = false;
          public bool despawnStatDamageModifiersExpanded = false;
          public WorldVariableCollection playerStatDamageModifiers = new WorldVariableCollection();
  
          public bool damageKnockBackExpanded = false;
          public bool sendDamageKnockback = false;
          public bool receiveKnockbackWhenDamaged = false;
          public bool receiveKnockbackWhenInvince = false;
          public KillerFloat damageKnockUpMeters = new KillerFloat(10f, float.MinValue, float.MaxValue);
          public KillerFloat damageKnockBackFactor = new KillerFloat(3f, float.MinValue, float.MaxValue);
  
          // death prefab settings
          public WaveSpecifics.SpawnOrigin deathPrefabSource = WaveSpecifics.SpawnOrigin.Specific;
          public int deathPrefabPoolIndex = 0;
          public string deathPrefabPoolName = null;
          public bool deathPrefabSettingsExpanded = false;
          public Transform deathPrefabSpecific;
          public bool deathPrefabKeepSameParent = true;
          public KillerInt deathPrefabSpawnPercent = new KillerInt(100, 0, 100);
          public KillerInt deathPrefabQty = new KillerInt(1, 0, 100);
          public Vector3 deathPrefabOffset = Vector3.zero;
          public RotationMode rotationMode = RotationMode.UseDeathPrefabRotation;
          public bool deathPrefabKeepVelocity = true;
          public Vector3 deathPrefabCustomRotation = Vector3.zero;
          public KillerFloat deathDelay = new KillerFloat(0, 0, 100);
  
          // retrigger limit settings
          public TriggeredSpawner.RetriggerLimitMode retriggerLimitMode = TriggeredSpawner.RetriggerLimitMode.None;
          public KillerInt limitPerXFrame = new KillerInt(1, 1, int.MaxValue);
          public KillerFloat limitPerSeconds = new KillerFloat(0.1f, .1f, float.MaxValue);
          public SpawnerDestroyedBehavior spawnerDestroyedAction = SpawnerDestroyedBehavior.DoNothing;
  
          public Killable parentKillableForParentDestroyed = null;
          public SpawnerDestroyedBehavior parentDestroyedAction = SpawnerDestroyedBehavior.DoNothing;
  
          public DeathDespawnBehavior deathDespawnBehavior = DeathDespawnBehavior.ReturnToPool;
          public bool timerDeathEnabled = false;
          public KillerFloat timerDeathSeconds = new KillerFloat(1f, 0.1f, float.MaxValue);
          public SpawnerDestroyedBehavior timeUpAction = SpawnerDestroyedBehavior.Die;
  
          public int currentHitPoints;
  
          public bool isVisible;
  
          public bool showRespawnSettings = false;
          public RespawnType respawnType = RespawnType.None;
          public int timesToRespawn = 1;
          public KillerFloat respawnDelay = new KillerFloat(0, 0, 100);
          public bool respawnFireEvents = false;
          public List<CGKCustomEventToFire> respawnCustomEvents = new List<CGKCustomEventToFire>();
  
          // ReSharper restore InconsistentNaming
          /*! \endcond */
  
          private Vector3 _respawnLocation = Vector3.zero;
  
          private int _timesRespawned;
          private GameObject _spawnedFromObject;
          private int? _spawnedFromGOInstanceId;
          private WavePrefabPool _deathPrefabWavePool;
          private Transform _trans;
          private GameObject _go;
          private int? _instanceId;
          private CharacterController _charCtrl;
          private Rigidbody _body;
          private Killable _parentKillable;
          private Collider _collider;
  
  #if UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2
      // not supported
  #else
          private Rigidbody2D _body2D;
          private Collider2D _collider2D;
  #endif
  
          private int _damageTaken;
          private int _damagePrefabsSpawned;
          private WavePrefabPool _damagePrefabWavePool;
          private int _triggeredLastFrame = -200;
          private float _triggeredLastTime = -100f;
          private bool _becameVisible;
          private float _spawnTime;
          private bool _isDespawning;
          private bool _isTemporarilyInvincible;
          private bool _spawnerSet;
          private readonly YieldInstruction _loopDelay = new WaitForSeconds(CoroutineInterval);
          private bool _spawnLocationSet;
          private bool _waitingToDestroy;
          private readonly List<Killable> _childrenToDestroy = new List<Killable>();
  
          private List<Killable> _childKillables = new List<Killable>();
  
          #endregion
  
          #region enums
          /*! \cond PRIVATE */
          public enum DeathDespawnBehavior {
              ReturnToPool,
              Disable
          }
  
          public enum RespawnType {
              None = 0,
              Infinite = 1,
              SetNumber = 2
          }
  
          public enum SpawnerDestroyedBehavior {
              DoNothing,
              Despawn,
              Die
          }
  
          public enum SpawnSource {
              None,
              Specific,
              PrefabPool
          }
  
          public enum DamagePrefabSpawnMode {
              None,
              PerHit,
              PerHitPointLost,
              PerGroupHitPointsLost
          }
  
          public enum RotationMode {
              CustomRotation,
              InheritExistingRotation,
              UseDeathPrefabRotation
          }
  
          public enum DespawnMode {
              None = -1,
              ZeroHitPoints = 0,
              LostAnyHitPoints = 1,
              CollisionOrTrigger = 2
          }
  
          /*! \endcond */
          #endregion
  
          #region MonoBehavior events and associated virtuals
  
          // ReSharper disable once UnusedMember.Local
          private void Awake() {
              _charCtrl = GetComponent<CharacterController>();
  
              _timesRespawned = 0;
              ResetSpawnerInfo();
          }
  
          // ReSharper disable once UnusedMember.Local
          private void Start() {
              SpawnedOrAwake(false);
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnSpawned() {
              // used by Core GameKit Pooling & also Pool Manager Pooling!
              SpawnedOrAwake();
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnDespawned() {
              _spawnerSet = false;
  
              // reset velocity
              ResetVelocity();
              ResetSpawnerInfo();
  
              // add code here to fire when despawned
              Despawned();
          }
  
          /// <summary>
          /// This method is automatically called just before the Killable is Despawned
          /// </summary>
          protected virtual void Despawned() {
              // add code to subclass if needing functionality here		
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnClick() {
              _OnClick();
          }
  
          /// <summary>
          /// This method gets called when a Killable is clicked
          /// </summary>
          protected virtual void _OnClick() {
              if (despawnOnClick) {
                  DestroyKillable();
              }
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnMouseDown() {
              _OnMouseDown();
          }
  
          /// <summary>
          /// This method gets called when a Killable has a mouse button clicked down.
          /// </summary>
          protected virtual void _OnMouseDown() {
              if (despawnOnMouseClick) {
                  DestroyKillable();
              }
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnBecameVisible() {
              BecameVisible();
          }
  
          /// <summary>
          /// This gets called when the Killable becomes visible.
          /// </summary>
          public virtual void BecameVisible() {
              if (isVisible) {
                  return; // to fix Unity error.
              }
  
              isVisible = true;
              _becameVisible = true;
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnBecameInvisible() {
              BecameInvisible();
          }
  
          /// <summary>
          /// This gets called when the Killable becomes invisible
          /// </summary>
          public virtual void BecameInvisible() {
              isVisible = false;
  
              if (despawnWhenOffscreen) {
                  Despawn(TriggeredSpawner.EventType.Invisible);
              }
          }
  
  #if UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2
      // not supported
  #else
          // ReSharper disable once UnusedMember.Local
          private void OnCollisionEnter2D(Collision2D coll) {
              CollisionEnter2D(coll);
          }
  
          /// <summary>
          /// This gets called when the Killable has a 2D collision
          /// </summary>
          /// <param name="collision">The collision object.</param>
          public virtual void CollisionEnter2D(Collision2D collision) {
              var othGo = collision.gameObject;
  
              if (!IsValidHit(othGo.layer, othGo.tag)) {
                  return;
              }
  
              var enemy = GetOtherKillable(othGo);
  
              CheckForAttackPoints(enemy, othGo);
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnTriggerEnter2D(Collider2D other) {
              TriggerEnter2D(other);
          }
  
          /// <summary>
          /// This gets called when the Killable has a 2D "trigger" collision
          /// </summary>
          /// <param name="other">The collider2D object.</param>
          public virtual void TriggerEnter2D(Collider2D other) {
              var othGo = other.gameObject;
  
              if (!IsValidHit(othGo.layer, othGo.tag)) {
                  return;
              }
  
              var enemy = GetOtherKillable(othGo);
  
              CheckForAttackPoints(enemy, othGo);
          }
  #endif
  
          // ReSharper disable once UnusedMember.Local
          private void OnCollisionEnter(Collision collision) {
              CollisionEnter(collision);
          }
  
          /// <summary>
          /// This gets called when the Killable has a collision
          /// </summary>
          /// <param name="collision">The collision object.</param>
          public virtual void CollisionEnter(Collision collision) {
              var othGo = collision.gameObject;
  
              if (!IsValidHit(othGo.layer, othGo.tag)) {
                  return;
              }
  
              var enemy = GetOtherKillable(othGo);
  
              CheckForAttackPoints(enemy, othGo);
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnTriggerEnter(Collider other) {
              TriggerEnter(other);
          }
  
          /// <summary>
          /// This gets called when the Killable has a "trigger" collision
          /// </summary>
          /// <param name="other">The collider object</param>
          public virtual void TriggerEnter(Collider other) {
              if (!IsValidHit(other.gameObject.layer, other.gameObject.tag)) {
                  return;
              }
  
              var enemy = GetOtherKillable(other.gameObject);
  
              CheckForAttackPoints(enemy, other.gameObject);
          }
  
          // ReSharper disable once UnusedMember.Local
          private void OnControllerColliderHit(ControllerColliderHit hit) {
              ControllerColliderHit(hit.gameObject);
          }
  
          /// <summary>
          /// This gets called when the Killable has a collision from a CharacterController. Do not call this.
          /// </summary>
          /// <param name="hit">The game object being hit.</param>
          /// <param name="calledFromOtherKillable">The object calling this method</param>
          public virtual void ControllerColliderHit(GameObject hit, bool calledFromOtherKillable = false) {
              if (calledFromOtherKillable && _charCtrl != null) {
                  // we don't need to be called from a Char Controller if we are one. Abort to exit potential endless loop.
                  return;
              }
  
              var enemy = GetOtherKillable(hit);
  
              if (enemy != null && !calledFromOtherKillable) {
                  // for Character Controllers, the hit object will not register a hit, so we call it manually.
                  enemy.ControllerColliderHit(GameObj, true);
              }
  
              if (!IsValidHit(hit.layer, hit.tag)) {
                  return;
              }
  
              CheckForAttackPoints(enemy, hit);
          }
  
          #endregion
  
          #region Public Methods
  
          /// <summary>
          /// Call this method to make your Killable invincible for X seconds.
          /// </summary>
          /// <param name="seconds">Number of seconds to make your Killable invincible.</param>
          public void TemporaryInvincibility(float seconds) {
              if (_isTemporarilyInvincible) {
                  // already invincible.
                  return;
              }
              StartCoroutine(SetSpawnInvincibleForSeconds(seconds));
          }
  
          /// <summary>
          /// Call this method to add attack points to the Killable.
          /// </summary>
          /// <param name="pointsToAdd">The number of attack points to add.</param>
          public void AddAttackPoints(int pointsToAdd) {
              atckPoints.Value += pointsToAdd;
              if (atckPoints.Value < 0) {
                  atckPoints.Value = 0;
              }
          }
  
          /// <summary>
          /// Call this method to add hit points to the Killable.
          /// </summary>
          /// <param name="pointsToAdd">The number of hit points to add to "current hit points".</param>
          public void AddHitPoints(int pointsToAdd) {
              hitPoints.Value += pointsToAdd;
              if (hitPoints.Value < 0) {
                  hitPoints.Value = 0;
              }
  
              currentHitPoints += pointsToAdd;
              if (currentHitPoints < 0) {
                  currentHitPoints = 0;
              }
          }
  
          /*! \cond PRIVATE */
          public bool IsUsingPrefabPool(Transform poolTrans) {
              var poolName = poolTrans.name;
  
              if (damagePrefabSource == SpawnSource.PrefabPool && damagePrefabPoolName == poolName) {
                  return true;
              }
  
              return false;
          }
  
          public void RegisterChildKillable(Killable kill) {
              if (_childKillables.Contains(kill)) {
                  return;
              }
  
              _childKillables.Add(kill);
  
              if (invincibleWhileChildrenKillablesExist && disableCollidersWhileChildrenKillablesExist) {
                  DisableColliders();
              }
  
              // Diagnostic code to uncomment if things are going wrong.
              //Debug.Log("ADD - children of '" + name + "': " + childKillables.Count);
          }
  
          public void RecordSpawner(GameObject spawnerObject) {
              _spawnedFromObject = spawnerObject;
              _spawnerSet = true;
          }
  
          public void UnregisterChildKillable(Killable kill) {
              _childKillables.Remove(kill);
  
              deathDespawnBehavior = DeathDespawnBehavior.Disable;
  
              if (_childKillables.Count == 0 && invincibleWhileChildrenKillablesExist &&
                  disableCollidersWhileChildrenKillablesExist) {
                  EnableColliders();
              }
  
              // Diagnostic code to uncomment if things are going wrong.
              //Debug.Log("REMOVE - children of '" + name + "': " + childKillables.Count);
          }
  
          public void RecordChildToDie(Killable kilChild) {
              if (_childrenToDestroy.Contains(kilChild)) {
                  return;
              }
  
              _childrenToDestroy.Add(kilChild);
          }
          /*! \endcond */
  
          #endregion
  
          #region Helper Methods
  
          private IEnumerator SetSpawnInvincibleForSeconds(float seconds) {
              _isTemporarilyInvincible = true;
              isInvincible = true;
  
              yield return new WaitForSeconds(seconds);
  
              if (!_isTemporarilyInvincible) {
                  yield break;
              }
  
              isInvincible = false;
              _isTemporarilyInvincible = false;
          }
  
          private void CheckForValidVariables() {
              // examine all KillerInts
              hitPoints.LogIfInvalid(Trans, "Killable Start Hit Points");
              maxHitPoints.LogIfInvalid(Trans, "Killable Max Hit Points");
              atckPoints.LogIfInvalid(Trans, "Killable Start Attack Points");
              damagePrefabSpawnQuantity.LogIfInvalid(Trans, "Killable Damage Prefab Spawn Quantity");
              damageGroupsize.LogIfInvalid(Trans, "Killable Group H.P. Amount");
              deathPrefabSpawnPercent.LogIfInvalid(Trans, "Killable Spawn % Chance");
              deathPrefabQty.LogIfInvalid(Trans, "Killable Death Prefab Spawn Quantity");
              limitPerXFrame.LogIfInvalid(Trans, "Killable Min Frames Between");
              deathDelay.LogIfInvalid(Trans, "Killable Death Delay");
              respawnDelay.LogIfInvalid(Trans, "Killable Respawn Delay");
              damageKnockUpMeters.LogIfInvalid(Trans, "Killable Knock Up Force");
              damageKnockBackFactor.LogIfInvalid(Trans, "Killable Knock Back Force");
  
              // examine all KillerFloats
              despawnIfNotVisibleForSec.LogIfInvalid(Trans, "Killable Not Visible Max Time");
              limitPerSeconds.LogIfInvalid(Trans, "Killable Min Seconds Between");
              if (timerDeathEnabled) {
                  timerDeathSeconds.LogIfInvalid(Trans, "Killable Timer Death Seconds");
              }
  
              if (invincibleOnSpawn) {
                  invincibleTimeSpawn.LogIfInvalid(Trans, "Killable Invincibility Time (sec)");
              }
  
              // check damage mod scenarios  
              // ReSharper disable once ForCanBeConvertedToForeach
              for (var i = 0; i < playerStatDamageModifiers.statMods.Count; i++) {
  
gt:                 var mod = playerStatDamageModifiers.statMods[i];