using ActionGameFramework.Health;
|
using Core.Utilities;
|
using DG.Tweening;
|
using System;
|
using System.Net.Http.Headers;
|
using TowerDefense.Agents;
|
using TowerDefense.UI.HUD;
|
using UnityEngine;
|
using System.Collections;
|
|
namespace TowerDefense.Towers.Projectiles
|
{
|
/// <summary>
|
/// Implementation of hitscan projectile
|
/// The principle behind this weapon is that it instantly attacks enemies
|
/// </summary>
|
[RequireComponent(typeof(Damager))]
|
public class HitscanAttack : MonoBehaviour
|
{
|
/// <summary>
|
/// The amount of time to delay
|
/// </summary>
|
public float delay;
|
|
/// <summary>
|
/// 攻击敌人后,重设敌人到开始位置的概率,默认为零,不需要重设。
|
/// </summary>
|
public float resetStartPosRate = 0;
|
|
/// <summary>
|
/// 链式攻击的概率,目前只有一个链式攻击的模式,暂时不需要处理细节,先测试起来
|
/// </summary>
|
public float chainAttackRate = 0;
|
|
/// <summary>
|
/// 当前攻击塔位对应的属性ID
|
/// </summary>
|
public int attributeId = 0;
|
|
/// <summary>
|
/// The delay timer
|
/// </summary>
|
protected Timer m_Timer;
|
|
/// <summary>
|
/// The enemy this projectile will attack
|
/// </summary>
|
protected Targetable m_Enemy;
|
|
/// <summary>
|
/// The Damager attached to the object
|
/// </summary>
|
protected Damager m_Damager;
|
|
/// <summary>
|
/// The towers projectile position
|
/// </summary>
|
protected Vector3 m_Origin;
|
|
/// <summary>
|
/// Configuration for pausing the timer delay timer
|
/// without setting Time.timeScale to 0
|
/// </summary>
|
protected bool m_PauseTimer;
|
|
protected int mLiveID = 0;
|
|
/// <summary>
|
/// 随机数数据
|
/// </summary>
|
protected System.Random mRand;
|
|
|
/// <summary>
|
/// 攻击增加.
|
/// </summary>
|
public float attackRise { get; set; }
|
|
/// <summary>
|
/// The delay configuration for the attacking
|
/// </summary>
|
/// <param name="origin">
|
/// The point the attack will be fired from
|
/// </param>
|
/// <param name="enemy">
|
/// The enemy to attack
|
/// </param>
|
public void AttackEnemy(Vector3 origin, Targetable enemy, float delayTime = 0)
|
{
|
m_Enemy = enemy;
|
m_Origin = origin;
|
// 更快的击中目标:
|
if (delayTime > 0)
|
m_Timer.SetTime(delayTime / 4.0f);
|
m_Timer.Reset();
|
m_PauseTimer = false;
|
mLiveID = enemy.liveID;
|
}
|
|
/// <summary>
|
/// 处理塔位的属性攻击
|
/// </summary>
|
/// <param name="enemy"></param>
|
/// <param name="attid"></param>
|
protected void ProcessTowerAttributeAttack(Targetable enemy, float damage, int attid)
|
{
|
int id = (int)Math.Floor(attid / 10000.0f);
|
switch (id)
|
{
|
case 2: // 减速.
|
enemy.addSpeedSlowRate(0.25f);
|
enemy.SetTargetableMatColor(Color.blue);
|
break;
|
case 3: // 中毒
|
enemy.poisonAgent(damage, attid);
|
enemy.SetTargetableMatColor(Color.green);
|
break;
|
case 5: // 破甲
|
enemy.bShieldBreak = true;
|
break;
|
}
|
return;
|
}
|
|
/// <summary>
|
/// The actual attack of the hitscan attack.
|
/// Early returns from the method if the there is no enemy to attack.
|
/// </summary>
|
protected void DealDamage()
|
{
|
Poolable.TryPool(gameObject);
|
|
if (m_Enemy == null)
|
{
|
return;
|
}
|
|
// 攻击目标已经经历过了Pool了,不能再攻击了。
|
if (mLiveID != m_Enemy.liveID) return;
|
|
// effects
|
// ParticleSystem pfxPrefab = m_Damager.collisionParticles;
|
// var attackEffect = Poolable.TryGetPoolable<ParticleSystem>(pfxPrefab.gameObject);
|
// attackEffect.transform.position = m_Enemy.position;
|
// attackEffect.Play();
|
|
// GameObject hitObj = Poolable.TryGetPoolable(m_Damager.collisionObj);
|
// ParticleSystem ps = hitObj.GetComponent<ParticleSystem>();
|
// if (ps == null)
|
// ps = hitObj.transform.GetChild(0).GetComponent<ParticleSystem>();
|
// ps.Play();
|
// // StartCoroutine(RecycleParticle(hitObj, ps.main.duration));
|
|
// var hitVFX = Instantiate(m_Damager.collisionObj, m_Enemy.position, Quaternion.identity);
|
// var ps = hitVFX.GetComponent<ParticleSystem>();
|
// if (ps == null)
|
// {
|
// var psChild = hitVFX.transform.GetChild(0).GetComponent<ParticleSystem>();
|
// psChild.Play();
|
// Destroy(hitVFX, psChild.main.duration);
|
// }
|
// else
|
// {
|
// ps.Play();
|
// Destroy(hitVFX, ps.main.duration);
|
// }
|
|
float finalDamage = m_Damager.finalDamage;
|
bool crit = m_Damager.isCrit;
|
if (crit)
|
{
|
finalDamage += finalDamage;
|
|
// 暂时去掉这个ShakePosition的功能:
|
//m_Enemy.transform.DOShakePosition(0.5f);
|
}
|
// 精英怪和Boss双倍攻击.
|
bool doubleHit = m_Damager.doubleHit && m_Enemy.bElit;
|
if (doubleHit)
|
{
|
finalDamage *= 2;
|
}
|
|
//
|
// 处理光塔对应的攻击增加:
|
if (attackRise > 0)
|
finalDamage += (finalDamage * attackRise);
|
// 破甲状态
|
if (m_Enemy.bShieldBreak)
|
finalDamage += (finalDamage * 0.1f);
|
|
//
|
// 提前处理非当前Enemy的爆炸攻击:
|
if (chainAttackRate > 0)
|
AgentInsManager.instance.StartExplodeAttack(m_Enemy as Agent, finalDamage);
|
|
int tid = m_Enemy.liveID;
|
Vector3 backPos = m_Enemy.position;
|
m_Enemy.TakeDamage(finalDamage, m_Enemy.position, m_Damager.alignmentProvider, attributeId);
|
|
// 处理塔位的技能攻击:
|
ProcessTowerAttributeAttack(m_Enemy, finalDamage, attributeId);
|
|
if (!m_Enemy.opponentAgent)
|
GameUI.instance.generateBloodText(backPos, finalDamage, crit, doubleHit);
|
|
// 播放受击动画:
|
if ((!m_Enemy.isDead) && (m_Enemy.liveID == tid))
|
(m_Enemy as Agent).PlayOnHit();
|
|
// 重设到开始位置的处理. liveID必须要等于tid.
|
/*if( (resetStartPosRate > 0)&&(!m_Enemy.isDead)&& (m_Enemy.liveID == tid))
|
{
|
if (mRand.NextDouble() < resetStartPosRate)
|
{
|
Agent ag = m_Enemy as Agent;
|
if( ag.bBoss)
|
{
|
// 如果是Boss,更低的概率重设位置.
|
if (mRand.NextDouble() < resetStartPosRate / 20.0f)
|
ag.execAgentPosResetAction();
|
}
|
else
|
ag.execAgentPosResetAction();
|
}
|
|
}*/
|
|
/*// 链式攻击的测试处理: 需要确保是同一个敌人,ID相同.
|
if( (chainAttackRate > 0)&& (!m_Enemy.isDead) && (m_Enemy.liveID == tid) )
|
{
|
if (mRand.NextDouble() < chainAttackRate)
|
AgentInsManager.instance.StartChainAttack(m_Enemy as Agent,m_Damager.alignmentProvider,(float)Math.Floor(finalDamage/2.0f ));
|
|
}*/
|
|
m_PauseTimer = true;
|
}
|
|
/// <summary>
|
/// Cache the damager component attached to this object
|
/// </summary>
|
protected virtual void Awake()
|
{
|
m_Damager = GetComponent<Damager>();
|
float fdelay = delay - 0.05f;
|
if (fdelay < 0) fdelay = 0.0f;
|
m_Timer = new Timer(fdelay, DealDamage);
|
if ((resetStartPosRate > 0) || (chainAttackRate > 0))
|
mRand = new System.Random();
|
|
attackRise = 0.0f;
|
}
|
|
/// <summary>
|
/// Update the m_Timer if it is available
|
/// </summary>
|
protected virtual void Update()
|
{
|
if (!m_PauseTimer)
|
{
|
m_Timer.Tick(Time.deltaTime);
|
}
|
}
|
}
|
}
|