public Ability Clone(){ Ability ab=new Ability(); ab.ID=ID; ab.name=name; ab.icon=icon; ab.cost=cost; ab.cooldown=cooldown; ab.currentCD=currentCD; ab.requireTargetSelection=requireTargetSelection; ab.singleUnitTargeting=singleUnitTargeting; ab.targetType=targetType; ab.effectObj=effectObj; ab.maxUseCount=maxUseCount; ab.usedCount=usedCount; ab.indicator=indicator; ab.autoScaleIndicator=autoScaleIndicator; ab.useDefaultEffect=useDefaultEffect; for(int i=0; i<effectList.Count; i++) ab.effectList.Add(effectList[i].Clone()); ab.effect=effect.Clone(); ab.aoeRadius=aoeRadius; ab.effectDelay=effectDelay; ab.useCustomDesp=useCustomDesp; ab.desp=desp; return ab; }
public static int GetAbilityIndex(Ability ab) { for(int i=0; i<instance.abilityList.Count; i++){ if(ab==instance.abilityList[i]) return i; } return -1; }
public static void OnAbilityActivated(Ability ability){ if(onAbilityActivatedE!=null) onAbilityActivatedE(ability); }
public static void OnNewAbility(Ability ability){ if(onNewAbilityE!=null) onNewAbilityE(ability); }
Vector2 DrawAbilityConfigurator(float startX, float startY, Ability ability) { //float cachedX=startX; float cachedY=startY; EditorUtilities.DrawSprite(new Rect(startX, startY, 60, 60), ability.icon); startX+=65; cont=new GUIContent("Name:", "The ability name to be displayed in game"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY/2, width, height), cont); ability.name=EditorGUI.TextField(new Rect(startX+spaceX-65, startY, width-5, height), ability.name); cont=new GUIContent("Icon:", "The ability icon to be displayed in game, must be a sprite"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.icon=(Sprite)EditorGUI.ObjectField(new Rect(startX+spaceX-65, startY, width-5, height), ability.icon, typeof(Sprite), false); cont=new GUIContent("Disable in AbilityManager:", "When checked, the ability won't appear on AbilityManager list and thus can't be access from the get go\nThis is to mark ability that can only be unlocked from perk"); EditorGUI.LabelField(new Rect(startX+235, startY, width+10, height), cont); ability.disableInAbilityManager=EditorGUI.Toggle(new Rect(startX+390, startY, 185, height), ability.disableInAbilityManager); startX-=65; startY+=10+spaceY/2; cachedY=startY+spaceY; cont=new GUIContent("Cost:", "The energy cost to use the ability"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.cost=EditorGUI.IntField(new Rect(startX+spaceX, startY, 40, height), ability.cost); cont=new GUIContent("Cooldown:", "The cooldown duration of the ability. Once used, the ability cannot be used again until the cooldown duration has passed"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.cooldown=EditorGUI.FloatField(new Rect(startX+spaceX, startY, 40, height), ability.cooldown); cont=new GUIContent("Require Targeting:", "Check if ability need a specific position or unit as target. When checked, the user will need to select a position/unit before the ability can be cast. Otherwise the ability be cast without a target position/unit. If ability uses default effects and this is unchecked, the effect will be apply to all unit in the game"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.requireTargetSelection=EditorGUI.Toggle(new Rect(startX+spaceX, startY, 40, height), ability.requireTargetSelection); if(ability.requireTargetSelection){ cont=new GUIContent(" - Single Unit Only:", "Check if the ability require a specific unit as a target. Otherwise the ability can be cast anywhere without a specific target"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.singleUnitTargeting=EditorGUI.Toggle(new Rect(startX+spaceX, startY, 40, height), ability.singleUnitTargeting); if(ability.singleUnitTargeting){ //~ cont=new GUIContent(" - Target Friendly:", "Check if the ability is meant to target firendly unit. Otherwise it will target"); //~ EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); //~ ability.targetType=EditorGUI.Toggle(new Rect(startX+spaceX, startY, 40, height), ability.targetType); int targetType=(int)ability.targetType; cont=new GUIContent(" - Target :", "Determine which type of unit the tower can target. Hostile for hostile unit. Friendly for friendly unit. Hybrid for both."); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); contL=new GUIContent[targetTypeLabel.Length]; for(int i=0; i<contL.Length; i++) contL[i]=new GUIContent(targetTypeLabel[i], targetTypeTooltip[i]); targetType = EditorGUI.Popup(new Rect(startX+spaceX, startY, width-20, height), new GUIContent(""), targetType, contL); ability.targetType=(Ability._TargetType)targetType; } cont=new GUIContent(" - Indicator:", "(Optional) The cursor indicator that used to indicate the ability target position during target selection phase for the ability. If left unassigned, the default indicator specified in the AbilityManager will be used instead"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.indicator=(Transform)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), ability.indicator, typeof(Transform), false); if(ability.indicator==null){ cont=new GUIContent(" - Scale Indicator:", "Automatically scale the indicator size to match the aoeRadius of the ability, or a unit width in case of a single unit targeting. Only applicable if ability is using default effects"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.autoScaleIndicator=EditorGUI.Toggle(new Rect(startX+spaceX, startY, 40, height), ability.autoScaleIndicator); } else{ cont=new GUIContent(" - Scale Indicator:", "Automatically scale the indicator size to match the aoeRadius of the ability. Only applicable if ability is using default effects"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); EditorGUI.LabelField(new Rect(startX+spaceX, startY, width, height), "not applicable"); } } else{ cont=new GUIContent(" - Targets Unit:", "Check if the unit is immuned to critical hit"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); EditorGUI.LabelField(new Rect(startX+spaceX, startY, width, height), "not applicable"); cont=new GUIContent(" - Indicator:", "(Optional) The cursor indicator that used to indicate the ability target position during target selection phase for the ability. If left unassigned, the default indicator specified in the AbilityManager will be used instead"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); EditorGUI.LabelField(new Rect(startX+spaceX, startY, width, height), "not applicable"); cont=new GUIContent(" - Scale Indicator:", "Automatically scale the indicator size to match the aoeRadius of the ability. Only applicable if ability is using default effects"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); EditorGUI.LabelField(new Rect(startX+spaceX, startY, width, height), "not applicable"); } cont=new GUIContent("Max Use Count:", "The maximum amount which the ability can be used in a level. Indicate unlimited usage when set to <0"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.maxUseCount=EditorGUI.IntField(new Rect(startX+spaceX, startY, 40, height), ability.maxUseCount); cont=new GUIContent("Effect Object:", "The effect object spawned at the selected position when the ability is used. This object can contain custom script to do custom effect for the ability"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.effectObj=(GameObject)EditorGUI.ObjectField(new Rect(startX+spaceX, startY, width, height), ability.effectObj, typeof(GameObject), false); cont=new GUIContent("Use Default Effect:", "Check to use default built in ability effects. Alternative you can script your custom effect and have it spawn as the ability's EffectObject"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.useDefaultEffect=EditorGUI.Toggle(new Rect(startX+spaceX, startY, 40, height), ability.useDefaultEffect); if(ability.useDefaultEffect){ if(ability.requireTargetSelection){ cont=new GUIContent(" - AOE Radius:", "The Area of Effective radius of the effect. Only target within the radius of the target position will be affected by the ability"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.aoeRadius=EditorGUI.FloatField(new Rect(startX+spaceX, startY, 40, height), ability.aoeRadius); } else{ cont=new GUIContent(" - AOE Radius:", "The Area of Effective radius of the effect. Only target within the radius of the target position will be affected by the ability"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); EditorGUI.LabelField(new Rect(startX+spaceX, startY, width, height), "not applicable"); } cont=new GUIContent(" - Effect Delay:", "The delay in second before the effect actually hit after the ability is cast. This is mostly used to sync-up the visual effect with the effects."); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.effectDelay=EditorGUI.FloatField(new Rect(startX+spaceX, startY, 40, height), ability.effectDelay); } else{ cont=new GUIContent(" - AOE Radius:", "The Area of Effective radius of the effect. Only target within the radius of the target position will be affected by the ability"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); EditorGUI.LabelField(new Rect(startX+spaceX, startY, width, height), "not applicable"); cont=new GUIContent(" - Effect Delay:", "The delay in second before the effect actually hit after the ability is cast. This is mostly used to sync-up the visual effect with the effects."); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); EditorGUI.LabelField(new Rect(startX+spaceX, startY, width, height), "not applicable"); } startY+=10; cont=new GUIContent("Custom Description:", "Check to use use custom description. If not, the default one (generated based on the effect) will be used"); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, width, height), cont); ability.useCustomDesp=EditorGUI.Toggle(new Rect(startX+spaceX, startY, 40, height), ability.useCustomDesp); if(ability.useCustomDesp){ GUIStyle style=new GUIStyle("TextArea"); style.wordWrap=true; cont=new GUIContent("Perk description (to be used in runtime): ", ""); EditorGUI.LabelField(new Rect(startX, startY+=spaceY, 400, 20), cont); ability.desp=EditorGUI.TextArea(new Rect(startX, startY+spaceY-3, 270, 150), ability.desp, style); } if(ability.useDefaultEffect){ startY=cachedY; startX+=300; Vector2 v2=DrawAbilityEffect(ability.effect, startX, startY); startX-=300; startY=v2.y; } float contWidth=spaceX+width; return new Vector2(contWidth, startY); }
void OnGUI() { if(window==null) Init(); if(GUI.Button(new Rect(window.position.width-120, 5, 100, 25), "Save")){ EditorUtility.SetDirty(prefab); } if(GUI.Button(new Rect(5, 5, 120, 25), "Create New")){ Ability newAbility=new Ability(); int ID=GenerateNewID(); abilityIDList.Add(ID); newAbility.ID=ID; newAbility.name="Ability "+ID; abilityList.Add(newAbility); SelectAbility(abilityList.Count-1); GUI.changed=true; } if(abilityList.Count>0 && GUI.Button(new Rect(130, 5, 100, 25), "Clone Selected")){ Ability newAbility=abilityList[selectID].Clone(); int ID=GenerateNewID(); abilityIDList.Add(ID); newAbility.ID=ID; newAbility.name+=" (Clone)"; abilityList.Insert(selectID+1, newAbility); SelectAbility(selectID+1); GUI.changed=true; } 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); startX=v2.x+25; if(abilityList.Count==0) return; Rect visibleRect=new Rect(startX, startY, window.position.width-startX-10, window.position.height-startY-5); Rect contentRect=new Rect(startX, startY, contentWidth-startY, contentHeight); //~ GUI.color=new Color(.8f, .8f, .8f, 1f); //~ GUI.Box(visibleRect, ""); //~ GUI.color=Color.white; scrollPos2 = GUI.BeginScrollView(visibleRect, scrollPos2, contentRect); //float cachedX=startX; v2=DrawAbilityConfigurator(startX, startY, abilityList[selectID]); //contentWidth=v2.x+50; contentHeight=v2.y; GUI.EndScrollView(); contentWidth=startX+280; if(GUI.changed) EditorUtility.SetDirty(prefab); }
public void _AddNewAbility(Ability ab){ for(int i=0; i<abilityList.Count; i++){ if(ab.ID==abilityList[i].ID) return; } abilityList.Add(ab.Clone()); if(onAddNewAbilityE!=null) onAddNewAbilityE(ab); }
//called in every frame, execute if there's an ability is selected and pending target selection //use only mouse input atm. void SelectAbilityTarget() { if (selectedAbilityID < 0) { return; } //only cast on terrain and platform LayerMask mask = 1 << LayerManager.LayerPlatform(); int terrainLayer = LayerManager.LayerTerrain(); if (terrainLayer >= 0) { mask |= 1 << terrainLayer; } Ability ability = abilityList[selectedAbilityID]; if (ability.singleUnitTargeting) { if (ability.targetType == Ability._TargetType.Hybrid) { mask |= 1 << LayerManager.LayerTower(); mask |= 1 << LayerManager.LayerCreep(); } else if (ability.targetType == Ability._TargetType.Friendly) { mask |= 1 << LayerManager.LayerTower(); } else if (ability.targetType == Ability._TargetType.Hostile) { mask |= 1 << LayerManager.LayerCreep(); } } #if UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 Unit targetUnit = null; if (Input.touchCount >= 1) { Camera mainCam = Camera.main; if (mainCam != null) { Ray ray = mainCam.ScreenPointToRay(Input.touches[0].position); RaycastHit hit; if (Physics.Raycast(ray, out hit, Mathf.Infinity, mask)) { currentIndicator.position = hit.point + new Vector3(0, 0.1f, 0); validTarget = true; if (ability.singleUnitTargeting) { targetUnit = hit.transform.GetComponent <Unit>(); if (targetUnit != null) { currentIndicator.position = targetUnit.thisT.position; } else { validTarget = false; } } } else { validTarget = false; } } } else { if (validTarget) { ActivateAbility(ability, currentIndicator.position, targetUnit); } else { GameControl.DisplayMessage("Invalid target for ability"); } ClearSelectedAbility(); } #else Camera mainCam = Camera.main; if (mainCam != null) { Ray ray = mainCam.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, Mathf.Infinity, mask)) { currentIndicator.position = hit.point + new Vector3(0, 0.1f, 0); Unit targetUnit = null; validTarget = true; if (ability.singleUnitTargeting) { targetUnit = hit.transform.GetComponent <Unit>(); if (targetUnit != null) { currentIndicator.position = targetUnit.thisT.position; } else { validTarget = false; } } if (Input.GetMouseButtonDown(0)) { if (validTarget) { ActivateAbility(ability, currentIndicator.position, targetUnit); ClearSelectedAbility(); } else { GameControl.DisplayMessage("Invalid target for ability"); } } } } if (Input.GetMouseButtonDown(1)) { ClearSelectedAbility(); } #endif }
public static void AddNewAbility(Ability newAbility){ instance._AddNewAbility(newAbility); }
//called from ActivateAbility, cast the ability, visual effect and actual effect goes here public void CastAbility(Ability ab, Vector3 pos, Unit unit=null){ if(ab.effectObj!=null){ ObjectPoolManager.Spawn(ab.effectObj, pos, Quaternion.identity); } if(ab.useDefaultEffect){ StartCoroutine(ApplyAbilityEffect(ab, pos, unit)); } }
//called when an ability is fired, reduce the energy, start the cooldown and what not public void ActivateAbility(Ability ab, Vector3 pos=default(Vector3), Unit unit=null){ ab.usedCount+=1; energy-=ab.GetCost(); StartCoroutine(ab.CooldownRoutine()); CastAbility(ab, pos, unit); if(onAbilityActivatedE!=null) onAbilityActivatedE(ab); }
void OnAbilityActivated(Ability ab){ if(abilityActivatedSound!=null) _PlaySound(abilityActivatedSound); }
void OnNewAbility(Ability newAbility){ Debug.Log("new abiility"); UIAbilityButton.AddNewAbility(newAbility); }
//apply the ability effect, damage, stun, buff and so on IEnumerator ApplyAbilityEffect(Ability ab, Vector3 pos, Unit tgtUnit = null) { yield return(new WaitForSeconds(ab.effectDelay)); LayerMask mask1 = 1 << LayerManager.LayerTower(); LayerMask mask2 = 1 << LayerManager.LayerCreep(); LayerMask mask3 = 1 << LayerManager.LayerCreepF(); LayerMask mask = mask1 | mask2 | mask3; List <Unit> creepList = new List <Unit>(); List <Unit> towerList = new List <Unit>(); if (tgtUnit == null) { float radius = ab.requireTargetSelection ? ab.GetAOERadius() : Mathf.Infinity; Collider[] cols = Physics.OverlapSphere(pos, radius, mask); if (cols.Length > 0) { for (int i = 0; i < cols.Length; i++) { Unit unit = cols[i].gameObject.GetComponent <Unit>(); if (unit.unitC != null) { creepList.Add(unit.unitC); } if (unit.unitT != null) { towerList.Add(unit.unitT); } } } } else { creepList.Add(tgtUnit); towerList.Add(tgtUnit); } AbilityEffect eff = ab.GetActiveEffect(); for (int n = 0; n < creepList.Count; n++) { if (eff.damageMax > 0) { creepList[n].ApplyDamage(Random.Range(eff.damageMin, eff.damageMax)); } else if (eff.stunChance > 0 && eff.duration > 0) { if (Random.Range(0f, 1f) < eff.stunChance) { creepList[n].ApplyStun(eff.duration); } } else if (eff.slow.IsValid()) { creepList[n].ApplySlow(eff.slow); } else if (eff.dot.GetTotalDamage() > 0) { creepList[n].ApplyDot(eff.dot); } } for (int n = 0; n < towerList.Count; n++) { if (eff.duration > 0) { if (eff.damageBuff > 0) { towerList[n].ABBuffDamage(eff.damageBuff, eff.duration); } else if (eff.rangeBuff > 0) { towerList[n].ABBuffRange(eff.rangeBuff, eff.duration); } else if (eff.cooldownBuff > 0) { towerList[n].ABBuffCooldown(eff.cooldownBuff, eff.duration); } } else if (eff.HPGainMax > 0) { towerList[n].RestoreHP(Random.Range(eff.HPGainMin, eff.HPGainMax)); } } }
void OnAbilityActivated(Ability ab){ StartCoroutine(ButtonCDROutine(ab)); }
//apply the ability effect, damage, stun, buff and so on IEnumerator ApplyAbilityEffect(Ability ab, Vector3 pos, Unit tgtUnit=null){ yield return new WaitForSeconds(ab.effectDelay); LayerMask mask1=1<<LayerManager.LayerTower(); LayerMask mask2=1<<LayerManager.LayerCreep(); LayerMask mask3=1<<LayerManager.LayerCreepF(); LayerMask mask=mask1 | mask2 | mask3; List<Unit> creepList=new List<Unit>(); List<Unit> towerList=new List<Unit>(); if(tgtUnit==null){ float radius=ab.requireTargetSelection ? ab.GetAOERadius() : Mathf.Infinity; Collider[] cols=Physics.OverlapSphere(pos, radius, mask); if(cols.Length>0){ for(int i=0; i<cols.Length; i++){ Unit unit=cols[i].gameObject.GetComponent<Unit>(); if(unit.unitC!=null) creepList.Add(unit.unitC); if(unit.unitT!=null) towerList.Add(unit.unitT); } } } else{ creepList.Add(tgtUnit); towerList.Add(tgtUnit); } AbilityEffect eff=ab.GetActiveEffect(); for(int n=0; n<creepList.Count; n++){ if(eff.damageMax>0){ creepList[n].ApplyDamage(Random.Range(eff.damageMin, eff.damageMax)); } else if(eff.stunChance>0 && eff.duration>0){ if(Random.Range(0f, 1f)<eff.stunChance) creepList[n].ApplyStun(eff.duration); } else if(eff.slow.IsValid()){ creepList[n].ApplySlow(eff.slow); } else if(eff.dot.GetTotalDamage()>0){ creepList[n].ApplyDot(eff.dot); } } for(int n=0; n<towerList.Count; n++){ if(eff.duration>0){ if(eff.damageBuff>0){ towerList[n].ABBuffDamage(eff.damageBuff, eff.duration); } else if(eff.rangeBuff>0){ towerList[n].ABBuffRange(eff.rangeBuff, eff.duration); } else if(eff.cooldownBuff>0){ towerList[n].ABBuffCooldown(eff.cooldownBuff, eff.duration); } } else if(eff.HPGainMax>0){ towerList[n].RestoreHP(Random.Range(eff.HPGainMin, eff.HPGainMax)); } } }
IEnumerator ButtonCDROutine(Ability ab){ int ID=ab.ID; buttonList[ID].imageIcon.color=new Color(.125f, .125f, .125f, 1); if(ab.usedCount==ab.maxUseCount){ buttonList[ID].label.text="Used"; yield break; } while(true){ string text=""; float duration=ab.currentCD; if(duration<=0) break; if(duration>60) text=Mathf.Floor(duration/60).ToString("F0")+"m"; else text=(Mathf.Ceil(duration)).ToString("F0")+"s"; buttonList[ID].label.text=text; yield return new WaitForSeconds(0.1f); } buttonList[ID].imageIcon.color=new Color(1, 1, 1, 1); buttonList[ID].label.text=""; }
public static void AddNewAbility(Ability ab){ instance._AddNewAbility(ab); }
public void _AddNewAbility(Ability newAbility){ float offset=0.5f * (buttonList.Count-1) * 55; for(int i=0; i<buttonList.Count; i++){ buttonList[i].rootT.localPosition+=new Vector3(offset, 0, 0); } int count=buttonList.Count; buttonList.Add(buttonList[count-1].Clone("button"+(count), new Vector3(60, 0, 0))); buttonList[count].imageIcon.sprite=newAbility.icon; EventTrigger trigger = buttonList[count].rootObj.GetComponent<EventTrigger>(); if(newAbility.requireTargetSelection){ EventTrigger.Entry entryRequireTargetSelect=SetupTriggerEntry(true); trigger.triggers.Add(entryRequireTargetSelect); } else{ EventTrigger.Entry entryDontRequireTargetSelect=SetupTriggerEntry(false); trigger.triggers.Add(entryDontRequireTargetSelect); } offset=0.5f * (buttonList.Count-1) * 55; for(int i=0; i<buttonList.Count; i++){ buttonList[i].rootT.localPosition-=new Vector3(offset, 0, 0); } }
public static void AddNewAbility(Ability ab) { instance._AddNewAbility(ab); }