using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; namespace TBTK { public class AbilityEditorWindow : TBEditorWindow { [MenuItem ("Tools/TBTK/AbilityEditor", false, 10)] static void OpenAbilityEditor () { Init(); } private static AbilityEditorWindow window; public static void Init (int prefabID=-1) { window = (AbilityEditorWindow)EditorWindow.GetWindow(typeof (AbilityEditorWindow), false, "AbilityEditor"); window.minSize=new Vector2(420, 300); TBE.Init(); window.InitLabel(); //if(prefabID>=0) window.selectID=AbilityUDB.GetPrefabIndex(prefabID); if(prefabID>=0){ window.selectID=AbilityUDB.GetPrefabIndex(prefabID); window.newSelectID=window.selectID; window.newSelectDelay=1; } window._SelectItem(); } private static string[] abTypeLabel; private static string[] abTypeTooltip; private static string[] tgtTypeLabel; private static string[] tgtTypeTooltip; private static string[] impactLabel; private static string[] impactTooltip; public void InitLabel(){ int enumLength = Enum.GetValues(typeof(Ability._AbilityType)).Length; abTypeLabel=new string[enumLength]; abTypeTooltip=new string[enumLength]; for(int i=0; i abilityList=editUnitAbility ? AbilityUDB.GetList() : AbilityFDB.GetList() ; Undo.RecordObject(this, "window"); Undo.RecordObject(EffectDB.GetDB(), "abilityDB"); if(GUI.Button(new Rect(Math.Max(260, window.position.width-120), 5, 100, 25), "Save")){ AbilityUDB.UpdateLabel(); AbilityFDB.UpdateLabel(); GUI.FocusControl(null); TBE.SetDirty(); } if(GUI.Button(new Rect(5, 5, 120, 25), "Create New")) Select(NewItem()); if(abilityList.Count>0 && GUI.Button(new Rect(130, 5, 100, 25), "Clone Selected")) Select(NewItem(selectID)); float startX=5; float startY=55; if(minimiseList){ if(GUI.Button(new Rect(startX, startY-20, 30, 18), ">>")) minimiseList=false; } else{ if(GUI.Button(new Rect(startX, startY-20, 30, 18), "<<")) minimiseList=true; } Vector2 v2=DrawAbilityList(startX, startY, abilityList); startX=v2.x+25; if(abilityList.Count==0) return; if(newSelectDelay>0){ newSelectDelay-=1; GUI.FocusControl(null); if(newSelectDelay==0) _SelectItem(); else Repaint(); } if(selectID>=abilityList.Count) return; Rect visibleRect=new Rect(startX, startY, window.position.width-startX, window.position.height-startY); Rect contentRect=new Rect(startX, startY, contentWidth, contentHeight); scrollPos = GUI.BeginScrollView(visibleRect, scrollPos, contentRect); v2=DrawAbilityConfigurator(startX, startY, abilityList[selectID]); contentWidth=v2.x-startX; contentHeight=v2.y-55; GUI.EndScrollView(); if(GUI.changed) TBE.SetDirty(); } private bool foldSetting=true; private bool foldCost=true; private bool foldVisual=true; private bool foldAttributesOnImpact=true; private string txt=""; private Vector2 DrawAbilityConfigurator(float startX, float startY, Ability item){ float maxX=startX; startY=TBE.DrawBasicInfo(startX, startY, item); int abType=(int)item.type; contL=TBE.SetupContL(abTypeLabel, abTypeTooltip); int initialType=abType; TBE.Label(startX, startY+=spaceY, width, height, "Effect Type:", "", TBE.headerS); abType = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), new GUIContent(""), abType, contL); item.type=(Ability._AbilityType)abType; if(!editUnitAbility){ bool revert=false; if(item.type==Ability._AbilityType.Teleport) revert=true; if(item.type==Ability._AbilityType.Charge) revert=true; if(item.type==Ability._AbilityType.Line) revert=true; if(item.type==Ability._AbilityType.Cone) revert=true; if(revert){ Debug.LogWarning(item.type+" type is not supported by faction ability"); item.type=(Ability._AbilityType)initialType; } } startY+=spaceY*0.5f; foldSetting=EditorGUI.Foldout(new Rect(startX, startY+=spaceY, spaceX, height), foldSetting, "Settings", TBE.foldoutS); if(foldSetting){ startX+=10; TBE.Label(startX, startY+=spaceY, width, height, "Cooldown:", "How many turn before the ability can be used again once it's used"); item.cooldown=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.cooldown); TBE.Label(startX, startY+=spaceY, width, height, "Use Limit:", "How many time the ability can be used in a single game"); GUI.color=(item.useLimit>0) ? Color.white : Color.grey ; item.useLimit=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.useLimit); GUI.color=Color.white; //startY+=spaceY*0.5f; if(editUnitAbility){ foldCost=EditorGUI.Foldout(new Rect(startX, startY+=spaceY, spaceX, height), foldCost, "Activation Cost"); if(foldCost){ startX+=10; TBE.Label(startX, startY+=spaceY, width, height, "move Cost:", "The cost of using the ability that counts towards movement limit "); item.moveCost=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.moveCost); TBE.Label(startX, startY+=spaceY, width, height, "attack Cost:", "The cost of using the ability that counts towards attack limit"); item.attackCost=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.attackCost); TBE.Label(startX, startY+=spaceY, width, height, "Ability Cost:", "The cost of using the ability that counts towards ability limit"); item.abilityCost=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.abilityCost); TBE.Label(startX, startY+=spaceY, width, height, "AP Cost:", "The action-point cost of using the ability"); item.apCost=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.apCost); TBE.Label(startX, startY+=spaceY, width, height, "End All Action:", "Check to end all action available to the unit once the ability is used"); item.endAllActionAfterUse=EditorGUI.Toggle(new Rect(startX+spaceX, startY, widthS, height), item.endAllActionAfterUse); startX-=10; } } startY+=spaceY*0.5f; Ability._TargetType cachedTgtType=item.targetType; if(!editUnitAbility && !item.requireTarget){ if(cachedTgtType==Ability._TargetType.AllNode || cachedTgtType==Ability._TargetType.EmptyNode){ item.targetType=Ability._TargetType.AllUnit; cachedTgtType=Ability._TargetType.AllUnit; } } int tgtType=(int)item.targetType; contL=TBE.SetupContL(tgtTypeLabel, tgtTypeTooltip); TBE.Label(startX, startY+=spaceY, width, height, "Target Type:", "", TBE.headerS); tgtType = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), new GUIContent(""), tgtType, contL); item.targetType=(Ability._TargetType)tgtType; if(!editUnitAbility && !item.requireTarget){ if(item.targetType==Ability._TargetType.AllNode || cachedTgtType==Ability._TargetType.EmptyNode){ Debug.Log("target type not supported for faction ability that doesn't require target input"); item.targetType=cachedTgtType; } } TBE.Label(startX, startY+=spaceY, width, height, "Require Target:", "Check if the ability will require the player to actively select a target\nIf left uncheck, as unity-ability the ability will target the source unit, as faction ability the ability will target will valid target on the grid"); item.requireTarget=EditorGUI.Toggle(new Rect(startX+spaceX, startY, widthS, height), item.requireTarget); if(editUnitAbility){ TBE.Label(startX, startY+=spaceY, width, height, "Require LOS:", "Check if the ability require the target to be in line-of-sight"); if(!item.requireTarget) TBE.Label(startX+spaceX, startY, widthS, height, "n/a"); else item.requireLos=EditorGUI.Toggle(new Rect(startX+spaceX, startY, widthS, height), item.requireLos); } if(editUnitAbility){ TBE.Label(startX, startY+=spaceY, width, height, "Range (min/max):", "The effective range of the ability"); if(!item.requireTarget) TBE.Label(startX+spaceX, startY, widthS, height, "n/a"); else{ GUI.color=item.rangeMin>1 ? Color.white : Color.grey ; item.rangeMin=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.rangeMin); GUI.color=Color.white; item.range=EditorGUI.DelayedIntField(new Rect(startX+spaceX+widthS, startY, widthS, height), item.range); } } bool typeSupportAOE=!(item.type==Ability._AbilityType.Teleport || item.type==Ability._AbilityType.SpawnUnit || item.type==Ability._AbilityType.Cone); TBE.Label(startX, startY+=spaceY, width, height, "AOE Range:", "The effective area-of-effect (AOE) range of the ability at the target node"); if((!editUnitAbility && !item.requireTarget) || !typeSupportAOE) TBE.Label(startX+spaceX, startY, widthS, height, "n/a", ""); else{ GUI.color=(item.aoeRange>0) ? Color.white : Color.grey ; item.aoeRange=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.aoeRange); GUI.color= Color.white; } if(item.type==Ability._AbilityType.Cone){ TBE.Label(startX, startY+=spaceY, width, height, "FOV:", "The Field-Of-View of the ability in angle. Determine how wide the cone is"); item.fov=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.fov); } startY+=spaceY*0.5f; if(editUnitAbility){ TBE.Label(startX, startY+=spaceY, width, height, "Use Attack Sequence:", "Check to have the unit using the ability runs a standard attack sequence at the target (the unit will aim towards the target and fire a shoot-object)"); if(!item.requireTarget) TBE.Label(startX+spaceX+10, startY, widthS, height, "n/a"); else item.useAttackSequence=EditorGUI.Toggle(new Rect(startX+spaceX+10, startY, widthS, height), item.useAttackSequence); TBE.Label(startX, startY+=spaceY, width, height, "Aim At Target Unit:", "Check to have the unit aim at the unit (if there's one) when using the ability\nOtherwise the ability will aim at the node"); if(!item.requireTarget || !item.useAttackSequence || item.type==Ability._AbilityType.Line || item.type==Ability._AbilityType.Cone) TBE.Label(startX+spaceX+10, startY, widthS, height, "-"); else item.aimAtUnit=EditorGUI.Toggle(new Rect(startX+spaceX+10, startY, widthS, height), item.aimAtUnit); TBE.Label(startX, startY+=spaceY, width, height, "Shoot Object:", "OPTIONAL: The alternate shoot-object to use for the ability\nIf left unassgined, the unit will use its default shoot-object"); if(!item.requireTarget || !item.useAttackSequence) TBE.Label(startX+spaceX+10, startY, width, height, "-"); else item.shootObject=(ShootObject)EditorGUI.ObjectField(new Rect(startX+spaceX+10, startY, width-10, height), item.shootObject, typeof(ShootObject), true); //startY+=10; } startX-=10; } startY+=spaceY*0.5f; foldAttributesOnImpact=EditorGUI.Foldout(new Rect(startX, startY+=spaceY, spaceX, height), foldAttributesOnImpact, "Attributes On Impact", TBE.foldoutS); if(foldAttributesOnImpact){ startX+=10; TBE.Label(startX, startY+=spaceY, width, height, "Impact Delay:", "The delay in second before any effect is actually being applied to the target\nThis is for any visual effect (if there's any) to play out"); item.impactDelay=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.impactDelay); if(item.type==Ability._AbilityType.None){ } if(item.type==Ability._AbilityType.SpawnUnit){ item.requireTarget=true; item.targetType=Ability._TargetType.EmptyNode; item.useAttackSequence=false; //GUIStyle styleSO=item.spawnUnitPrefab==null ? TBE.conflictS : null; //TBE.Label(startX, startY+=spaceY, width, height, "Spawn Unit:", "", styleSO); //item.spawnUnitPrefab=(Unit)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), item.spawnUnitPrefab, typeof(Unit), true); TBE.Label(startX, startY+=spaceY, width, height, "Unit To Spawn:", "The unit prefab to spawn when the ability is used"); int unitIdx=UnitDB.GetPrefabIndex(item.spawnUnitPrefab); unitIdx = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), unitIdx, UnitDB.label); item.spawnUnitPrefab=unitIdx>=0 ? UnitDB.GetItem(unitIdx) : null; if(GUI.Button(new Rect(startX+spaceX+width+3, startY, height, height), "-")) item.spawnUnitPrefab=null; } if(item.type==Ability._AbilityType.Teleport){ item.requireTarget=true; item.targetType=Ability._TargetType.EmptyNode; item.useAttackSequence=false; } if(item.type==Ability._AbilityType.ScanFogOfWar){ item.targetType=Ability._TargetType.AllNode; TBE.Label(startX, startY+=spaceY, width, height, "Duration:", "The number of turn in which the fog-of-war will be revealed"); if(!item.switchFaction) TBE.Label(startX+spaceX, startY, widthS, height, "-", ""); else item.duration=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.duration); } if(item.type==Ability._AbilityType.Generic || item.type==Ability._AbilityType.Charge || item.type==Ability._AbilityType.Line || item.type==Ability._AbilityType.Cone){ startY+=spaceY*0.5f; if(item.type==Ability._AbilityType.Charge){ item.requireTarget=true; } else if(item.type==Ability._AbilityType.Line || item.type==Ability._AbilityType.Cone){ item.requireTarget=true; } int impactType=(int)item.impactType; contL=TBE.SetupContL(impactLabel, impactTooltip); TBE.Label(startX, startY+=spaceY, width, height, "Impact Type:", ""); impactType = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), new GUIContent(""), impactType, contL); item.impactType=(Ability._ImpactType)impactType; if(item.impactType!=Ability._ImpactType.None){ TBE.Label(startX, startY+=spaceY, width, height, "Damage Type:", ""); if(item.impactType==Ability._ImpactType.Negative){ item.damageType = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), item.damageType, TBE.GetDamageLabel()); } else TBE.Label(startX+spaceX, startY, width, height, "-"); TBE.Label(startX, startY+=spaceY, width, height, "HP Modifier Min/Max:", "The immediate change applied to the target's hit-point upon the ability impact/hit"); GUI.color=(item.hpModifierMin!=0 || item.hpModifierMax!=0) ? Color.white : Color.grey ; item.hpModifierMin=EditorGUI.DelayedIntField(new Rect(startX+spaceX+3, startY, widthS, height), item.hpModifierMin); item.hpModifierMax=EditorGUI.DelayedIntField(new Rect(startX+spaceX+widthS+5, startY, widthS, height), item.hpModifierMax); GUI.color=Color.white; TBE.Label(startX, startY+=spaceY, width, height, "AP Modifier Min/Max:", "The immediate change applied to the target's action-point upon the ability impact/hit"); GUI.color=(item.apModifierMin!=0 || item.apModifierMax!=0) ? Color.white : Color.grey ; item.apModifierMin=EditorGUI.DelayedIntField(new Rect(startX+spaceX+3, startY, widthS, height), item.apModifierMin); item.apModifierMax=EditorGUI.DelayedIntField(new Rect(startX+spaceX+widthS+5, startY, widthS, height), item.apModifierMax); GUI.color=Color.white; startY+=spaceY*0.5f; if(item.impactType==Ability._ImpactType.Negative){ TBE.Label(startX, startY+=spaceY, width, height, "Factor Target Stats:", "Check to factor in target stats (defend, dodge, etc.) to calculate the damage"); item.factorInTargetStats=EditorGUI.Toggle(new Rect(startX+spaceX, startY, widthS, height), item.factorInTargetStats); TBE.Label(startX, startY+=spaceY, width, height, " - Attack:", "The attack value of the ability\nUsed along with the target's defense to modify the effective damage"+TBE.AttackTT()); if(!item.factorInTargetStats) TBE.Label(startX+spaceX, startY, widthS, height, "-"); else item.attack=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.attack); TBE.Label(startX, startY+=spaceY, width, height, " - Hit Chance:", "The chance for the ability to hit\nThis will be negate by the target's dodge chance"+TBE.ChanceTT()); if(!item.factorInTargetStats) TBE.Label(startX+spaceX, startY, widthS, height, "-"); else item.hitChance=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.hitChance); TBE.Label(startX, startY+=spaceY, width, height, " - Crit. Chance:", "The chance for the ability to score a critical hit\nThis will be negate by the target's critical reduction chance"+TBE.ChanceTT()); if(!item.factorInTargetStats) TBE.Label(startX+spaceX, startY, widthS, height, "-"); else item.critChance=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.critChance); TBE.Label(startX, startY+=spaceY, width, height, " - Crit. Multiplier:", "The damage multiplier to use when the ability score a critical hit"+TBE.ChanceTT()); if(!item.factorInTargetStats) TBE.Label(startX+spaceX, startY, widthS, height, "-"); else item.critMultiplier=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.critMultiplier); } } startY+=spaceY*0.5f; TBE.Label(startX, startY+=spaceY, width, height, "Effect Hit Chance:", "The chance of any effect item being applied\nApplies to ClearAllEffect and SwitchFaction too"+TBE.ChanceTT()); GUI.color=(item.effHitChance>0) ? Color.white : Color.grey ; item.effHitChance=EditorGUI.DelayedFloatField(new Rect(startX+spaceX, startY, widthS, height), item.effHitChance); TBE.Label(startX, startY+=spaceY, width, height, "Effect On Hit:", "The effect to be applied to the target when the ability hit"); for(int i=0; i=0) item.effectIDList[i]=EffectDB.GetItemID(effIdx); if(GUI.Button(new Rect(startX+spaceX+width+3, startY, height, height), "-")) item.effectIDList.RemoveAt(i); startY+=spaceY; } int newEffID=-1; newEffID = EditorGUI.Popup(new Rect(startX+spaceX, startY, width, height), newEffID, EffectDB.label); if(newEffID>=0) newEffID=EffectDB.GetItemID(newEffID); if(newEffID>=0 && !item.effectIDList.Contains(newEffID)) item.effectIDList.Add(newEffID); TBE.Label(startX, startY+=spaceY, width, height, "Clear All Effect:", "Check to have the ability clear all the active effects on the target"); item.clearAllEffect=EditorGUI.Toggle(new Rect(startX+spaceX, startY, widthS, height), item.clearAllEffect); startY+=spaceY*0.5f; TBE.Label(startX, startY+=spaceY, width, height, "Switch Faction:", "Check to have the ability switch the target faction"); item.switchFaction=EditorGUI.Toggle(new Rect(startX+spaceX, startY, widthS, height), item.switchFaction); TBE.Label(startX, startY+=spaceY, width, height, " - Duration:", "The duration in turn in which the target unit's faction is switched"); if(!item.switchFaction) TBE.Label(startX+spaceX, startY, widthS, height, "n/a", ""); else item.duration=EditorGUI.DelayedIntField(new Rect(startX+spaceX, startY, widthS, height), item.duration); TBE.Label(startX, startY+=spaceY, width, height, " - Controllable:", "Check to let the player take control of the unit when the target is switched"); if(!item.switchFaction) TBE.Label(startX+spaceX, startY, widthS, height, "n/a", ""); else item.switchFacControllable=EditorGUI.Toggle(new Rect(startX+spaceX, startY, widthS, height), item.switchFacControllable); GUI.color=Color.white; } startX-=10; } startY+=spaceY*0.5f; foldVisual=EditorGUI.Foldout(new Rect(startX, startY+=spaceY, spaceX, height), foldVisual, "Visual & Audio Setting", TBE.foldoutS); if(foldVisual){ startX+=10; if(editUnitAbility){ txt="OPTIONAL: The effect object to spawn when the ability is activated\nYou can also add custom script on this object to have your own custom ability effect"; startY=DrawVisualObject(startX, startY+=spaceY, item.effectOnUse, "Effect On Use:", txt); } txt="OPTIONAL: The effect object to spawn when on each individual target of the ability\nYou can also add custom script on this object to have your own custom ability effect"; startY=DrawVisualObject(startX, startY+=spaceY, item.effectOnHit, "Effect On Hit:", txt); TBE.Label(startX, startY+=spaceY*1.5f, width, height, "Activation Sound:", "OPTIONAL: Audio clip to play when the ability is activated"); item.activateSound=(AudioClip)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), item.activateSound, typeof(AudioClip), true); startX-=10; } startY+=spaceY*2; GUIStyle style=new GUIStyle("TextArea"); style.wordWrap=true; cont=new GUIContent("Ability description (for runtime and editor): ", ""); EditorGUI.LabelField(new Rect(startX, startY, 400, height), cont); item.desp=EditorGUI.DelayedTextField(new Rect(startX, startY+spaceY-3, 270, 150), item.desp, style); return new Vector2(maxX, startY+170); } protected Vector2 DrawAbilityList(float startX, float startY, List abilityList){ List list=new List(); for(int i=0; i=0) item=AbilityUDB.GetList()[idx].Clone(); item.prefabID=TBE.GenerateNewID(AbilityUDB.GetPrefabIDList()); AbilityUDB.GetList().Add(item); AbilityUDB.UpdateLabel(); return AbilityUDB.GetList().Count-1; } else{ if(idx>=0) item=AbilityFDB.GetList()[idx].Clone(); item.prefabID=TBE.GenerateNewID(AbilityFDB.GetPrefabIDList()); AbilityFDB.GetList().Add(item); AbilityFDB.UpdateLabel(); return AbilityFDB.GetList().Count-1; } } protected override void DeleteItem(){ if(editUnitAbility){ AbilityUDB.GetList().RemoveAt(deleteID); AbilityUDB.UpdateLabel(); } else{ AbilityFDB.GetList().RemoveAt(deleteID); AbilityFDB.UpdateLabel(); } } protected override void SelectItem(){ } private void _SelectItem(){ selectID=newSelectID; if(editUnitAbility){ if(AbilityUDB.GetList().Count<=0) return; selectID=Mathf.Clamp(selectID, 0, AbilityUDB.GetList().Count-1); } else{ if(AbilityFDB.GetList().Count<=0) return; selectID=Mathf.Clamp(selectID, 0, AbilityFDB.GetList().Count-1); } Repaint(); } //~ protected override void SelectItem(){ SelectItem(selectID); } //~ private void SelectItem(int newID){ //~ selectID=newID; //~ if(editUnitAbility){ //~ if(AbilityUDB.GetList().Count<=0) return; //~ selectID=Mathf.Clamp(selectID, 0, AbilityUDB.GetList().Count-1); //~ } //~ else{ //~ if(AbilityFDB.GetList().Count<=0) return; //~ selectID=Mathf.Clamp(selectID, 0, AbilityFDB.GetList().Count-1); //~ } //~ } protected override void ShiftItemUp(){ if(selectID>0) ShiftItem(-1); } protected override void ShiftItemDown(){ if(selectID<(editUnitAbility ? AbilityUDB.GetList().Count : AbilityFDB.GetList().Count)-1) ShiftItem(1); } private void ShiftItem(int dir){ if(editUnitAbility){ Ability item=AbilityUDB.GetList()[selectID]; AbilityUDB.GetList()[selectID]=AbilityUDB.GetList()[selectID+dir]; AbilityUDB.GetList()[selectID+dir]=item; } else{ Ability item=AbilityFDB.GetList()[selectID]; AbilityFDB.GetList()[selectID]=AbilityFDB.GetList()[selectID+dir]; AbilityFDB.GetList()[selectID+dir]=item; } selectID+=dir; } } }