feat(WerewolfSkill): 狂暴狼人技能系统重构为吸血战坦体系 - 实现完整的嗜血狂暴技能体系:1级嗜血狂暴、3级血脉沸腾、 6级残虐血性、10级不灭狼王四个阶段 - 添加重抓撕咬动画效果和鲜血飞溅特效 - 实现扇形AOE撕裂攻击和吸血回复机制 - 添加背水一战机制:血量越低减伤和吸血越高 - 实现击杀刷新技能时间的连击机制 - 更新狼人定位从快速近战改为吸血战坦 - 添加全局单位击杀事件监听处理 ```
400 lines
14 KiB
C#
400 lines
14 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
/// <summary>
|
||
/// 狂暴狼人技能:嗜血狂暴体系(吸血战坦 · 1/3/6/10 级完整阶梯)
|
||
/// 1级【嗜血狂暴】:重抓撕咬造成 150% 伤害(立即回复该次伤害 50% 生命);激活 5 秒【嗜血状态】,普攻吸血 +30%,攻速 +25%
|
||
/// 3级【血脉沸腾】:嗜血状态延长至 6.5 秒,期间普攻吸血提升至 45%,攻速 +35%,撕咬伤害提升至 190%
|
||
/// 6级【残虐血性】:撕咬升级为前方扇形 AOE 撕裂;嗜血期间触发【背水一战】:自身血量每降低 10%,伤害减免 +3%,普攻吸血额外 +5%
|
||
/// 10级【不灭狼王】:释放技能临时提升 35% 最大生命值上限及等额回血;技能期间只要击杀敌人,立即回复 20% 最大生命值并刷新嗜血时间!
|
||
/// </summary>
|
||
public class WerewolfSkill : UnitSkill
|
||
{
|
||
[Header("特效资源")]
|
||
[Tooltip("重抓抓痕特效 (SwordSlashMiniRed)")]
|
||
public GameObject clawSlashVfxPrefab;
|
||
|
||
[Tooltip("受击鲜血爆裂特效 (BloodExplosion)")]
|
||
public GameObject bloodHitVfxPrefab;
|
||
|
||
private bool isSkillPending = false;
|
||
private bool isBloodthirstActive = false;
|
||
private float bloodthirstTimer = 0f;
|
||
private float bloodthirstMaxDuration = 5f;
|
||
|
||
// 属性加成追踪与还原
|
||
private float baseBonusLifesteal = 0f;
|
||
private float dynamicLossLifesteal = 0f;
|
||
private float dynamicLossDamageReduction = 0f;
|
||
private float appliedAttackSpeedMultiplier = 1f;
|
||
private float temporaryBonusMaxHealth = 0f;
|
||
|
||
private void EnsureUnit()
|
||
{
|
||
if (unit == null)
|
||
{
|
||
unit = GetComponent<UnitBase>();
|
||
}
|
||
}
|
||
|
||
protected override void Awake()
|
||
{
|
||
base.Awake();
|
||
EnsureUnit();
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
EnsureUnit();
|
||
WorldHealthBar.OnUnitKilled += OnGlobalUnitKilled;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当能量蓄满时触发:播放大招攻击动画
|
||
/// </summary>
|
||
public override void ReleaseSkill()
|
||
{
|
||
EnsureUnit();
|
||
if (unit == null) return;
|
||
|
||
WorldHealthBar healthBar = unit.GetComponent<WorldHealthBar>();
|
||
if (healthBar == null || healthBar.IsDead) return;
|
||
|
||
if (unit.Target == null) return;
|
||
|
||
unit.FaceCurrentTarget(0f);
|
||
isSkillPending = true;
|
||
|
||
// 10级大招临时提升 35% 最大生命上限
|
||
if (unit.level >= 10 && temporaryBonusMaxHealth <= 0f)
|
||
{
|
||
temporaryBonusMaxHealth = healthBar.maxHealth * 0.35f;
|
||
healthBar.maxHealth += temporaryBonusMaxHealth;
|
||
healthBar.Heal(temporaryBonusMaxHealth);
|
||
Debug.Log($"<color=red>【不灭狼王】爆发!</color> 狼王之怒提升生命上限 +{temporaryBonusMaxHealth:F0} 并回血!");
|
||
}
|
||
|
||
// 播放大招攻击动画(挠击动作)
|
||
unit.PlaySkillAnimation(OnSkillAnimationFinished, 1.2f);
|
||
Debug.Log($"<color=red>【狂暴狼人】释放大招【嗜血狂暴】</color>,等待挠击命中帧!");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画帧命中回调(兼容 OnAttackEffect 和 OnSkillHit1)
|
||
/// </summary>
|
||
public void OnSkillHit1()
|
||
{
|
||
ExecuteBiteStrike();
|
||
}
|
||
|
||
public void OnAttackEffect()
|
||
{
|
||
if (isSkillPending)
|
||
{
|
||
ExecuteBiteStrike();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 实际结算大招重抓撕咬伤害与启动嗜血狂暴
|
||
/// </summary>
|
||
private void ExecuteBiteStrike()
|
||
{
|
||
if (!isSkillPending) return;
|
||
isSkillPending = false;
|
||
|
||
EnsureUnit();
|
||
if (unit == null) return;
|
||
|
||
WorldHealthBar healthBar = unit.GetComponent<WorldHealthBar>();
|
||
if (healthBar == null || healthBar.IsDead) return;
|
||
|
||
int level = unit.level;
|
||
|
||
// 伤害倍率:1级 150%,3级+ 190%
|
||
float damageMultiplier = level >= 3 ? 1.90f : 1.50f;
|
||
float damage = unit.attackPower * damageMultiplier;
|
||
|
||
// 6级升级为扇形前方 AOE 撕咬,否则为单体重抓
|
||
if (level >= 6)
|
||
{
|
||
ExecuteCleaveBite(damage);
|
||
}
|
||
else
|
||
{
|
||
ExecuteSingleBite(damage);
|
||
}
|
||
|
||
// 激活【嗜血状态】
|
||
ActivateBloodthirst(level);
|
||
}
|
||
|
||
private void ExecuteSingleBite(float damage)
|
||
{
|
||
if (unit.Target == null) return;
|
||
|
||
GameObject target = unit.Target;
|
||
UnitBase targetUnit = target.GetComponentInParent<UnitBase>();
|
||
WorldHealthBar targetBar = target.GetComponentInParent<WorldHealthBar>();
|
||
|
||
PlayClawAndBloodVfx(target.transform.position + Vector3.up * 0.8f);
|
||
|
||
float dealtDamage = 0f;
|
||
if (targetUnit != null)
|
||
{
|
||
targetUnit.ReceiveDamage(damage, unit.gameObject);
|
||
dealtDamage = damage;
|
||
}
|
||
else if (targetBar != null)
|
||
{
|
||
targetBar.TakeDamage(damage, unit.gameObject);
|
||
dealtDamage = damage;
|
||
}
|
||
|
||
// 立即回复该次伤害 50% 的生命
|
||
WorldHealthBar myBar = unit.GetComponent<WorldHealthBar>();
|
||
if (myBar != null && dealtDamage > 0f)
|
||
{
|
||
float healAmount = dealtDamage * 0.5f;
|
||
myBar.Heal(healAmount);
|
||
Debug.Log($"<color=green>【嗜血狂暴】撕咬重创!</color> 造成 {dealtDamage:F1} 伤害,吸血回复 +{healAmount:F1} HP!");
|
||
}
|
||
}
|
||
|
||
private void ExecuteCleaveBite(float damage)
|
||
{
|
||
float cleaveRadius = 2.4f;
|
||
float cleaveAngle = 120f;
|
||
Vector3 forward = unit.transform.forward;
|
||
Vector3 myPos = unit.transform.position;
|
||
|
||
UnitTeam myTeam = unit.GetComponent<UnitTeam>();
|
||
var allTeams = FindObjectsOfType<UnitTeam>();
|
||
float totalDealt = 0f;
|
||
|
||
foreach (var t in allTeams)
|
||
{
|
||
if (t == null || !t.gameObject.activeInHierarchy || myTeam == null || !myTeam.IsEnemy(t.gameObject)) continue;
|
||
|
||
Vector3 toEnemy = t.transform.position - myPos;
|
||
toEnemy.y = 0f;
|
||
if (toEnemy.sqrMagnitude <= cleaveRadius * cleaveRadius)
|
||
{
|
||
float angle = Vector3.Angle(forward, toEnemy.normalized);
|
||
if (angle <= cleaveAngle * 0.5f)
|
||
{
|
||
PlayClawAndBloodVfx(t.transform.position + Vector3.up * 0.8f);
|
||
|
||
var targetUnit = t.GetComponentInParent<UnitBase>();
|
||
if (targetUnit != null)
|
||
{
|
||
targetUnit.ReceiveDamage(damage, unit.gameObject);
|
||
totalDealt += damage;
|
||
}
|
||
else
|
||
{
|
||
var bar = t.GetComponentInParent<WorldHealthBar>();
|
||
if (bar != null)
|
||
{
|
||
bar.TakeDamage(damage, unit.gameObject);
|
||
totalDealt += damage;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 6级扇形撕裂吸血:回复总伤害的 50%
|
||
WorldHealthBar myBar = unit.GetComponent<WorldHealthBar>();
|
||
if (myBar != null && totalDealt > 0f)
|
||
{
|
||
float healAmount = totalDealt * 0.5f;
|
||
myBar.Heal(healAmount);
|
||
Debug.Log($"<color=green>【残虐血性】扇形撕裂!</color> 命中敌群造成 {totalDealt:F1} 伤害,吸血狂吸 +{healAmount:F1} HP!");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 激活/刷新嗜血状态
|
||
/// </summary>
|
||
private void ActivateBloodthirst(int level)
|
||
{
|
||
float duration = level >= 3 ? 6.5f : 5.0f;
|
||
float bonusLifesteal = level >= 3 ? 0.45f : 0.30f;
|
||
float bonusAttackSpeed = level >= 3 ? 1.35f : 1.25f;
|
||
|
||
bloodthirstMaxDuration = duration;
|
||
bloodthirstTimer = duration;
|
||
|
||
if (!isBloodthirstActive)
|
||
{
|
||
isBloodthirstActive = true;
|
||
baseBonusLifesteal = bonusLifesteal;
|
||
unit.lifestealRate += baseBonusLifesteal;
|
||
|
||
appliedAttackSpeedMultiplier = bonusAttackSpeed;
|
||
unit.MultiplyAttackSpeed(appliedAttackSpeedMultiplier, 3.5f);
|
||
}
|
||
else
|
||
{
|
||
// 已在嗜血中时重置持续时间
|
||
bloodthirstTimer = duration;
|
||
}
|
||
|
||
Debug.Log($"<color=red>【嗜血状态激活】</color> 持续 {duration:F1} 秒:吸血 +{bonusLifesteal * 100:F0}%,攻速 +{(bonusAttackSpeed - 1f) * 100:F0}%!");
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
if (!isBloodthirstActive || unit == null) return;
|
||
|
||
bloodthirstTimer -= Time.deltaTime;
|
||
|
||
// 6级【背水一战】:嗜血期间自身血量越残,额外获得越高额的减伤与吸血
|
||
if (unit.level >= 6)
|
||
{
|
||
UpdateDesperationBuff();
|
||
}
|
||
|
||
if (bloodthirstTimer <= 0f)
|
||
{
|
||
EndBloodthirst();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 6级【背水一战】:自身血量每降低 10%,减伤 +3%,吸血 +5%
|
||
/// </summary>
|
||
private void UpdateDesperationBuff()
|
||
{
|
||
WorldHealthBar healthBar = unit.GetComponent<WorldHealthBar>();
|
||
if (healthBar == null || healthBar.maxHealth <= 0f) return;
|
||
|
||
float lostHealthRatio = Mathf.Clamp01(1f - (healthBar.currentHealth / healthBar.maxHealth));
|
||
int lostTens = Mathf.FloorToInt(lostHealthRatio * 10f); // 每 10%
|
||
|
||
float targetReduction = lostTens * 0.03f; // 每 10% 减伤 +3% (最高 27%)
|
||
float targetExtraLifesteal = lostTens * 0.05f; // 每 10% 吸血 +5% (最高 45%)
|
||
|
||
// 差量动态更新,避免死循环累加
|
||
float deltaReduction = targetReduction - dynamicLossDamageReduction;
|
||
unit.damageReductionRate += deltaReduction;
|
||
dynamicLossDamageReduction = targetReduction;
|
||
|
||
float deltaLifesteal = targetExtraLifesteal - dynamicLossLifesteal;
|
||
unit.lifestealRate += deltaLifesteal;
|
||
dynamicLossLifesteal = targetExtraLifesteal;
|
||
}
|
||
|
||
private void EndBloodthirst()
|
||
{
|
||
if (!isBloodthirstActive) return;
|
||
isBloodthirstActive = false;
|
||
bloodthirstTimer = 0f;
|
||
|
||
// 还原属性
|
||
if (unit != null)
|
||
{
|
||
unit.lifestealRate = Mathf.Max(0f, unit.lifestealRate - baseBonusLifesteal - dynamicLossLifesteal);
|
||
unit.damageReductionRate = Mathf.Max(0f, unit.damageReductionRate - dynamicLossDamageReduction);
|
||
|
||
if (appliedAttackSpeedMultiplier > 1f)
|
||
{
|
||
unit.MultiplyAttackSpeed(1f / appliedAttackSpeedMultiplier, 3.5f);
|
||
}
|
||
|
||
// 10级大招临时提升的生命上限还原
|
||
if (temporaryBonusMaxHealth > 0f)
|
||
{
|
||
WorldHealthBar bar = unit.GetComponent<WorldHealthBar>();
|
||
if (bar != null)
|
||
{
|
||
bar.maxHealth = Mathf.Max(1f, bar.maxHealth - temporaryBonusMaxHealth);
|
||
bar.currentHealth = Mathf.Min(bar.currentHealth, bar.maxHealth);
|
||
}
|
||
temporaryBonusMaxHealth = 0f;
|
||
}
|
||
}
|
||
|
||
baseBonusLifesteal = 0f;
|
||
dynamicLossLifesteal = 0f;
|
||
dynamicLossDamageReduction = 0f;
|
||
appliedAttackSpeedMultiplier = 1f;
|
||
|
||
Debug.Log($"<color=gray>【嗜血状态结束】属性恢复正常。</color>");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 10级【不灭狼王】:技能期间击杀敌人,立即回复 20% 最大生命值并刷新嗜血时间!
|
||
/// </summary>
|
||
private void OnGlobalUnitKilled(GameObject victim, GameObject killer)
|
||
{
|
||
if (!isBloodthirstActive || unit == null || killer == null) return;
|
||
if (unit.level < 10) return;
|
||
|
||
// 判断击杀者是否是自己
|
||
if (killer == unit.gameObject || killer.transform.IsChildOf(unit.transform))
|
||
{
|
||
WorldHealthBar myBar = unit.GetComponent<WorldHealthBar>();
|
||
if (myBar != null && !myBar.IsDead)
|
||
{
|
||
float heal = myBar.maxHealth * 0.20f;
|
||
myBar.Heal(heal);
|
||
|
||
// 重置/刷新嗜血持续时间
|
||
bloodthirstTimer = bloodthirstMaxDuration;
|
||
Debug.Log($"<color=red>【不灭狼王·猎杀连环】!</color> 击杀 {victim.name},立即回复 20% 生命 (+{heal:F0}) 并刷新狂暴状态!");
|
||
}
|
||
}
|
||
}
|
||
|
||
private void PlayClawAndBloodVfx(Vector3 position)
|
||
{
|
||
// 1. 鲜血飞溅
|
||
GameObject bloodPrefab = bloodHitVfxPrefab;
|
||
#if UNITY_EDITOR
|
||
if (bloodPrefab == null)
|
||
{
|
||
bloodPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/TJGeneratorLibEffects/Prefabs/Combat/Blood/Red/BloodExplosion.prefab");
|
||
}
|
||
#endif
|
||
if (bloodPrefab != null)
|
||
{
|
||
var vfx = Instantiate(bloodPrefab, position, Quaternion.identity);
|
||
vfx.transform.localScale = Vector3.one * 1.5f;
|
||
Destroy(vfx, 1.5f);
|
||
}
|
||
|
||
// 2. 红色抓痕
|
||
GameObject slashPrefab = clawSlashVfxPrefab;
|
||
#if UNITY_EDITOR
|
||
if (slashPrefab == null)
|
||
{
|
||
slashPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/TJGeneratorLibEffects/Prefabs/Combat/Sword/Slash/SwordSlashMini/SwordSlashMiniRed.prefab");
|
||
}
|
||
#endif
|
||
if (slashPrefab != null && unit != null)
|
||
{
|
||
Quaternion rot = Quaternion.Euler(-90f, 0f, -unit.transform.eulerAngles.y);
|
||
var vfx = Instantiate(slashPrefab, position, rot);
|
||
vfx.transform.localScale = Vector3.one * 1.4f;
|
||
Destroy(vfx, 1.0f);
|
||
}
|
||
}
|
||
|
||
private void OnSkillAnimationFinished()
|
||
{
|
||
if (isSkillPending)
|
||
{
|
||
ExecuteBiteStrike(); // 容错兜底:若动画结束仍未收到 Event,强制结算撕咬
|
||
}
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
WorldHealthBar.OnUnitKilled -= OnGlobalUnitKilled;
|
||
EndBloodthirst();
|
||
}
|
||
}
|