411 lines
16 KiB
C#
411 lines
16 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace TBTK{
|
|
|
|
[System.Serializable]
|
|
public class Ability : TBTKItem{
|
|
|
|
public enum _AbilityType{ Generic, Teleport, SpawnUnit, Charge, Line, Cone, ScanFogOfWar, None, }
|
|
|
|
public enum _TargetType{AllNode, AllUnit, HostileUnit, FriendlyUnit, EmptyNode}
|
|
public enum _ImpactType{None, Negative, Positive}
|
|
|
|
|
|
[HideInInspector] public Unit srcUnit; //runtime attribute
|
|
[HideInInspector] public int facID; //runtime attribute
|
|
[HideInInspector] public int index; //runtime attribute
|
|
public void Init(Unit unit, int idx){ srcUnit=unit; facID=srcUnit.facID; index=idx; isUnitAbility=true; }
|
|
public void Init(int fID, int idx){ facID=fID; index=idx; isFacAbility=true; }
|
|
|
|
[HideInInspector] public bool isUnitAbility=false;
|
|
[HideInInspector] public bool isFacAbility=false;
|
|
|
|
public _AbilityType type;
|
|
public bool IsLine(){ return type==_AbilityType.Line; }
|
|
//public bool IsGeneric(){ return type==_AbilityType.Generic; }
|
|
//public bool IsTeleport(){ return type==_AbilityType.Teleport; }
|
|
|
|
public _TargetType targetType;
|
|
public bool requireTarget=true;
|
|
public bool requireLos=true;
|
|
public int rangeMin=0;
|
|
public int range=5;
|
|
public int aoeRange=0;
|
|
public bool useLineAOE=false; //aoe only applies in the directions of the adjacent neighbours
|
|
|
|
public int fov=60;
|
|
|
|
//[HideInInspector]
|
|
public bool TargetStraightLineOnly(){ return type==_AbilityType.Charge | type==_AbilityType.Line; }
|
|
public bool TargetCone(){ return type==_AbilityType.Cone; }
|
|
|
|
public Unit spawnUnitPrefab;
|
|
|
|
public int moveCost;
|
|
public int attackCost;
|
|
public int abilityCost;
|
|
public int apCost;
|
|
public bool endAllActionAfterUse;
|
|
|
|
public int cooldown=1;
|
|
[HideInInspector] private int currentCD; //runtime attribute
|
|
|
|
public int useLimit=0;
|
|
[HideInInspector] private int useCount; //runtime attribute
|
|
|
|
public float impactDelay=0f;
|
|
|
|
|
|
|
|
//for generic type
|
|
public _ImpactType impactType;
|
|
|
|
public bool HasNoImpact(){ return impactType==_ImpactType.None; }
|
|
public bool HasPositiveImpact(){ return impactType==_ImpactType.Positive; }
|
|
public bool HasNegativeImpact(){ return impactType==_ImpactType.Negative; }
|
|
|
|
public int hpModifierMin=5;
|
|
public int hpModifierMax=5;
|
|
public int GetRandHPModifier(){ return (int)Mathf.Round(Rand.Range(GetHPMin(), GetHPMax())); }
|
|
|
|
public int apModifierMin=5;
|
|
public int apModifierMax=5;
|
|
public int GetRandAPModifier(){ return (int)Mathf.Round(Rand.Range(GetAPMin(), GetAPMax())); }
|
|
|
|
//used when impactType=_AbInstantImpact.Negative
|
|
public int damageType=0; //public bool useDamageTable=false;
|
|
public float attack=1; //public float GetAttack(){ return attack; }
|
|
public float hitChance=1; //public float GetHit(){ return hitChance; }
|
|
public float critChance=0; //public float GetCritChance(){ return critChance; }
|
|
public float critMultiplier=2; //public float GetCritMultiplier(){ return critMultiplier; }
|
|
public bool factorInTargetStats=false;
|
|
|
|
|
|
public float effHitChance=0f; //applies for effctIDList, clearAllEffect, switchFaction
|
|
|
|
public List<int> effectIDList=new List<int>();
|
|
|
|
public bool clearAllEffect=false;
|
|
|
|
//for factionSwitch
|
|
public bool switchFaction=false;
|
|
//public int switchFacDuration=1;
|
|
public bool switchFacControllable=true; //can be unit be controlled directly after a faction-switch
|
|
|
|
//public int revealFogDuration=1;
|
|
|
|
public int duration=1; //for switch-faction and reveal-fog
|
|
|
|
//animation
|
|
public bool useAttackSequence;
|
|
public bool aimAtUnit;
|
|
public ShootObject shootObject;
|
|
|
|
|
|
//visual effects
|
|
public VisualObject effectOnUse;
|
|
public VisualObject effectOnHit;
|
|
|
|
public AudioClip activateSound;
|
|
|
|
|
|
public enum _AbilityStatus{
|
|
Ready,
|
|
OnCooldown,
|
|
HitUsedLimit,
|
|
Disabled,
|
|
InsufficientAP,
|
|
HitAbilityPerTurnLimit,
|
|
HitMovePerTurnLimit,
|
|
}
|
|
|
|
public _AbilityStatus IsAvailable(){
|
|
if(currentCD>0) return _AbilityStatus.OnCooldown;
|
|
if(HasUseLimit() && GetUseRemain()<=0) return _AbilityStatus.HitUsedLimit;
|
|
|
|
if(isUnitAbility){
|
|
if(srcUnit.AbilityDisabled()) return _AbilityStatus.Disabled;
|
|
if(srcUnit.ap<GetAPCost()) return _AbilityStatus.InsufficientAP;
|
|
if(srcUnit.GetAbilityRemain()<abilityCost) return _AbilityStatus.HitAbilityPerTurnLimit;
|
|
if(srcUnit.GetMoveRemain()<moveCost || srcUnit.GetAttackRemain()<attackCost) return _AbilityStatus.HitMovePerTurnLimit;
|
|
}
|
|
|
|
//check available target?
|
|
return _AbilityStatus.Ready;
|
|
}
|
|
|
|
public void IterateCD(){ if(currentCD>0) currentCD-=1; }
|
|
public int GetCurrentCD(){ return currentCD-1; }
|
|
|
|
public bool HasUseLimit(){ return useLimit>0; }
|
|
public int GetUseRemain(){ return GetUseLimit()-useCount; }
|
|
|
|
public void Activate(){
|
|
Debug.Log("Ability activated - "+name);
|
|
|
|
useCount+=1;
|
|
currentCD=GetCooldown();
|
|
|
|
if(srcUnit!=null){
|
|
if(!endAllActionAfterUse){
|
|
srcUnit.moveThisTurn+=moveCost;
|
|
srcUnit.attackThisTurn+=attackCost;
|
|
srcUnit.abilityThisTurn+=abilityCost;
|
|
srcUnit.ap-=GetAPCost();
|
|
}
|
|
else srcUnit.EndAllAction();
|
|
|
|
effectOnUse.Spawn(srcUnit.GetPos());
|
|
}
|
|
|
|
AudioManager.PlaySound(activateSound);
|
|
}
|
|
|
|
|
|
|
|
public IEnumerator HitTarget(Node node){
|
|
if(impactDelay>0) yield return new WaitForSeconds(impactDelay);
|
|
|
|
if(type==_AbilityType.Generic){
|
|
//Debug.Log(!isUnitAbility +" "+ !requireTarget);
|
|
if(!isUnitAbility && !requireTarget){ //faction ability that doesn't require target will get all valid target on the grid
|
|
List<Node> nodeList=GridManager.GetTargetNodeForNonTargetingFAbility(this);
|
|
//Debug.Log(" "+nodeList.Count);
|
|
for(int i=0; i<nodeList.Count; i++){
|
|
|
|
nodeList[i].unit.ApplyAttack(this);
|
|
effectOnHit.Spawn(nodeList[i].GetPos());
|
|
}
|
|
}
|
|
else{
|
|
int aoe=GetAOE();
|
|
if(aoe<=0){
|
|
if(node.unit!=null) node.unit.ApplyAttack(this);
|
|
}
|
|
else{
|
|
List<Node> nodeList=GridManager.GetNodesWithinDistance(node, aoe);
|
|
nodeList.Add(node);
|
|
|
|
for(int i=0; i<nodeList.Count; i++){
|
|
if(nodeList[i].unit==null) continue;
|
|
|
|
if(targetType==Ability._TargetType.AllUnit){
|
|
nodeList[i].unit.ApplyAttack(this);
|
|
}
|
|
else if(targetType==Ability._TargetType.HostileUnit){
|
|
if(nodeList[i].unit.GetFacID()!=facID) nodeList[i].unit.ApplyAttack(this);
|
|
}
|
|
else if(targetType==Ability._TargetType.FriendlyUnit){
|
|
if(nodeList[i].unit.GetFacID()==facID) nodeList[i].unit.ApplyAttack(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(type==_AbilityType.Teleport){
|
|
if(srcUnit!=null){
|
|
srcUnit.node.unit=null;
|
|
srcUnit.node=node;
|
|
srcUnit.node.unit=srcUnit;
|
|
srcUnit.GetT().position=node.GetPos();
|
|
|
|
GridManager.SetupFogOfWar();
|
|
UnitManager.CheckAITrigger(srcUnit);
|
|
if(node.collectible!=null) yield return srcUnit.StartCoroutine(node.collectible.Trigger(srcUnit));
|
|
}
|
|
}
|
|
else if(type==_AbilityType.SpawnUnit){
|
|
GameObject obj=(GameObject)MonoBehaviour.Instantiate(spawnUnitPrefab.gameObject, node.GetPos(), Quaternion.identity);
|
|
Unit unit=obj.GetComponent<Unit>();
|
|
unit.node=node; node.unit=unit;
|
|
UnitManager.AddUnit(unit, srcUnit!=null ? srcUnit.GetFacID() : facID);
|
|
|
|
GridManager.SetupFogOfWar();
|
|
}
|
|
else if(type==_AbilityType.Charge){
|
|
if(node.unit!=null){
|
|
float wantedAngle=GridManager.GetAngle(srcUnit.node, node, true);
|
|
float tgtDist=GridManager.GetDistance(srcUnit.node, node);
|
|
List<Node> neighbours=node.GetNeighbourList(true);
|
|
for(int i=0; i<neighbours.Count; i++){
|
|
float dist=GridManager.GetDistance(srcUnit.node, neighbours[i]);
|
|
float angle=Mathf.Abs(wantedAngle-GridManager.GetAngle(srcUnit.node, neighbours[i], true));
|
|
if(dist>=tgtDist || angle>1) continue;
|
|
yield return CRoutine.Get().StartCoroutine(srcUnit.MoveRoutine(neighbours[i], 3));
|
|
break;
|
|
}
|
|
}
|
|
|
|
int aoe=GetAOE();
|
|
if(aoe<=0){
|
|
if(node.unit!=null) node.unit.ApplyAttack(this);
|
|
}
|
|
else{
|
|
List<Node> nodeList=GridManager.GetNodesWithinDistance(node, aoe);
|
|
for(int i=0; i<nodeList.Count; i++){
|
|
if(nodeList[i].unit!=null && !OnSameFac(srcUnit, nodeList[i].unit)) nodeList[i].unit.ApplyAttack(this);
|
|
}
|
|
}
|
|
}
|
|
else if(type==_AbilityType.Line || type==_AbilityType.Cone){
|
|
List<Node> nodeList=new List<Node>();
|
|
if(type==_AbilityType.Line) nodeList=GridManager.GetNodesInALine(srcUnit.node, GridManager.GetAngle(node, srcUnit.node, true), GetRange());
|
|
if(type==_AbilityType.Cone) nodeList=GridManager.GetNodesInACone(srcUnit.node, node, GetRange(), fov);
|
|
|
|
for(int i=0; i<nodeList.Count; i++){
|
|
if(nodeList[i].unit==null) continue;
|
|
|
|
if(targetType==_TargetType.HostileUnit){
|
|
if(!OnSameFac(srcUnit, nodeList[i].unit)) nodeList[i].unit.ApplyAttack(this);
|
|
}
|
|
else if(targetType==_TargetType.FriendlyUnit){
|
|
if(OnSameFac(srcUnit, nodeList[i].unit)) nodeList[i].unit.ApplyAttack(this);
|
|
}
|
|
else nodeList[i].unit.ApplyAttack(this);
|
|
}
|
|
}
|
|
else if(type==_AbilityType.ScanFogOfWar){
|
|
if(GameControl.EnableFogOfWar()){
|
|
List<Node> nodeList=GridManager.GetNodesWithinDistance(node, GetAOE());
|
|
for(int i=0; i<nodeList.Count; i++) nodeList[i].RevealFogOfWar(GetDuration());
|
|
}
|
|
}
|
|
|
|
if(node!=null) effectOnHit.Spawn(node.GetPos());
|
|
}
|
|
|
|
public static bool OnSameFac(Unit unit1, Unit unit2){ return unit1.GetFacID()==unit2.GetFacID(); }
|
|
|
|
|
|
public bool IsUAB(){ return isUnitAbility; }
|
|
|
|
|
|
public List<int> GetRuntimeEffectIDList(){
|
|
if(IsUAB()) return PerkManager.ModifyUAbilityEffectList(prefabID, new List<int>( effectIDList ));
|
|
else return PerkManager.ModifyFAbilityEffectList(prefabID, new List<int>( effectIDList ));
|
|
}
|
|
|
|
public int GetAPCost(){
|
|
if(IsUAB()) return (int)(apCost * PerkManager.GetUAbilityMulAPCost(prefabID) + PerkManager.GetUAbilityModAPCost(prefabID));
|
|
else return (int)(apCost * PerkManager.GetFAbilityMulAPCost(prefabID) + PerkManager.GetFAbilityModAPCost(prefabID));
|
|
}
|
|
public int GetDuration(){
|
|
if(IsUAB()) return (int)(duration * PerkManager.GetUAbilityMulDur(prefabID) + PerkManager.GetUAbilityModDur(prefabID));
|
|
else return(int)( duration * PerkManager.GetFAbilityMulDur(prefabID) + PerkManager.GetFAbilityModDur(prefabID));
|
|
}
|
|
public int GetCooldown(){
|
|
if(IsUAB()) return (int)(cooldown * PerkManager.GetUAbilityMulCD(prefabID) + PerkManager.GetUAbilityModCD(prefabID));
|
|
else return(int)( cooldown * PerkManager.GetFAbilityMulCD(prefabID) + PerkManager.GetFAbilityModCD(prefabID));
|
|
}
|
|
public int GetUseLimit(){
|
|
if(IsUAB()) return (int)(useLimit * PerkManager.GetUAbilityMulUseLim(prefabID) + PerkManager.GetUAbilityModUseLim(prefabID));
|
|
else return (int)(useLimit * PerkManager.GetFAbilityMulUseLim(prefabID) + PerkManager.GetFAbilityModUseLim(prefabID));
|
|
}
|
|
|
|
|
|
public float GetAttack(){
|
|
if(IsUAB()) return attack * PerkManager.GetUAbilityMulAttack(prefabID) + PerkManager.GetUAbilityModAttack(prefabID);
|
|
else return attack * PerkManager.GetFAbilityMulAttack(prefabID) + PerkManager.GetFAbilityModAttack(prefabID);
|
|
}
|
|
public float GetHit(){
|
|
if(IsUAB()) return hitChance * PerkManager.GetUAbilityMulHit(prefabID) + PerkManager.GetUAbilityModHit(prefabID);
|
|
else return hitChance * PerkManager.GetFAbilityMulHit(prefabID) + PerkManager.GetFAbilityModHit(prefabID);
|
|
}
|
|
public float GetHPMin(){
|
|
if(IsUAB()) return hpModifierMin * PerkManager.GetUAbilityMulDmgHPMin(prefabID) + PerkManager.GetUAbilityModDmgHPMin(prefabID);
|
|
else return hpModifierMin * PerkManager.GetFAbilityMulDmgHPMin(prefabID) + PerkManager.GetFAbilityModDmgHPMin(prefabID);
|
|
}
|
|
public float GetHPMax(){
|
|
if(IsUAB()) return hpModifierMax * PerkManager.GetUAbilityMulDmgHPMax(prefabID) + PerkManager.GetUAbilityModDmgHPMax(prefabID);
|
|
else return hpModifierMax * PerkManager.GetFAbilityMulDmgHPMax(prefabID) + PerkManager.GetFAbilityModDmgHPMax(prefabID);
|
|
}
|
|
public float GetAPMin(){
|
|
if(IsUAB()) return apModifierMin * PerkManager.GetUAbilityMulDmgAPMin(prefabID) + PerkManager.GetUAbilityModDmgAPMin(prefabID);
|
|
else return apModifierMin * PerkManager.GetFAbilityMulDmgAPMin(prefabID) + PerkManager.GetFAbilityModDmgAPMin(prefabID);
|
|
}
|
|
public float GetAPMax(){
|
|
if(IsUAB()) return apModifierMax * PerkManager.GetUAbilityMulDmgAPMax(prefabID) + PerkManager.GetUAbilityModDmgAPMax(prefabID);
|
|
else return apModifierMax * PerkManager.GetFAbilityMulDmgAPMax(prefabID) + PerkManager.GetFAbilityModDmgAPMax(prefabID);
|
|
}
|
|
public float GetCritChance(){
|
|
if(IsUAB()) return critChance * PerkManager.GetUAbilityMulCritC(prefabID) + PerkManager.GetUAbilityModCritC(prefabID);
|
|
else return critChance * PerkManager.GetFAbilityMulCritC(prefabID) + PerkManager.GetFAbilityModCritC(prefabID);
|
|
}
|
|
public float GetCritMultiplier(){
|
|
if(IsUAB()) return critMultiplier * PerkManager.GetUAbilityMulCritM(prefabID) + PerkManager.GetUAbilityModCritM(prefabID);
|
|
else return critMultiplier * PerkManager.GetFAbilityMulCritM(prefabID) + PerkManager.GetFAbilityModCritM(prefabID);
|
|
}
|
|
|
|
public int GetRangeMin(){
|
|
if(IsUAB()) return (int)(rangeMin * PerkManager.GetUAbilityMulRange(prefabID) + PerkManager.GetUAbilityModRange(prefabID));
|
|
else return rangeMin;
|
|
}
|
|
public int GetRange(){
|
|
if(IsUAB()) return (int)(range * PerkManager.GetUAbilityMulRange(prefabID) + PerkManager.GetUAbilityModRange(prefabID));
|
|
else return range;
|
|
}
|
|
public int GetAOE(){
|
|
if(IsUAB()) return (int)(aoeRange * PerkManager.GetUAbilityMulAOE(prefabID) + PerkManager.GetUAbilityModAOE(prefabID));
|
|
else return (int)(aoeRange * PerkManager.GetFAbilityMulAOE(prefabID) + PerkManager.GetFAbilityModAOE(prefabID));
|
|
}
|
|
|
|
public float GetEffHitChance(){
|
|
if(IsUAB()) return (int)(effHitChance * PerkManager.GetUAbilityMulEffHitC(prefabID) + PerkManager.GetUAbilityModEffHitC(prefabID));
|
|
else return (int)(effHitChance * PerkManager.GetFAbilityMulEffHitC(prefabID) + PerkManager.GetFAbilityModEffHitC(prefabID));
|
|
}
|
|
|
|
|
|
|
|
public Ability Clone(){
|
|
Ability clone=new Ability();
|
|
|
|
base.Clone(this, clone);
|
|
|
|
clone.type=type;
|
|
|
|
clone.targetType=targetType;
|
|
clone.requireTarget=requireTarget; clone.requireLos=requireLos;
|
|
clone.rangeMin=rangeMin; clone.range=range; clone.aoeRange=aoeRange;
|
|
clone.fov=fov;
|
|
|
|
|
|
clone.spawnUnitPrefab=spawnUnitPrefab;
|
|
|
|
clone.moveCost=moveCost; clone.attackCost=attackCost; clone.abilityCost=abilityCost;
|
|
clone.apCost=apCost; clone.endAllActionAfterUse=endAllActionAfterUse;
|
|
|
|
clone.duration=duration; clone.cooldown=cooldown; clone.useLimit=useLimit;
|
|
clone.impactDelay=impactDelay;
|
|
|
|
clone.impactType=impactType;
|
|
clone.hpModifierMin=hpModifierMin; clone.hpModifierMax=hpModifierMax;
|
|
clone.apModifierMin=apModifierMin; clone.apModifierMax=apModifierMax;
|
|
|
|
clone.damageType=damageType;
|
|
clone.attack=attack; clone.hitChance=hitChance;
|
|
clone.critChance=critChance; clone.critMultiplier=critMultiplier;
|
|
clone.factorInTargetStats=factorInTargetStats;
|
|
|
|
clone.effHitChance=effHitChance;
|
|
|
|
clone.effectIDList=new List<int>( effectIDList );
|
|
clone.clearAllEffect=clearAllEffect;
|
|
|
|
clone.switchFaction=switchFaction;
|
|
//clone.switchFacDuration=switchFacDuration;
|
|
clone.switchFacControllable=switchFacControllable;
|
|
|
|
clone.useAttackSequence=useAttackSequence;
|
|
clone.aimAtUnit=aimAtUnit;
|
|
clone.shootObject=shootObject;
|
|
|
|
clone.effectOnUse=effectOnUse!=null ? effectOnUse.Clone() : new VisualObject();
|
|
clone.effectOnHit=effectOnHit!=null ? effectOnHit.Clone() : new VisualObject();
|
|
|
|
return clone;
|
|
}
|
|
}
|
|
|
|
} |