public AttackInstance Clone() { AttackInstance aInstance=new AttackInstance(); aInstance.isAOE=isAOE; aInstance.aoeDistance=aoeDistance; aInstance.aStats=aStats.Clone(); return aInstance; }
//function call to fire the object public void Shoot(int srcL, float srcR, Transform shootPoint, AttackInstance aInstance=null, AimInfo aimInfo=null) { Init(); thisObj.SetActive(true); //cached all the passed information lcoally attInstance=aInstance; srcLayer=srcL; //the layer of the shooting unit (so we know it's from player or AI) srcRange=srcR; //the range of the shooting unit (so we know when to stop) state=_State.Shot; shootTime=Time.time; //to track how long the shoot object is has been travelledDistance=0; //to track how far the shoot object is has been fired //if there's any hideObject, set it to true (it's probably set to false when the shoot-object last hit something) if(hideObject!=null) hideObject.SetActive(true); //instantiate the shoot effect ShootEffect(thisT.position, thisT.rotation); if(type==_SOType.Simple || type==_SOType.Homing){ //if(aInstance!=null && thisCollider.enabled){ //~ Physics.IgnoreCollision(aInstance.srcUnit.GetCollider(), thisObj.GetComponent<Collider>(), true); //Debug.Log("collision avoidance with shooter unresolved"); //Physics.IgnoreCollision(srcCollider, thisCollider, true); //} // for homing shootobject, the shootobject needs to be aiming at some position, or a specific unit if(type==_SOType.Homing){ if(aimInfo.targetT!=null) targetUnit=aimInfo.unit; targetPos=aimInfo.hitPoint+new Vector3(Random.value-0.5f, 0, Random.value-0.5f)*2*spread; initialPos=shootPoint.position; initialDist=Vector3.Distance(targetPos, initialPos); curveMod=Random.Range(0, 2f); dummyPos=thisT.TransformPoint(Vector3.forward*speed*5); dummyPos=(targetPos+dummyPos)/2; } } else if(type==_SOType.Beam){ //if(attachedToShootPoint) transform.parent=shootPoint; thisT.parent=shootPoint; ObjectPoolManager.Unspawn(thisObj, beamDuration-.01f); } else if(type==_SOType.Point){ StartCoroutine(PointRoutine(aimInfo)); } //update the layermask used to do the hit detection in accordance to rules set in GameControl UpdateSphereCastMask(!GameControl.SOHitFriendly(), !GameControl.SOHitShootObject(), !GameControl.SOHitCollectible()); }
//~ IEnumerator ShootRoutine(RaycastHit hit=default(RaycastHit)){ IEnumerator ShootRoutine(ShootObject.AimInfo aimInfo=null) { if(uAnimation!=null) uAnimation.AttackRange(); AttackStats aStats=ModifyAttackStatsToExistingEffect(weaponList[weaponID].CloneStats()); AttackInstance aInstance=new AttackInstance(this, aStats); int weapID=weaponID; //to prevent weapon switch and state change while delay and firing multiple so int spread=weaponList[weapID].spread; if(spread>1){ aInstance.aStats.damageMin/=spread; aInstance.aStats.damageMax/=spread; } float startAngle=spread>1 ? -weaponList[weapID].spreadAngle/2f : 0 ; float angleDelta=spread>1 ? weaponList[weapID].spreadAngle/(spread-1) : 0 ; List<Collider> soColliderList=new List<Collider>(); //colliders of all the so fired, used to tell each so to ignore each other for(int i=0; i<weaponList[weapID].shootPointList.Count; i++){ Transform shootPoint=weaponList[weapID].shootPointList[i]; float recoilSign=(Random.value<recoilSignTH ? -1 : 1); recoilSignTH=Mathf.Clamp(recoilSignTH+(recoilSign>0 ? 0.25f : -0.25f), 0, 1); float recoilValue=recoilSign*Random.Range(0.1f, 1f)*GetRecoil(); Quaternion baseShootRot=shootPoint.rotation*Quaternion.Euler(0, recoilValue, 0); for(int m=0; m<Mathf.Max(1, spread); m++){ Vector3 shootPos=shootPoint.position; if(spread>1) shootPos=shootPoint.TransformPoint(new Vector3(0, 0, Random.Range(-1.5f, 1.5f))); Quaternion shootRot=baseShootRot*Quaternion.Euler(0, startAngle+(m*angleDelta), 0); //GameObject soObj=(GameObject)Instantiate(weaponList[weapID].shootObject, shootPos, shootRot); GameObject soObj=ObjectPoolManager.Spawn(weaponList[weapID].shootObject, shootPos, shootRot); ShootObject soInstance=soObj.GetComponent<ShootObject>(); soInstance.IgnoreCollider(GetCollider()); for(int n=0; n<soColliderList.Count; n++) soInstance.IgnoreCollider(soColliderList[n]); if(soInstance.GetCollider()!=null) soColliderList.Add(soInstance.GetCollider()); soInstance.Shoot(thisObj.layer, GetRange(), shootPoint, aInstance.Clone(), aimInfo); //soInstance.Shoot(thisObj.layer, GetRange(), shootPoint, aInstance.Clone(), hit); } TDS.CameraShake(weaponList[weapID].recoilCamShake); if(weaponList[weapID].shootPointDelay>0) yield return new WaitForSeconds(weaponList[weapID].shootPointDelay); if(weapID>=weaponList.Count) break; } }
//applies the effect of an attack public void ApplyAttack(AttackInstance attInstance) { //if unit is invincible, do nothing if(IsInvincible()){ Debug.Log("Immuned"); Vector3 osPos=new Vector3(0, Random.value+0.5f, 0); new TextOverlay(thisT.position+osPos, "Immuned"); return; } AttackStats aStats=attInstance.aStats; //check if the attack is an aoe and modify the damage value is dimishingAOE is enabled float damage=Random.Range(aStats.damageMin, aStats.damageMax); if(attInstance.isAOE && aStats.dimishingAOE){ damage*=Mathf.Clamp(1-attInstance.aoeDistance/aStats.aoeRadius, 0, 1); } //check for cirtical and modify the damage based on critical multiplier bool critical=Random.value<aStats.critChance; if(critical) damage*=aStats.critMultiplier; //modify the damage based on damage and armor type damage*=DamageTable.GetModifier(armorType, aStats.damageType); //if damage is valid, modify the hit point if(damage>0){ //show the overlay (for UI) Vector3 offsetPos=new Vector3(0, Random.value+0.5f, 0); if(!critical) new TextOverlay(thisT.position+offsetPos, damage.ToString("f0")); else new TextOverlay(thisT.position+offsetPos, damage.ToString("f0"), new Color(1f, 0.9f, 0.9f, 1f), 1.5f); //if the unit is player, fire event to inform UI if(thisObj.layer==TDS.GetLayerPlayer()) TDS.PlayerDamaged(damage); //register the stagger hpRegenStaggerCounter=hpRegenStagger; hitPoint-=damage; //damage the hitpoint if(hitPoint<=0) Destroyed(); if(hitPoint>0 && uAnimation!=null) uAnimation.Hit(); } //apply effect if there's any if(aStats.effect!=null) ApplyEffect(aStats.effect); }
IEnumerator _ShootTarget(float targetDist) { if(targetDist>GetRange()) yield break; //if target is out of range, dont continue currentCD=cooldown; //set the cooldown if(uAnimation!=null) uAnimation.AttackRange(); //play the attack animation List<Collider> soColliderList=new List<Collider>(); //colliders of all the shoot-objs fired, used to tell each so to ignore each other //loop through the shoot-points, fire an shoot-object for each of them for(int i=0; i<shootPointList.Count; i++){ //create a new attak instance for the attack AttackInstance attInstance=new AttackInstance(this, ModifyAttackStatsToExistingEffect(attackStats.Clone())); //record the target position, in case the target get destroyed before all shoot point has fired if(target!=null) targetLastPos=target.thisT.position; //create an aimInfo instance for the shootObject ShootObject.AimInfo aimInfo=target!=null ? new ShootObject.AimInfo(target) : new ShootObject.AimInfo(targetLastPos); //get the shoot rotation Quaternion shootRot=shootPointList[i].rotation; if(alwaysShootTowardsTarget && target!=null) shootRot=Quaternion.LookRotation(target.thisT.position-thisT.position); //spawn the shootobject GameObject soObj=ObjectPoolManager.Spawn(shootObject, shootPointList[i].position, shootRot); ShootObject soInstance=soObj.GetComponent<ShootObject>(); //inform the shootobject to ignore the certain collider soInstance.IgnoreCollider(GetCollider()); for(int n=0; n<soColliderList.Count; n++) soInstance.IgnoreCollider(soColliderList[n]); if(soInstance.GetCollider()!=null) soColliderList.Add(soInstance.GetCollider()); //fire the shootobject soInstance.Shoot(thisObj.layer, GetRange(), shootPointList[i], attInstance, aimInfo); //delay a bit before the next shoot point, if a delay has been specified if(shootPointDelay>0) yield return new WaitForSeconds(shootPointDelay); } yield return null; }
//function call when the unit collide with a player unit, for contact(melee) attack void OnPlayerContact() { if(!enableContactAttack) return; if(contactCurrentCD>0) return; //if attack is still on cd if(uAnimation!=null) uAnimation.AttackMelee(); //play attack animation contactCurrentCD=contactCooldown; //set the cooldown //create an attack instance and attack the player unit AttackInstance attInstance=new AttackInstance(this, ModifyAttackStatsToExistingEffect(contactAttackStats.Clone())); GameControl.GetPlayer().ApplyAttack(attInstance); }
//apply effect to all active hostile unit void ApplyEffectAll() { //get all active hostile unit from UnitTracker List<Unit> unitList=UnitTracker.GetAllUnitList(); for(int i=0; i<unitList.Count; i++){ //clone the attack stats so that the original value wont get modified when making further calculation AttackInstance aInstance=new AttackInstance(); aInstance.aStats=aStats.Clone(); unitList[i].ApplyAttack(aInstance); } }
//apply effect to all hostile unit surrounding the player void ApplyEffectAOE(Collider playerCollider) { float aoeRadius=aStats.aoeRadius; if(aoeRadius>0){ Collider[] cols=Physics.OverlapSphere(transform.position, aoeRadius); //get all the collider in range for(int i=0; i<cols.Length; i++){ if(cols[i]==playerCollider) continue; //clone the attack stats so that the original value wont get modified when making further calculation AttackInstance aInstance=new AttackInstance(null, aStats); aInstance.isAOE=true; aInstance.aoeDistance=Vector3.Distance(transform.position, cols[i].transform.position); Unit unitInstance=cols[i].gameObject.GetComponent<Unit>(); if(unitInstance!=null) unitInstance.ApplyAttack(aInstance); } } //since this is aoe effect, explosion force applies TDSPhysics.ApplyExplosionForce(transform.position, aStats, true); }
//launch the ability, at the position given public void Activate(Vector3 pos=default(Vector3)) { currentCD=cooldown; //set the cooldown GameControl.GetPlayer().energy-=cost; //deduct player energy by the ability cost AudioManager.PlaySound(launchSFX); //instantiate the launch object, if there's any if(launchObj!=null){ GameObject obj=(GameObject)MonoBehaviour.Instantiate(launchObj, pos, Quaternion.identity); if(autoDestroyLaunchObj) MonoBehaviour.Destroy(obj, launchObjActiveDuration); } //for aoe ability if(type==_AbilityType.AOE || type==_AbilityType.AOESelf){ //get all the collider in range Collider[] cols=Physics.OverlapSphere(pos, aStats.aoeRadius); for(int i=0; i<cols.Length; i++){ Unit unitInstance=cols[i].gameObject.GetComponent<Unit>(); //only continue if the collider is a unit and is not player if(unitInstance!=null && unitInstance!=GameControl.GetPlayer()){ //create an AttackInstance and mark it as AOE attack AttackInstance aInstance=new AttackInstance(); aInstance.aStats=aStats.Clone(); aInstance.isAOE=true; aInstance.aoeDistance=Vector3.Distance(pos, cols[i].transform.position); //apply the AttackInstance unitInstance.ApplyAttack(aInstance); } } //apply explosion force TDSPhysics.ApplyExplosionForce(pos, aStats); } //for ability that affects all hostile unit in game else if(type==_AbilityType.All){ //get all hostile unit for unit tracker List<Unit> unitList=new List<Unit>( UnitTracker.GetAllUnitList() ); //loop through all unit, create an AttackInstance and apply the attack for(int i=0; i<unitList.Count; i++){ AttackInstance aInstance=new AttackInstance(); aInstance.aStats=aStats.Clone(); unitList[i].ApplyAttack(aInstance); } } //for ability that meant to be cast on player unit else if(type==_AbilityType.Self){ //apply the attack on player AttackInstance aInstance=new AttackInstance(); aInstance.aStats=aStats.Clone(); GameControl.GetPlayer().ApplyAttack(aInstance); } //for ability that fires a shoot object else if(type==_AbilityType.Shoot){ //get the position and rotation to fire the shoot object from Transform srcT=GetShootObjectSrcTransform(); Vector3 shootPos=srcT.TransformPoint(shootPosOffset); pos.y=shootPos.y; Quaternion shootRot=Quaternion.LookRotation(pos-shootPos); //create the AttackInstance AttackInstance aInstance=new AttackInstance(); aInstance.aStats=aStats.Clone(); //Instantiate the shoot-object and fires it GameObject soObj=(GameObject)MonoBehaviour.Instantiate(shootObject, shootPos, shootRot); ShootObject soInstance=soObj.GetComponent<ShootObject>(); soInstance.Shoot(GameControl.GetPlayer().thisObj.layer, range, srcT, aInstance); } }