1127 lines
43 KiB
C#
1127 lines
43 KiB
C#
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
using UnityEngine;
|
|
|
|
namespace TBTK{
|
|
|
|
public class Unit : TBMonoItem {
|
|
|
|
public static bool enableRotation=true;
|
|
|
|
public static bool inspector=true;
|
|
|
|
[HideInInspector] public bool loadedFromCache=false;
|
|
|
|
|
|
public int value=50;
|
|
|
|
[Space(5)]
|
|
public int facID; //in runtime, this also correspond to the faction index in factionList
|
|
public void SetFacID(int id){ facID=id; }
|
|
public int GetBaseFacID(){ return facID ; }
|
|
public int GetFacID(){ return tempFacID>=0 ? tempFacID : facID ; }
|
|
|
|
public bool playableUnit=false;
|
|
|
|
public AI._AIBehaviour aiBehaviour=AI._AIBehaviour.aggressive;
|
|
public bool requireTrigger=true; //when true, unit starts in passive state, then switch to aggressive or evasive (doesnt apply for passive)
|
|
//[HideInInspector]
|
|
public bool triggered=false;
|
|
|
|
public bool IsPassive(){ return aiBehaviour==AI._AIBehaviour.passive; }
|
|
public bool IsAggressive(){ return (aiBehaviour==AI._AIBehaviour.aggressive && !requireTrigger) || triggered; }
|
|
|
|
//public bool IsPassive(){ return aiBehaviour==AI._AIBehaviour.passive || !IsAggressive(); }
|
|
//public bool IsAggressive(){ return aiBehaviour==AI._AIBehaviour.aggressive && (triggered || !requireTrigger); }
|
|
//public bool IsEvasive(){ return aiBehaviour=AI._AIBehaviour.evasive && (triggered || !requireTrigger); }
|
|
|
|
|
|
[Space(8)]
|
|
public float hp=10;
|
|
public float ap=2;
|
|
|
|
//public float GetFullHP(){ return stats.hp; }
|
|
public float GetHPRatio(){ return hp/GetFullHP(); }
|
|
|
|
//public float GetFullAP(){ return stats.ap; }
|
|
public float GetAPRatio(){ return ap/GetFullAP(); }
|
|
|
|
public float GetFullHP(){ return stats.hp * GetFullHPMul() + GetFullHPMod(); }
|
|
public float GetFullHPMul(){ return activeEffectMul.stats.hp * PerkManager.GetUnitMulHP(prefabID); }
|
|
public float GetFullHPMod(){ return activeEffectMod.stats.hp + PerkManager.GetUnitModHP(prefabID); }
|
|
|
|
public float GetFullAP(){ return stats.ap * GetFullAPMul() + GetFullAPMod(); }
|
|
public float GetFullAPMul(){ return activeEffectMul.stats.ap * PerkManager.GetUnitMulAP(prefabID); }
|
|
public float GetFullAPMod(){ return activeEffectMod.stats.ap + PerkManager.GetUnitModAP(prefabID); }
|
|
|
|
public float GetHPRegen(){ return stats.hpRegen * GetHPRegenMul() + GetHPRegenMod(); }
|
|
public float GetHPRegenMul(){ return activeEffectMul.stats.hpRegen * PerkManager.GetUnitMulHPRegen(prefabID); }
|
|
public float GetHPRegenMod(){ return activeEffectMod.stats.hpRegen + PerkManager.GetUnitModHPRegen(prefabID); }
|
|
|
|
public float GetAPRegen(){ return stats.apRegen * GetAPRegenMul() + GetAPRegenMod(); }
|
|
public float GetAPRegenMul(){ return activeEffectMul.stats.apRegen * PerkManager.GetUnitMulAPRegen(prefabID); }
|
|
public float GetAPRegenMod(){ return activeEffectMod.stats.apRegen + PerkManager.GetUnitModAPRegen(prefabID); }
|
|
|
|
|
|
|
|
public int GetMoveLimit(){ return (int)(stats.moveLimit * GetMoveLimitMul()) + (int)GetMoveLimitMod(); }
|
|
public float GetMoveLimitMul(){ return activeEffectMul.stats.moveLimit * PerkManager.GetUnitMulMoveLim(prefabID); }
|
|
public float GetMoveLimitMod(){ return activeEffectMod.stats.moveLimit + PerkManager.GetUnitModMoveLim(prefabID); }
|
|
public int GetMoveRemain(){ return GetMoveLimit()-moveThisTurn; }
|
|
public int moveThisTurn=0;
|
|
|
|
public int GetAttackLimit(){ return (int)(stats.attackLimit * GetAttackLimitMul()) + (int)GetAttackLimitMod(); }
|
|
public float GetAttackLimitMul(){ return activeEffectMul.stats.moveLimit * PerkManager.GetUnitMulAttackLim(prefabID); }
|
|
public float GetAttackLimitMod(){ return activeEffectMod.stats.moveLimit + PerkManager.GetUnitModAttackLim(prefabID); }
|
|
public int GetAttackRemain(){ return GetAttackLimit()-attackThisTurn; }
|
|
public int attackThisTurn=0;
|
|
|
|
public int GetCounterLimit(){ return (int)(stats.counterLimit * GetCounterLimitMul()) + (int)GetCounterLimitMod(); }
|
|
public float GetCounterLimitMul(){ return activeEffectMul.stats.counterLimit * PerkManager.GetUnitMulCounterLim(prefabID); }
|
|
public float GetCounterLimitMod(){ return activeEffectMod.stats.counterLimit + PerkManager.GetUnitModCounterLim(prefabID); }
|
|
public int counterThisTurn=0;
|
|
|
|
public int GetAbilityLimit(){ return (int)(stats.abilityLimit * GetAbilityLimitMul() + GetAbilityLimitMod()); }
|
|
public float GetAbilityLimitMul(){ return activeEffectMul.stats.abilityLimit * PerkManager.GetUnitMulAbilityLim(prefabID); }
|
|
public float GetAbilityLimitMod(){ return activeEffectMod.stats.abilityLimit + PerkManager.GetUnitModAbilityLim(prefabID); }
|
|
public int GetAbilityRemain(){ return GetAbilityLimit()-abilityThisTurn; }
|
|
public int abilityThisTurn=0;
|
|
|
|
//public const int apPerMove=1;
|
|
//public const int apPerNode=0;
|
|
//public const int apPerAttack=1;
|
|
|
|
public bool HasTakenAction(){ return (moveThisTurn + attackThisTurn + abilityThisTurn + counterThisTurn) > 0 ; }
|
|
|
|
[Space(8)]
|
|
public AStar._BypassUnit canMovePastUnit;
|
|
//public bool canMovePastUnit;
|
|
public bool canMovePastObs;
|
|
|
|
[Space(8)]
|
|
public Transform targetPoint;
|
|
public float radius=0.25f;
|
|
|
|
public Vector3 GetTargetPoint(){ return targetPoint!=null ? targetPoint.position : GetPos(); }
|
|
public float GetRadius(){ return radius; }
|
|
|
|
public ShootObject soRange;
|
|
public ShootObject soMelee;
|
|
public float shootPointSpacing=0.1f;
|
|
public List<Transform> shootPointList=new List<Transform>();
|
|
|
|
public Transform turretPivot;
|
|
public Transform barrelPivot;
|
|
public bool snapAiming;
|
|
public bool aimInXAxis;
|
|
public bool rotateWhileAiming;
|
|
|
|
private float aimSpeed=7;
|
|
private bool instantRotate=false;
|
|
[Space(5)] public float moveSpeed=5;
|
|
|
|
|
|
|
|
public delegate bool ActionCamCheck (bool actionType);
|
|
public delegate IEnumerator ActionCamStart(Vector3 srcPos, Vector3 tgtPos);
|
|
public delegate IEnumerator ActionCamEnd();
|
|
|
|
public static ActionCamCheck actionCamCheck;
|
|
public static ActionCamStart actionCamStart;
|
|
public static ActionCamEnd actionCamEnd;
|
|
|
|
|
|
void Awake(){
|
|
thisT=transform;
|
|
thisObj=gameObject;
|
|
|
|
if(shootPointList.Count==0) shootPointList.Add(thisT);
|
|
|
|
InitAbility();
|
|
UpdateActiveEffect();
|
|
|
|
if(turretPivot!=null) defaultTurretRot=turretPivot.localRotation;
|
|
if(barrelPivot!=null) defaultBarrelRot=barrelPivot.localRotation;
|
|
|
|
triggered=false;
|
|
|
|
InitAnimation();
|
|
}
|
|
|
|
void Start(){
|
|
if(GameControl.EnableFogOfWar() && !playableUnit){
|
|
Utility.SetLayerRecursively(thisT, TBTK.GetLayerInvisible());
|
|
}
|
|
}
|
|
|
|
|
|
public void NewTurn(bool restoreFullHP=false){ //restoreFullHP is used when the game start
|
|
if(restoreFullHP) hp=GetFullHP();
|
|
|
|
if(GameControl.RestoreAPOnTurn()) ap=GetFullAP();
|
|
else ap=Mathf.Min(ap+GetAPRegen(), GetFullAP());
|
|
|
|
hp=Mathf.Min(hp+GetHPRegen(), GetFullHP());
|
|
|
|
moveThisTurn=0;
|
|
attackThisTurn=0;
|
|
counterThisTurn=0;
|
|
abilityThisTurn=0;
|
|
}
|
|
|
|
|
|
public bool CanAttack(){
|
|
if(attackThisTurn>=GetAttackLimit()) return false;
|
|
if(GameControl.UseAPToAttack() && GameControl.GetAPPerAttack()>ap) return false;
|
|
return true;
|
|
}
|
|
public bool CanMove(){
|
|
if(moveThisTurn>=GetMoveLimit()) return false;
|
|
if(GameControl.UseAPToMove() && GameControl.GetAPPerMove()>ap) return false;
|
|
if(GameControl.UseAPToMove() && GameControl.GetAPPerNode()>ap) return false;
|
|
return true;
|
|
}
|
|
public bool CanCounter(Unit tgtUnit){
|
|
if(IsStunned()) return false;
|
|
if(counterThisTurn>=GetCounterLimit()) return false;
|
|
if(GameControl.UseAPToAttack() && GameControl.GetAPPerAttack()>ap) return false;
|
|
|
|
int targetRange=GridManager.GetDistance(tgtUnit.node, node);
|
|
if(targetRange>GetAttackRange()) return false;
|
|
|
|
float minAttackRange=GetAttackRangeMin();
|
|
if(minAttackRange>0 && targetRange<minAttackRange) return false;
|
|
|
|
if(requireLOSToAttack && !GridManager.CheckLOS(node, tgtUnit.node, GetSight())) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
[Space(8)]
|
|
public bool hasMeleeAttack=true;
|
|
public bool requireLOSToAttack=true;
|
|
|
|
public int armorType=0;
|
|
|
|
public int damageType=0;
|
|
public Stats stats;
|
|
|
|
public int damageTypeMelee=0;
|
|
public Stats statsMelee;
|
|
|
|
public List<int> attackEffectIDList=new List<int>();
|
|
|
|
public List<int> GetRuntimeAttackEffectIDList(){
|
|
return PerkManager.ModifyUnitAttackEffectList(prefabID, attackEffectIDList);
|
|
//return attackEffectIDList;
|
|
}
|
|
|
|
|
|
public float GetAttack(){ return stats.attack * GetAttackMul() + GetAttackMod(); }
|
|
public float GetAttackMul(){ return activeEffectMul.stats.attack * PerkManager.GetUnitMulAttack(prefabID); }
|
|
public float GetAttackMod(){ return activeEffectMod.stats.attack + PerkManager.GetUnitModAttack(prefabID); }
|
|
|
|
public float GetDefense(){ return stats.defense * GetDefenseMul() + GetDefenseMod(); }
|
|
public float GetDefenseMul(){ return activeEffectMul.stats.defense * PerkManager.GetUnitMulDefense(prefabID); }
|
|
public float GetDefenseMod(){ return activeEffectMod.stats.defense + PerkManager.GetUnitModDefense(prefabID); }
|
|
|
|
public float GetHit(){ return stats.hit * GetHitMul() + GetHitMod(); }
|
|
public float GetHitMul(){ return activeEffectMul.stats.hit * PerkManager.GetUnitMulHit(prefabID); }
|
|
public float GetHitMod(){ return activeEffectMod.stats.hit + PerkManager.GetUnitModHit(prefabID); }
|
|
|
|
public float GetDodge(){ return stats.dodge * GetMulDodge() + GetModDodge(); }
|
|
public float GetMulDodge(){ return activeEffectMul.stats.dodge * PerkManager.GetUnitMulDodge(prefabID); }
|
|
public float GetModDodge(){ return activeEffectMod.stats.dodge + PerkManager.GetUnitModDodge(prefabID); }
|
|
|
|
public float GetDmgHPMin(){ return stats.dmgHPMin * GetMulDmgHPMin() + GetModDmgHPMin(); }
|
|
public float GetMulDmgHPMin(){ return activeEffectMul.stats.dmgHPMin * PerkManager.GetUnitMulDmgHPMin(prefabID); }
|
|
public float GetModDmgHPMin(){ return activeEffectMod.stats.dmgHPMin + PerkManager.GetUnitModDmgHPMin(prefabID); }
|
|
|
|
public float GetDmgHPMax(){ return stats.dmgHPMax * GetMulDmgHPMax() + GetModDmgHPMax(); }
|
|
public float GetMulDmgHPMax(){ return activeEffectMul.stats.dmgHPMax * PerkManager.GetUnitMulDmgHPMax(prefabID); }
|
|
public float GetModDmgHPMax(){ return activeEffectMod.stats.dmgHPMax + PerkManager.GetUnitModDmgHPMax(prefabID); }
|
|
|
|
public float GetDmgAPMin(){ return stats.dmgAPMin * GetMulDmgAPMin() + GetModDmgAPMin(); }
|
|
public float GetMulDmgAPMin(){ return activeEffectMul.stats.dmgAPMin * PerkManager.GetUnitMulDmgAPMin(prefabID); }
|
|
public float GetModDmgAPMin(){ return activeEffectMod.stats.dmgAPMin + PerkManager.GetUnitModDmgAPMin(prefabID); }
|
|
|
|
public float GetDmgAPMax(){ return stats.dmgAPMax * GetMulDmgAPMax() + GetModDmgAPMax(); }
|
|
public float GetMulDmgAPMax(){ return activeEffectMul.stats.dmgAPMax * PerkManager.GetUnitMulDmgAPMax(prefabID); }
|
|
public float GetModDmgAPMax(){ return activeEffectMod.stats.dmgAPMax + PerkManager.GetUnitModDmgAPMax(prefabID); }
|
|
|
|
public float GetCritChance(){ return stats.critChance * GetMulCritChance() + GetModCritChance(); }
|
|
public float GetMulCritChance(){ return activeEffectMul.stats.critChance * PerkManager.GetUnitMulCritC(prefabID); }
|
|
public float GetModCritChance(){ return activeEffectMod.stats.critChance + PerkManager.GetUnitModCritC(prefabID); }
|
|
|
|
public float GetCritReduc(){ return stats.critReduc * GetMulCritReduc() + GetModCritReduc(); }
|
|
public float GetMulCritReduc(){ return activeEffectMul.stats.critReduc * PerkManager.GetUnitMulCritR(prefabID); }
|
|
public float GetModCritReduc(){ return activeEffectMod.stats.critReduc + PerkManager.GetUnitModCritR(prefabID); }
|
|
|
|
public float GetCritMultiplier(){ return stats.critMultiplier * GetMulCritMul() + GetModCritMul(); }
|
|
public float GetMulCritMul(){ return activeEffectMul.stats.critMultiplier * PerkManager.GetUnitMulCritM(prefabID); }
|
|
public float GetModCritMul(){ return activeEffectMod.stats.critMultiplier + PerkManager.GetUnitModCritM(prefabID); }
|
|
|
|
public float GetCDmgMul(){ return stats.cDmgMultip * GetMulCDmgMul() + GetModCDmgMul(); }
|
|
public float GetMulCDmgMul(){ return activeEffectMul.stats.cDmgMultip * PerkManager.GetUnitMulCDmgMul(prefabID); }
|
|
public float GetModCDmgMul(){ return activeEffectMod.stats.cDmgMultip + PerkManager.GetUnitModCDmgMul(prefabID); }
|
|
|
|
public float GetCHitPenalty(){ return stats.cHitPenalty * GetMulCHitPen() + GetModCHitPen(); }
|
|
public float GetMulCHitPen(){ return activeEffectMul.stats.cHitPenalty * PerkManager.GetUnitMulCHitPen(prefabID); }
|
|
public float GetModCHitPen(){ return activeEffectMod.stats.cHitPenalty + PerkManager.GetUnitModCHitPen(prefabID); }
|
|
|
|
public float GetCCritPenalty(){ return stats.cCritPenalty * GetMulCCritPen() + GetModCCritPen(); }
|
|
public float GetMulCCritPen(){ return activeEffectMul.stats.cCritPenalty * PerkManager.GetUnitMulCCritPen(prefabID); }
|
|
public float GetModCCritPen(){ return activeEffectMod.stats.cCritPenalty + PerkManager.GetUnitModCCritPen(prefabID); }
|
|
|
|
public float GetODmgMul(){ return stats.oDmgMultip * GetMulODmgMul() + GetModODmgMul(); }
|
|
public float GetMulODmgMul(){ return activeEffectMul.stats.oDmgMultip * PerkManager.GetUnitMulODmgMul(prefabID); }
|
|
public float GetModODmgMul(){ return activeEffectMod.stats.oDmgMultip + PerkManager.GetUnitModODmgMul(prefabID); }
|
|
|
|
public float GetOHitPenalty(){ return stats.oHitPenalty * GetMulOHitPen() + GetModOHitPen(); }
|
|
public float GetMulOHitPen(){ return activeEffectMul.stats.oHitPenalty * PerkManager.GetUnitMulOHitPen(prefabID); }
|
|
public float GetModOHitPen(){ return activeEffectMod.stats.cHitPenalty + PerkManager.GetUnitModOHitPen(prefabID); }
|
|
|
|
public float GetOCritPenalty(){ return stats.oCritPenalty * GetMulOCritPen() +GetModOCritPen(); }
|
|
public float GetMulOCritPen(){ return activeEffectMul.stats.oCritPenalty * PerkManager.GetUnitMulOCritPen(prefabID); }
|
|
public float GetModOCritPen(){ return activeEffectMod.stats.oCritPenalty + PerkManager.GetUnitModOCritPen(prefabID); }
|
|
|
|
public int GetAttackRange(){ return (int)Mathf.Round(stats.attackRange * GetMulARange() + GetModARange()); }
|
|
public float GetMulARange(){ return activeEffectMul.stats.attackRange * PerkManager.GetUnitMulARange(prefabID); }
|
|
public float GetModARange(){ return activeEffectMod.stats.attackRange + PerkManager.GetUnitModARange(prefabID); }
|
|
|
|
public int GetAttackRangeMin(){ return (int)Mathf.Round(stats.attackRangeMin * GetMulARangeMin() + GetModARangeMin()); }
|
|
public float GetMulARangeMin(){ return activeEffectMul.stats.attackRangeMin * PerkManager.GetUnitMulARangeMin(prefabID); }
|
|
public float GetModARangeMin(){ return activeEffectMod.stats.attackRangeMin + PerkManager.GetUnitModARangeMin(prefabID); }
|
|
|
|
public int GetMoveRange(){ return (int)Mathf.Round(stats.moveRange * GetMulMRange() + GetModMRange()); }
|
|
public float GetMulMRange(){ return activeEffectMul.stats.moveRange * PerkManager.GetUnitMulMRange(prefabID); }
|
|
public float GetModMRange(){ return activeEffectMod.stats.moveRange + PerkManager.GetUnitModMRange(prefabID); }
|
|
|
|
public float GetTurnPriority(){ return stats.turnPriority * GetMulTPriority() + GetModTPriority(); }
|
|
public float GetMulTPriority(){ return activeEffectMul.stats.turnPriority * PerkManager.GetUnitMulTPrioity(prefabID); }
|
|
public float GetModTPriority(){ return activeEffectMod.stats.turnPriority + PerkManager.GetUnitModTPrioity(prefabID); }
|
|
|
|
public int GetSight(){ return (int)Mathf.Round(stats.sight * GetMulSight() + GetModSight()); }
|
|
public float GetMulSight(){ return activeEffectMul.stats.sight * PerkManager.GetUnitMulSight(prefabID); }
|
|
public float GetModSight(){ return activeEffectMod.stats.sight + PerkManager.GetUnitModSight(prefabID); }
|
|
|
|
|
|
public bool IsStunned(){ return activeEffectMod.stun; }
|
|
public bool AbilityDisabled(){ return activeEffectMod.disableAbility; }
|
|
//public bool HasOverwatch(){ return activeEffectMod.overwatch; }
|
|
|
|
public float GetAttackM(){ return statsMelee.attack * GetAttackMul() + GetAttackMod(); }
|
|
public float GetHitM(){ return statsMelee.hit * GetHitMul() + GetHitMod(); }
|
|
public float GetDmgHPMinM(){ return statsMelee.dmgHPMin * GetMulDmgHPMin() + GetModDmgHPMin(); }
|
|
public float GetDmgHPMaxM(){ return statsMelee.dmgHPMax * GetMulDmgHPMax() + GetModDmgHPMax(); }
|
|
public float GetDmgAPMinM(){ return statsMelee.dmgAPMin * GetMulDmgAPMin() + GetModDmgAPMin(); }
|
|
public float GetDmgAPMaxM(){ return statsMelee.dmgAPMax * GetMulDmgAPMax() + GetModDmgAPMax(); }
|
|
|
|
public float GetCritChanceM(){ return statsMelee.critChance * GetMulCritChance() + GetModCritChance(); }
|
|
public float GetCritMultiplierM(){ return statsMelee.critMultiplier * GetMulCritMul() +GetModCritMul(); }
|
|
|
|
public int GetAttackRangeMelee(){ return (int)(statsMelee.attackRange); }
|
|
//public int GetAttackRangeMelee(){ return (int)(statsMelee.attackRange *activeEffectMul.stats.attackRange + activeEffectMod.stats.attackRange); }
|
|
|
|
|
|
|
|
public bool overwatching;
|
|
public bool HasOverwatch(){ return activeEffectMod.overwatch || overwatching; }
|
|
|
|
|
|
[Space(8)] //faction switch
|
|
public int tempFacID=-1;
|
|
public int tempFacDur=-1;
|
|
public bool tempFacControl=false;
|
|
|
|
public void SwitchFaction(int newFacID, int dur, bool controllable){
|
|
tempFacID=newFacID;
|
|
tempFacDur=dur;
|
|
tempFacControl=controllable;
|
|
|
|
UnitManager.AddFacSwitchUnit(this);
|
|
}
|
|
|
|
|
|
[Space(8)]
|
|
public List<int> immuneEffectList=new List<int>();
|
|
|
|
public Effect activeEffectMod;
|
|
public Effect activeEffectMul;
|
|
public List<Effect> effectList=new List<Effect>();
|
|
|
|
public Effect GetEffect(int idx){ return effectList[idx]; }
|
|
|
|
public void ApplyEffect(List<int> list){
|
|
if(list.Count==0) return;
|
|
for(int i=0; i<list.Count; i++){
|
|
Effect eff=EffectDB.GetPrefab(list[i]).Clone(true);
|
|
|
|
List<int> immuneList=PerkManager.ModifyUnitImmuneEffectList(prefabID, immuneEffectList);
|
|
if(immuneList.Contains(list[i])){
|
|
TBTK.TextOverlay("Immuned to "+eff.name, GetPos());
|
|
continue;
|
|
}
|
|
|
|
eff.hitVisualEffect.Spawn(GetPos(), Quaternion.identity);
|
|
if(eff.activeVisualEffect!=null){
|
|
eff.activeVisualEffect=ObjectPoolManager.Spawn(eff.activeVisualEffect, GetPos(), Quaternion.identity);
|
|
eff.activeVisualEffect.parent=thisT;
|
|
}
|
|
|
|
eff.durationRemain=eff.duration;
|
|
effectList.Add(eff);
|
|
ApplyEffectImpact(effectList[effectList.Count-1]);
|
|
}
|
|
UpdateActiveEffect();
|
|
}
|
|
|
|
public void UpdateActiveEffect(){
|
|
activeEffectMod=new Effect(); activeEffectMod.stats.ResetAsModifier();
|
|
activeEffectMul=new Effect(); activeEffectMul.stats.ResetAsMultiplier();
|
|
|
|
for(int i=0; i<effectList.Count; i++){
|
|
//if(effectList[i]==null){ effectList.RemoveAt(i); i-=1; continue; }
|
|
|
|
activeEffectMod.stun|=effectList[i].stun;
|
|
activeEffectMod.disableAbility|=effectList[i].disableAbility;
|
|
activeEffectMod.overwatch|=effectList[i].overwatch;
|
|
|
|
if(effectList[i].IsMultiplier()){
|
|
activeEffectMul.stats.ApplyMultiplier(effectList[i].stats);
|
|
}
|
|
else{
|
|
activeEffectMod.stats.ApplyModifier(effectList[i].stats);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RemoveOverwatch(){
|
|
for(int i=0; i<effectList.Count; i++){
|
|
if(effectList[i].overwatch){ effectList.RemoveAt(i); break; }
|
|
}
|
|
UpdateActiveEffect();
|
|
}
|
|
|
|
public void IterateCD(){
|
|
bool requireUpdate=false;
|
|
for(int i=0; i<effectList.Count; i++){
|
|
effectList[i].durationRemain-=1;
|
|
if(effectList[i].durationRemain<=0){
|
|
if(effectList[i].activeVisualEffect!=null){
|
|
ObjectPoolManager.Unspawn(effectList[i].activeVisualEffect);
|
|
}
|
|
|
|
requireUpdate=true;
|
|
effectList.RemoveAt(i); i-=1;
|
|
continue;
|
|
}
|
|
|
|
ApplyEffectImpact(effectList[i]);
|
|
}
|
|
if(requireUpdate) UpdateActiveEffect();
|
|
|
|
if(hp<=0) return; //UnitManager will call the detroy function
|
|
|
|
for(int i=0; i<abilityList.Count; i++) abilityList[i].IterateCD();
|
|
|
|
if(tempFacID>=0){
|
|
tempFacDur-=1;
|
|
if(tempFacDur<=0){
|
|
UnitManager.RemoveFacSwitchUnit(this);
|
|
tempFacID=-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ApplyEffectImpact(Effect eff){
|
|
if(eff.HasNoImpact()) return;
|
|
|
|
if(eff.HasPositiveImpact()){
|
|
hp=Mathf.Min(hp+eff.GetRandHPModifier(), GetFullHP());
|
|
ap=Mathf.Min(ap+eff.GetRandAPModifier(), GetFullAP());
|
|
}
|
|
if(eff.HasNegativeImpact()){
|
|
hp-=eff.GetRandHPModifier() * DamageTable.GetMultiplier(eff.damageType, armorType);
|
|
ap-=eff.GetRandAPModifier();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public void ResetEffect(bool forceUpdate=false){
|
|
if(!forceUpdate && effectList.Count==0) return;
|
|
effectList.Clear();
|
|
UpdateActiveEffect();
|
|
}
|
|
|
|
|
|
|
|
#region ability
|
|
[Space(8)]
|
|
public List<int> abilityIDList=new List<int>();
|
|
public List<Ability> abilityList=new List<Ability>(); //runtime attribute
|
|
public Ability GetAbility(int idx){ return abilityList[idx]; }
|
|
//private int selectedAbIdx=-1;
|
|
|
|
private bool abilityInitiated=false;
|
|
public void InitAbility(){
|
|
if(abilityInitiated) return;
|
|
abilityInitiated=true;
|
|
|
|
//abilityIDList=new List<int>{ 0, 1 };
|
|
|
|
abilityList.Clear();
|
|
|
|
List<int> extraAbIDList=PerkManager.GetUnitAbilityID(prefabID);
|
|
abilityIDList.AddRange(extraAbIDList);
|
|
|
|
for(int i=0; i<abilityIDList.Count; i++) AddAbility(abilityIDList[i]);
|
|
}
|
|
public void AddAbility(int abPrefabID){
|
|
abilityList.Add(AbilityUDB.GetPrefab(abPrefabID).Clone());
|
|
abilityList[abilityList.Count-1].Init(this, abilityList.Count-1);
|
|
}
|
|
|
|
|
|
public Ability._AbilityStatus SelectAbility(int idx){
|
|
Ability._AbilityStatus abilityStatus=abilityList[idx].IsAvailable();
|
|
if(abilityStatus!=Ability._AbilityStatus.Ready) return abilityStatus;
|
|
|
|
//~ int usable=abilityList[idx].IsAvailable();
|
|
//~ if(usable!=0) return usable;
|
|
|
|
if(!abilityList[idx].requireTarget){
|
|
//cast ability on self
|
|
UseAbility(idx, node);
|
|
}
|
|
else{
|
|
//GridManager.SetupAbilityTargetList(this, abilityList[idx]);
|
|
//selectedAbIdx=idx;
|
|
AbilityManager.AbilityTargetModeUnit(this, abilityList[idx]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public void UseAbility(int idx, Node target){
|
|
GameControl.UnitUseAbility(this, abilityList[idx], target);
|
|
//StartCoroutine(_UseAbility(abilityList[idx], target));
|
|
}
|
|
public IEnumerator UseAbilityRoutine(Ability ability, Node tgtNode){
|
|
bool actionCam=(actionCamCheck!=null && actionCamStart!=null && actionCamCheck(false));
|
|
if(actionCam) yield return StartCoroutine(actionCamStart(GetTargetPoint(), tgtNode.GetPos()));
|
|
|
|
ability.Activate();
|
|
|
|
if(ability.requireTarget){
|
|
if(ability.IsLine()) tgtNode=tgtNode.abLineParent;
|
|
|
|
//yield return StartCoroutine(AbilityRoutine(target, ability));
|
|
while(Rotate(tgtNode.GetPos())>2) yield return null;
|
|
|
|
if(ability.useAttackSequence){
|
|
bool useMelee=CheckUseMeleeAttack(tgtNode);
|
|
float attackDelay=AnimPlayAttack(useMelee); AudioPlayAttack(useMelee);
|
|
if(attackDelay>0) yield return new WaitForSeconds(attackDelay);
|
|
|
|
GameObject soObj=ability.shootObject!=null ? ability.shootObject.gameObject : GetShootObject(tgtNode);
|
|
//Vector3 offset=new Vector3(0, (ability.IsLine() ? shootPointList[0].position.y-node.GetPos().y : 0), 0);
|
|
Vector3 offset=new Vector3(0, shootPointList[0].position.y-node.GetPos().y, 0);
|
|
yield return StartCoroutine(FireShootObject(soObj, tgtNode, ability.aimAtUnit & ability.type!=Ability._AbilityType.Line, offset));
|
|
}
|
|
else{
|
|
float animationDelay=AnimPlayAbility(ability.index);
|
|
if(animationDelay>0) yield return new WaitForSeconds(animationDelay);
|
|
}
|
|
}
|
|
else{
|
|
float animationDelay=AnimPlayAbility(ability.index);
|
|
if(animationDelay>0) yield return new WaitForSeconds(animationDelay);
|
|
}
|
|
|
|
yield return CRoutine.Get().StartCoroutine(ability.HitTarget(tgtNode));
|
|
//AbilityHit(ability, target);
|
|
|
|
if(actionCam && actionCamEnd!=null) yield return StartCoroutine(actionCamEnd());
|
|
}
|
|
|
|
//~ public void AbilityHit(Ability ability, Node target){
|
|
//~ ability.HitTarget(target);
|
|
//~ }
|
|
#endregion
|
|
|
|
|
|
|
|
void CheckMoveSpeed(){
|
|
if(moveSpeed<=0){
|
|
Debug.LogError("Setting Error, unit move speed has been set to 0", gameObject);
|
|
moveSpeed=1;
|
|
}
|
|
}
|
|
|
|
private float Rotate(Vector3 tgtPos){ //for move
|
|
if(!enableRotation) return 0;
|
|
|
|
Quaternion wantedRot=Quaternion.LookRotation(tgtPos-thisT.position);
|
|
wantedRot=Quaternion.Euler(0, wantedRot.eulerAngles.y, 0);
|
|
|
|
if(instantRotate){ thisT.rotation=wantedRot; return 0; }
|
|
|
|
CheckMoveSpeed();
|
|
|
|
//turretObject.rotation=Quaternion.Slerp(turretObject.rotation, wantedRot, Time.deltaTime*moveSpeed*2);
|
|
thisT.rotation=Quaternion.Slerp(thisT.rotation, wantedRot, Time.deltaTime*moveSpeed*3f);
|
|
|
|
return Quaternion.Angle(thisT.rotation, wantedRot);
|
|
}
|
|
|
|
public bool Aiming(Vector3 tgtPoint){
|
|
if(turretPivot==null) return true;
|
|
|
|
//float elevation=shootObject.GetElevationAngle(shootPoint[0].position, tgtPoint);
|
|
|
|
if(!aimInXAxis || barrelPivot!=null) tgtPoint.y=turretPivot.position.y;
|
|
Quaternion wantedRot=Quaternion.LookRotation(tgtPoint-turretPivot.position);
|
|
//if(elevation!=0 && aimInXAxis && barrelPivot==null) wantedRot*=Quaternion.Euler(elevation, 0, 0);
|
|
|
|
if(snapAiming) turretPivot.rotation=wantedRot;
|
|
else turretPivot.rotation=Quaternion.Lerp(turretPivot.rotation, wantedRot, aimSpeed*Time.deltaTime);
|
|
|
|
if(!aimInXAxis || barrelPivot==null) return Quaternion.Angle(turretPivot.rotation, wantedRot)<2;
|
|
|
|
Quaternion wantedRotX=Quaternion.LookRotation(tgtPoint-barrelPivot.position);
|
|
//if(elevation!=0) wantedRotX*=Quaternion.Euler(elevation, 0, 0);
|
|
|
|
if(snapAiming) barrelPivot.rotation=wantedRotX;
|
|
else barrelPivot.rotation=Quaternion.Lerp(barrelPivot.rotation, wantedRotX, aimSpeed*Time.deltaTime*2);
|
|
|
|
return Quaternion.Angle(turretPivot.rotation, wantedRot)<2 & Quaternion.Angle(barrelPivot.rotation, wantedRotX)<2;
|
|
}
|
|
|
|
IEnumerator AimRoutine(Node tgtNode, float duration=3){
|
|
while(duration>0){
|
|
if(rotateWhileAiming) Rotate(tgtNode.GetPos());
|
|
if(Aiming(tgtNode.unit!=null ? tgtNode.unit.GetTargetPoint() : tgtNode.GetPos())) break;
|
|
duration-=Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
private Quaternion defaultTurretRot;
|
|
private Quaternion defaultBarrelRot;
|
|
IEnumerator ResetAim(){
|
|
while(turretPivot!=null){
|
|
turretPivot.localRotation=Quaternion.Lerp(turretPivot.localRotation, defaultTurretRot, aimSpeed*Time.deltaTime);
|
|
|
|
bool reset=false;
|
|
|
|
if(barrelPivot!=null){
|
|
barrelPivot.localRotation=Quaternion.Lerp(barrelPivot.localRotation, defaultBarrelRot, aimSpeed*Time.deltaTime*2);
|
|
reset=(Quaternion.Angle(turretPivot.localRotation, defaultTurretRot)<1 & Quaternion.Angle(barrelPivot.localRotation, defaultBarrelRot)<1);
|
|
}
|
|
else reset=(Quaternion.Angle(turretPivot.localRotation, defaultTurretRot)<1);
|
|
|
|
if(reset) break;
|
|
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
public IEnumerator MoveRoutine(Node targetNode, float speedMul=1){
|
|
List<Node> path=AStar.SearchWalkableNode(node, targetNode, AStar.BypassUnitCode(this), canMovePastObs, true);
|
|
//List<Node> path=AStar.SearchWalkableNode(node, targetNode, canMovePastUnit, canMovePastObs, true);
|
|
//while(path.Count>GetMoveRange()) path.RemoveAt(path.Count-1);
|
|
|
|
CheckMoveSpeed();
|
|
|
|
moveThisTurn+=1;
|
|
ap-=GameControl.GetAPPerMove()+path.Count*GameControl.GetAPPerNode();
|
|
|
|
waitingForMoveRoutine=true;
|
|
|
|
//Debug.Log("MoveRoutine "+path.Count+" "+node.GetPos()+" "+targetNode.GetPos());
|
|
|
|
Unit cachedUnit=null; //for when unit can goes pass an occupied node
|
|
|
|
AnimPlayMove(true); AudioPlayMove();
|
|
|
|
while(path.Count>0){
|
|
if(!IsVisible() && !path[0].IsVisible()){
|
|
thisT.position=path[0].GetPos();
|
|
}
|
|
else{
|
|
while(Rotate(path[0].GetPos())>5) yield return null;
|
|
|
|
while(true){
|
|
float dist=Vector3.Distance(thisT.position, path[0].GetPos());
|
|
if(dist<0.05f) break;
|
|
|
|
if(enableRotation){
|
|
Quaternion wantedRot=Quaternion.LookRotation(path[0].GetPos()-thisT.position);
|
|
wantedRot=Quaternion.Euler(0, wantedRot.eulerAngles.y, 0);
|
|
thisT.rotation=Quaternion.Slerp(thisT.rotation, wantedRot, Time.deltaTime*moveSpeed*speedMul);
|
|
}
|
|
|
|
Vector3 dir=(path[0].GetPos()-thisT.position).normalized;
|
|
thisT.Translate(dir*Mathf.Min(moveSpeed*Time.deltaTime*speedMul, dist), Space.World);
|
|
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
|
|
node.unit=cachedUnit; cachedUnit=null;
|
|
|
|
node=path[0];
|
|
|
|
if(path[0].unit!=null) cachedUnit=path[0].unit;
|
|
path[0].unit=this;
|
|
|
|
GridManager.SetupFogOfWar();
|
|
UnitManager.CheckAITrigger(this);
|
|
|
|
yield return StartCoroutine(CheckOverwatch());
|
|
if(hp<=0) break; //if destroyed by overwatch
|
|
|
|
path.RemoveAt(0);
|
|
}
|
|
|
|
AnimPlayMove(false); AudioStopMove();
|
|
|
|
if(node.collectible!=null) yield return StartCoroutine(node.collectible.Trigger(this));
|
|
|
|
//ApplyAttack(50, 0);
|
|
|
|
//UnitManager.SelectUnit(this);
|
|
|
|
if(IsVisible()) yield return new WaitForSeconds(0.2f);
|
|
|
|
waitingForMoveRoutine=false;
|
|
}
|
|
|
|
public IEnumerator CheckOverwatch(){
|
|
List<Unit> hostileList=UnitManager.GetAllHostileUnits(facID);
|
|
for(int i=0; i<hostileList.Count; i++){
|
|
if(!hostileList[i].HasOverwatch()) continue;
|
|
|
|
if(GameControl.UseAPToAttack() && GameControl.GetAPPerAttack()>hostileList[i].ap) continue;
|
|
|
|
int targetRange=GridManager.GetDistance(node, hostileList[i].node);
|
|
if(targetRange>hostileList[i].GetAttackRange()) continue;
|
|
|
|
float minAttackRange=hostileList[i].GetAttackRangeMin();
|
|
if(minAttackRange>0 && targetRange<minAttackRange) continue;
|
|
|
|
if(requireLOSToAttack && !GridManager.CheckLOS(node, hostileList[i].node, GetSight())) continue;
|
|
|
|
yield return StartCoroutine(hostileList[i].Overwatch(this));
|
|
if(hp<=0) yield break;
|
|
}
|
|
}
|
|
public IEnumerator Overwatch(Unit unit){
|
|
yield return StartCoroutine(AttackRoutine(unit.node, false, true));
|
|
}
|
|
|
|
|
|
public bool CheckUseMeleeAttack(Node tgtNode){
|
|
if(!hasMeleeAttack) return false;
|
|
return GridManager.GetDistance(node, tgtNode)<=GetAttackRangeMelee();
|
|
}
|
|
public GameObject GetShootObject(Node tgtNode){
|
|
bool useMelee=CheckUseMeleeAttack(tgtNode);
|
|
if(useMelee && soMelee!=null) return soMelee.gameObject;
|
|
if(!useMelee && soRange!=null) return soRange.gameObject;
|
|
|
|
//Debug.LogWarning("No ShootObject - you need to assign shoot-object to unit prefab for attack to work");
|
|
|
|
return GetDummySO();
|
|
//return CheckUseMeleeAttack(tgtNode) ? soMelee.gameObject : soRange.gameObject ;
|
|
}
|
|
|
|
public IEnumerator AttackRoutine(Node targetNode, bool isCounter=false, bool isOverwatch=false){
|
|
if(!isOverwatch){
|
|
if(!isCounter){
|
|
attackThisTurn+=1;
|
|
ap-=GameControl.GetAPPerAttack();
|
|
}
|
|
else{
|
|
counterThisTurn+=1;
|
|
}
|
|
}
|
|
else{
|
|
RemoveOverwatch();
|
|
}
|
|
|
|
if(!isCounter && !isOverwatch && GameControl.EndMoveAfterAttack()) EndAllAction();
|
|
|
|
bool actionCam=(actionCamCheck!=null && actionCamStart!=null && actionCamCheck(true));
|
|
if(actionCam) yield return StartCoroutine(actionCamStart(GetTargetPoint(), targetNode.GetPos()));
|
|
//yield return StartCoroutine(CameraControl.ActionCamFadeIn(GetTargetPoint(), targetNode.GetPos()));
|
|
|
|
yield return StartCoroutine(AimRoutine(targetNode));
|
|
|
|
bool useMelee=CheckUseMeleeAttack(targetNode);
|
|
|
|
float attackDelay=AnimPlayAttack(useMelee); AudioPlayAttack(useMelee);
|
|
if(attackDelay>0) yield return new WaitForSeconds(attackDelay);
|
|
|
|
yield return StartCoroutine(FireShootObject(GetShootObject(targetNode), targetNode, true));
|
|
|
|
if(targetNode.unit!=null){
|
|
Attack attack=new Attack(this, targetNode.unit, null, isCounter, isOverwatch);
|
|
|
|
//attack.hit=true; attack.crit=true;
|
|
|
|
if(attack.hit){
|
|
if(!useMelee) effectAttackHit.Spawn(targetNode.unit.GetTargetPoint(), Quaternion.identity);
|
|
else effectAttackHitMelee.Spawn(targetNode.unit.GetTargetPoint(), Quaternion.identity);
|
|
}
|
|
|
|
bool targetDestroyed=targetNode.unit.ApplyAttack(attack);
|
|
//bool targetDestroyed=targetNode.unit.ApplyAttack(this, isCounter, isOverwatch);
|
|
|
|
if(GameControl.EnableCounterAttack() && !targetDestroyed && !isCounter && !isOverwatch && targetNode.unit.CanCounter(this)){
|
|
waitingForCounter=true;
|
|
yield return StartCoroutine(targetNode.unit.AttackRoutine(node, true, false));
|
|
}
|
|
}
|
|
|
|
if(actionCam && actionCamEnd!=null) yield return StartCoroutine(actionCamEnd());
|
|
//yield return StartCoroutine(CameraControl.ActionCamFadeOut());
|
|
|
|
if(waitingForAttackAnimation){ while(waitingForAttackAnimation) yield return null; }
|
|
else yield return new WaitForSeconds(0.2f);
|
|
|
|
if(turretPivot!=thisT) StartCoroutine(ResetAim());
|
|
|
|
waitingForCounter=false;
|
|
}
|
|
public IEnumerator FireShootObject(GameObject soPrefab, Node tgtNode, bool aimAtUnit, Vector3 offset=default(Vector3)){
|
|
waitingForHit=true;
|
|
|
|
for(int i=0; i<shootPointList.Count; i++){
|
|
GameObject sObj=(GameObject)Instantiate(soPrefab, shootPointList[i].position, shootPointList[i].rotation);
|
|
ShootObject soInstance=sObj.GetComponent<ShootObject>();
|
|
|
|
if(aimAtUnit && tgtNode.unit!=null){
|
|
if(i==shootPointList.Count-1) soInstance.InitShoot(tgtNode.unit, HitCallback, shootPointList[i]);
|
|
else soInstance.InitShoot(tgtNode.unit, null, shootPointList[i]);
|
|
}
|
|
else{
|
|
if(i==shootPointList.Count-1) soInstance.InitShoot(tgtNode, HitCallback, shootPointList[i], offset);
|
|
else soInstance.InitShoot(tgtNode, null, shootPointList[i], offset);
|
|
}
|
|
|
|
if(i<shootPointList.Count-1) yield return new WaitForSeconds(shootPointSpacing);
|
|
}
|
|
|
|
while(waitingForHit) yield return null;
|
|
}
|
|
|
|
private bool waitingForMoveRoutine=false;
|
|
private bool waitingForCounter=false;
|
|
private bool waitingForHit=false;
|
|
public void HitCallback(){ waitingForHit=false; }
|
|
|
|
|
|
//public bool ApplyAttack(Unit srcUnit, bool isCounter, bool isOverwatch){
|
|
public bool ApplyAttack(Attack attack){
|
|
if(!playableUnit && !triggered) triggered=true;
|
|
|
|
//Attack attack=new Attack(srcUnit, this, null, isCounter, isOverwatch);
|
|
Overlay(attack);
|
|
bool destroyed=_ApplyAttack(attack.damageHP, attack.damageAP);
|
|
if(!destroyed) ApplyEffect(attack.srcUnit.GetRuntimeAttackEffectIDList());
|
|
return destroyed;
|
|
}
|
|
public bool ApplyAttack(Ability srcAbility){
|
|
bool destroyed=false;
|
|
bool applyEffect=Rand.value()<srcAbility.GetEffHitChance();
|
|
|
|
if(srcAbility.clearAllEffect) ResetEffect();
|
|
|
|
if(srcAbility.HasNegativeImpact()){
|
|
if(srcAbility.factorInTargetStats){
|
|
Attack attack=new Attack(srcAbility, this); Overlay(attack);
|
|
destroyed=_ApplyAttack(attack.damageHP, attack.damageAP);
|
|
}
|
|
else{
|
|
float dmg=srcAbility.GetRandHPModifier() * DamageTable.GetMultiplier(srcAbility.damageType, armorType);
|
|
TBTK.TextOverlay(Mathf.Round(dmg).ToString("f0"), GetPos());
|
|
_ApplyAttack(dmg);
|
|
ap-=srcAbility.GetRandAPModifier();
|
|
}
|
|
}
|
|
else if(srcAbility.HasPositiveImpact()){
|
|
hp=Mathf.Min(hp+srcAbility.GetRandHPModifier(), GetFullHP());
|
|
ap=Mathf.Min(ap+srcAbility.GetRandAPModifier(), GetFullAP());
|
|
}
|
|
|
|
if(!destroyed && applyEffect){
|
|
ApplyEffect(srcAbility.GetRuntimeEffectIDList());
|
|
|
|
if(srcAbility.switchFaction && srcAbility.facID!=facID){
|
|
SwitchFaction(srcAbility.facID, srcAbility.GetDuration(), srcAbility.switchFacControllable);
|
|
}
|
|
}
|
|
|
|
return destroyed;
|
|
}
|
|
public bool _ApplyAttack(float damageHP, float damageAP=0){
|
|
if(damageAP>0) ap=Mathf.Max(0, ap-damageAP);
|
|
|
|
if(damageHP<=0) return false;
|
|
|
|
AnimPlayHit(); AudioPlayHit();
|
|
|
|
hp-=damageHP;
|
|
|
|
if(hp<=0){
|
|
hp=0;
|
|
StartCoroutine(DestroyRoutine());
|
|
}
|
|
|
|
return hp<=0;
|
|
}
|
|
|
|
|
|
public void Overlay(Attack attack){ StartCoroutine(_Overlay(attack)); }
|
|
public IEnumerator _Overlay(Attack attack){
|
|
if(attack.hit){
|
|
if(attack.damageAP>0){
|
|
if(attack.crit) TBTK.TextOverlay("<i>Critical -"+Mathf.Round(attack.damageAP).ToString("f0")+"AP</i>", GetPos()+new Vector3(0, .5f, 0));
|
|
else TBTK.TextOverlay("<i>-"+Mathf.Round(attack.damageAP).ToString("f0")+"AP</i>", GetPos()+new Vector3(0, .5f, 0));
|
|
|
|
yield return new WaitForSeconds(0.15f);
|
|
}
|
|
|
|
if(attack.damageHP>0){
|
|
if(attack.crit) TBTK.TextOverlay("Critical -"+Mathf.Round(attack.damageHP).ToString("f0")+"HP", GetPos());
|
|
else TBTK.TextOverlay("-"+Mathf.Round(attack.damageHP).ToString("f0")+"HP", GetPos());
|
|
}
|
|
}
|
|
else TBTK.TextOverlay("Missed", GetPos());
|
|
|
|
yield return null;
|
|
}
|
|
|
|
|
|
[Space(8)] public bool dead=false;
|
|
public IEnumerator DestroyRoutine(){
|
|
node.unit=null;
|
|
UnitManager.UnitDestroyed(this);
|
|
|
|
//yield return null; //wait a frame for all on-going coroutine to end
|
|
while(waitingForCounter) yield return null;
|
|
while(waitingForMoveRoutine) yield return null;
|
|
|
|
effectOnDestroyed.Spawn(GetTargetPoint());
|
|
|
|
float delay=Mathf.Max(AudioPlayDestroy(), AnimPlayDestroyed());
|
|
if(delay>0) yield return new WaitForSeconds(delay);
|
|
|
|
yield return null;
|
|
|
|
Destroy(thisObj);
|
|
}
|
|
|
|
|
|
public bool IsAllActionCompleted(){
|
|
if(hp<=0) return true;
|
|
|
|
if(CanMove()) return false;
|
|
if(CanAttack()) return false;
|
|
|
|
for(int i=0; i<abilityList.Count; i++){
|
|
if(abilityList[i].IsAvailable()==0) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void EndAllAction(){
|
|
ap=0;
|
|
moveThisTurn=(int)stats.moveLimit;
|
|
//attackThisTurn=(int)stats.attackLimit; //dont end attack incase the unit can do two attack in a single turn
|
|
abilityThisTurn=(int)stats.abilityLimit;
|
|
}
|
|
|
|
|
|
public bool IsVisible(){ return thisObj.layer!=TBTK.GetLayerInvisible(); }
|
|
|
|
|
|
private static GameObject dummySO;
|
|
public static GameObject GetDummySO(){
|
|
if(dummySO==null){
|
|
dummySO=new GameObject("Dummy ShootObject");
|
|
ShootObject so=dummySO.AddComponent<ShootObject>();
|
|
so.type=ShootObject._Type.Effect;
|
|
so.effectDuration=0.25f;
|
|
}
|
|
|
|
return dummySO;
|
|
}
|
|
|
|
|
|
|
|
|
|
[Header("Visual Effects")]
|
|
public VisualObject effectAttackHit=new VisualObject();
|
|
public VisualObject effectAttackHitMelee=new VisualObject();
|
|
|
|
public VisualObject effectOnDestroyed=new VisualObject();
|
|
|
|
|
|
#region animation
|
|
[Header("Animation")]
|
|
public Transform animatorT;
|
|
protected Animator animator;
|
|
|
|
[Space(5)]
|
|
public AnimationClip clipIdle;
|
|
public AnimationClip clipMove;
|
|
public AnimationClip clipHit;
|
|
public AnimationClip clipDestroyed;
|
|
|
|
public AnimationClip clipAttackRange;
|
|
public AnimationClip clipAttackMelee;
|
|
public float animAttackDelayRange=0;
|
|
public float animAttackDelayMelee=0;
|
|
|
|
public List<AnimationClip> clipAbilityList=new List<AnimationClip>();
|
|
public List<float> animAbilityDelayList=new List<float>();
|
|
|
|
private void InitAnimation(){
|
|
if(animatorT!=null) animator=animatorT.GetComponent<Animator>();
|
|
if(animator==null) return;
|
|
|
|
AnimatorOverrideController aniOverrideController = new AnimatorOverrideController();
|
|
aniOverrideController.runtimeAnimatorController = animator.runtimeAnimatorController;
|
|
animator.runtimeAnimatorController = aniOverrideController;
|
|
|
|
if(clipIdle!=null) aniOverrideController["Idle"] = clipIdle;
|
|
if(clipMove!=null) aniOverrideController["Move"] = clipMove;
|
|
if(clipHit!=null) aniOverrideController["Hit"] = clipHit;
|
|
if(clipAttackRange!=null) aniOverrideController["AttackRange"] = clipAttackRange;
|
|
if(clipAttackMelee!=null) aniOverrideController["AttackMelee"] = clipAttackMelee;
|
|
if(clipDestroyed!=null) aniOverrideController["Destroyed"] = clipDestroyed;
|
|
|
|
for(int i=0; i<6; i++){
|
|
if(clipAbilityList[i]==null) continue;
|
|
aniOverrideController["Ability"+(i+1)] = clipAbilityList[i];
|
|
}
|
|
}
|
|
|
|
private float AnimPlayAbility(int idx){
|
|
Debug.Log("AnimPlayAbility "+idx);
|
|
if(animator==null || idx<0 || clipAbilityList.Count<=idx || clipAbilityList[idx]==null) return 0;
|
|
animator.SetTrigger("Ability"+(idx+1));
|
|
return animAbilityDelayList.Count>idx ? animAbilityDelayList[idx] : 0;
|
|
}
|
|
|
|
private void AnimPlayMove(bool moving){
|
|
if(animator!=null) animator.SetBool("Moving", moving);
|
|
}
|
|
private void AnimPlayHit(){
|
|
if(animator!=null && clipHit!=null) animator.SetTrigger("Hit");
|
|
}
|
|
private float AnimPlayDestroyed(){
|
|
if(animator==null || clipDestroyed==null) return 0;
|
|
if(clipDestroyed!=null) animator.SetBool("Destroyed", true);
|
|
return clipDestroyed!=null ? clipDestroyed.length : 0 ;
|
|
}
|
|
private float AnimPlayAttack(bool isMelee){
|
|
if(isMelee){
|
|
if(animator==null || clipAttackMelee==null) return 0;
|
|
if(clipAttackMelee!=null){
|
|
animator.SetTrigger("AttackMelee");
|
|
StartCoroutine(WaitingForAttackAnimation(clipAttackMelee.length));
|
|
}
|
|
return animAttackDelayMelee;
|
|
}
|
|
else{
|
|
if(animator==null || clipAttackRange==null) return 0;
|
|
if(clipAttackRange!=null){
|
|
animator.SetTrigger("AttackRange");
|
|
StartCoroutine(WaitingForAttackAnimation(clipAttackRange.length));
|
|
}
|
|
return animAttackDelayRange;
|
|
}
|
|
}
|
|
|
|
private bool waitingForAttackAnimation=false;
|
|
IEnumerator WaitingForAttackAnimation(float duration){
|
|
waitingForAttackAnimation=true;
|
|
yield return new WaitForSeconds(duration);
|
|
waitingForAttackAnimation=false;
|
|
}
|
|
|
|
|
|
//private int attackCounter=0;
|
|
#endregion
|
|
|
|
|
|
#region animation
|
|
[Header("Audio")]
|
|
public bool loopMoveSound=false;
|
|
private AudioSource audioSrc;
|
|
|
|
public AudioClip selectSound;
|
|
public AudioClip moveSound;
|
|
public AudioClip attackRangeSound;
|
|
public AudioClip attackMeleeSound;
|
|
public AudioClip hitSound;
|
|
public AudioClip destroySound;
|
|
|
|
|
|
private void InitAudio(){
|
|
if(!loopMoveSound || audioSrc!=null) return;
|
|
|
|
audioSrc=gameObject.AddComponent<AudioSource>();
|
|
audioSrc.playOnAwake=false; audioSrc.loop=true; audioSrc.volume=1; //src.spatialBlend=.75f;
|
|
audioSrc.clip=moveSound;
|
|
}
|
|
private void AudioPlayMove(){
|
|
if(moveSound==null) return;
|
|
|
|
if(loopMoveSound){
|
|
InitAudio();
|
|
audioSrc.Play();
|
|
return;
|
|
}
|
|
else AudioManager.PlaySound(moveSound, GetPos());
|
|
}
|
|
private void AudioStopMove(){
|
|
if(audioSrc!=null && moveSound!=null) audioSrc.Stop();
|
|
}
|
|
|
|
|
|
|
|
public void AudioPlaySelect(){
|
|
if(selectSound!=null) AudioManager.PlaySound(selectSound, GetPos());
|
|
}
|
|
public void AudioPlayAttack(bool isMelee){
|
|
if(!isMelee && attackRangeSound!=null) AudioManager.PlaySound(attackRangeSound, GetPos());
|
|
else if(attackMeleeSound!=null) AudioManager.PlaySound(attackMeleeSound, GetPos());
|
|
}
|
|
public void AudioPlayHit(){
|
|
if(hitSound!=null) AudioManager.PlaySound(hitSound, GetPos());
|
|
}
|
|
public float AudioPlayDestroy(){
|
|
if(destroySound==null) return 0;
|
|
AudioManager.PlaySound(destroySound, GetPos());
|
|
return destroySound.length;
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|