ProjectCreep/Assets/TBTK/Scripts/Ability.cs

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;
}
}
}