MusicBeat
This commit is contained in:
@@ -49,6 +49,8 @@ namespace Cielonos.MainGame.Inventory
|
||||
public AmmoSubmodule ammoSm;
|
||||
[HideInEditorMode]
|
||||
public OverloadSubmodule overloadSm;
|
||||
[HideInEditorMode]
|
||||
public AuraSubmodule auraSm;
|
||||
|
||||
[TitleGroup("Subcontrollers")]
|
||||
public FeedbackSubcontroller feedbackSc;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
public class BlackHoleDisplacer : ExtenderBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
8
Assets/Scripts/MainGame/Items/Extenders/FutureWand.meta
Normal file
8
Assets/Scripts/MainGame/Items/Extenders/FutureWand.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76c30d7d91254704cada46246e2e5a0d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// “黑洞”位移器 / “Black Hole” Displacer
|
||||
/// FutureWand的扩展器,使重攻击产生的黑洞向前/后移动,攻击段数由4变为8
|
||||
/// </summary>
|
||||
public class BlackHoleDisplacer : ExtenderBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 飞弹分离膜 / Missile Separation Membrane
|
||||
/// FutureWand的扩展器,使轻攻击的飞弹在击中敌人后分离一次,飞向附近的敌人。
|
||||
/// </summary>
|
||||
public class MissileSeparationMembrane : ExtenderBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fe9df091575f8f428a988fbe724dc3e
|
||||
141
Assets/Scripts/MainGame/Items/MainWeapons/DualHarmony.cs
Normal file
141
Assets/Scripts/MainGame/Items/MainWeapons/DualHarmony.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using Cielonos.MainGame.Characters;
|
||||
using SLSUtilities.FunctionalAnimation;
|
||||
using SLSUtilities.WwiseAssistance;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
public partial class DualHarmony : MainWeaponBase
|
||||
{
|
||||
private MusicBeatSystem MusicBeatSystem => CombatManager.GetCombatSystem<MusicBeatSystem>();
|
||||
|
||||
public CharacterBase currentTarget;
|
||||
public override void OnEquipped()
|
||||
{
|
||||
base.OnEquipped();
|
||||
RegisterFunctionsToAnimSc();
|
||||
/*if(!player.inputSc.IsMoving) PlayTargetedAnimation("Equip");
|
||||
viewObjects["Wand"].SetFadeAnim(0.5f);*/
|
||||
}
|
||||
|
||||
public override void OnPrimaryPress()
|
||||
{
|
||||
if (functionSm["LightAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
comboSm.main.NextCombo("L");
|
||||
string funcAnimName = "Attack" + comboSm.main.GetCurrentNodeName();
|
||||
FuncAnimData data = fullBodyFuncAnimSm.collection[funcAnimName];
|
||||
|
||||
float startUpTime = data.Interval(IntervalType.Active).StartTime;
|
||||
float nextBeatTime = MusicBeatSystem.GetNextBeat().time - MusicBeatSystem.CurrentSongTime;
|
||||
BeatMarker lastMarker = MusicBeatSystem.GetLastBeat();
|
||||
float compensation = lastMarker.time - MusicBeatSystem.CurrentSongTime;
|
||||
|
||||
functionSm["LightAttack"].Execute();
|
||||
|
||||
float difference;
|
||||
if (compensation > -startUpTime)
|
||||
{
|
||||
difference = startUpTime - nextBeatTime;
|
||||
if (difference < 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
difference = startUpTime;
|
||||
}
|
||||
|
||||
currentTarget = CombatManager.EnemySm.GetNearestEnemy(25f);
|
||||
if (currentTarget != null)
|
||||
{
|
||||
PlayTargetedAnimation(funcAnimName, currentTarget, 5f);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayTargetedAnimation(funcAnimName);
|
||||
}
|
||||
|
||||
fullBodyFuncAnimSm.currentRuntimeFuncAnim.currentPlayTime = difference;
|
||||
Debug.Log($"StartUpTime: {nextBeatTime}, FuncAnim Active StartTime: {startUpTime}, Difference: {difference}, Compensation: {compensation}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSecondaryPress()
|
||||
{
|
||||
/*if (functionSm["HeavyAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
comboSm.main.NextCombo("R");
|
||||
float startUpTime = MusicBeatSystem.GetNextBeat().time - MusicBeatSystem.CurrentSongTime;
|
||||
string funcAnimName = "Attack" + comboSm.main.GetCurrentNodeName();
|
||||
FuncAnimData data = fullBodyFuncAnimSm.collection[funcAnimName];
|
||||
|
||||
float difference = data.Interval(IntervalType.Active).StartTime - startUpTime;
|
||||
if (difference < 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentTarget = CombatManager.EnemySm.GetNearestEnemy(25f);
|
||||
if (currentTarget != null)
|
||||
{
|
||||
PlayTargetedAnimation(funcAnimName, currentTarget, 5f);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayTargetedAnimation(funcAnimName);
|
||||
}
|
||||
|
||||
fullBodyFuncAnimSm.currentRuntimeFuncAnim.currentPlayTime = difference;
|
||||
Debug.Log($"StartUpTime: {startUpTime}, FuncAnim Active StartTime: {data.Interval(IntervalType.Active).StartTime}, Difference: {difference}");
|
||||
}*/
|
||||
}
|
||||
|
||||
public override void OnSpecialAPress()
|
||||
{
|
||||
CombatManager.GetCombatSystem<MusicBeatSystem>().Activate();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DualHarmony
|
||||
{
|
||||
private void FAPF_GenerateBullet(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
CustomFunction.PC_StringStringFloat p = rtFuncAnim.GetParams<CustomFunction.PC_StringStringFloat>();
|
||||
GenerateProjectile(p.str0, attackData[p.str1], currentTarget, p.float0);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DualHarmony
|
||||
{
|
||||
private void GenerateProjectile(string vfxName, AttackUnit attackUnit,
|
||||
CharacterBase target, float speed, bool hasMuzzleEffect = true, Transform muzzle = null)
|
||||
{
|
||||
if (hasMuzzleEffect)
|
||||
{
|
||||
muzzle ??= player.bodyPartsSc.leftHand;
|
||||
vfxData.SpawnMuzzleVFX(vfxName, player, muzzle);
|
||||
}
|
||||
|
||||
Projectile projectile = vfxData.SpawnVFX(vfxName, player).GetComponentInChildren<Projectile>();
|
||||
Vector3 direction = player.transform.forward;
|
||||
if (target != null)
|
||||
{
|
||||
direction = (target.centerPoint.position - projectile.transform.position).normalized;
|
||||
}
|
||||
|
||||
projectile.Initialize(player, this, false, 1, Fraction.Enemy)
|
||||
.SetAttackSubmodule<Projectile>(attackUnit)
|
||||
.SetTimeSubmodule<Projectile>(10f)
|
||||
.SetHitSubmodule<Projectile>()
|
||||
.SetAdaptiveTraceMoveModule<Projectile>(target, speed, 5f, 180f, 30f, direction, detectRadius: 25f)
|
||||
.SetRaycastSubmodule<Projectile>(default, 0.25f, 0.5f);
|
||||
projectile.hitSm.AddHitSound(AK.EVENTS.FUTUREWAND_WEAKATTACKHIT);
|
||||
projectile.SetImpulseSubmodule().WithDynamicForce(1f);
|
||||
AudioManager.Post(AK.EVENTS.FUTUREWAND_NORMALBOLTRELEASE, projectile.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0fc3aa61a4c14f47a51b4e6d34bd731
|
||||
@@ -113,9 +113,9 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
private void FAPF_GenerateMultipleBolts(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
CustomFunction.PC_StringStringFloat p = rtFuncAnim.GetParams<CustomFunction.PC_StringStringFloat>();
|
||||
List<CharacterBase> targets = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 25f);
|
||||
List<Enemy> targets = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 25f);
|
||||
if (targets.Count > 0) vfxData.SpawnMuzzleVFX(p.str0, player, muzzle);
|
||||
foreach (CharacterBase target in targets)
|
||||
foreach (Enemy target in targets)
|
||||
{
|
||||
GenerateProjectile(p.str0, attackData[p.str1], target, p.float0, false);
|
||||
}
|
||||
@@ -149,7 +149,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
.SetAttackSubmodule<Projectile>(attackUnit)
|
||||
.SetTimeSubmodule<Projectile>(10f)
|
||||
.SetHitSubmodule<Projectile>()
|
||||
.SetAdaptiveTraceMoveModule<Projectile>(target, speed, 5f, 20f, 20f, direction, detectRadius: 25f)
|
||||
.SetAdaptiveTraceMoveModule<Projectile>(target, speed, 5f, 180f, 30f, direction, detectRadius: 25f)
|
||||
.SetRaycastSubmodule<Projectile>(default, 0.25f, 0.5f);
|
||||
|
||||
if (vfxName == "NormalBolt")
|
||||
@@ -170,6 +170,26 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
new Fusion(fusionStack).Apply(enemy);
|
||||
SubscribeFusionExplode(enemy);
|
||||
|
||||
if (!projectile.tags.Contains("Separation") && HasExtender<MissileSeparationMembrane>())
|
||||
{
|
||||
List<Enemy> nearbyEnemies = CombatManager.EnemySm.GetEnemiesInRadius(hitPosition, 25f).Exclude(enemy as Enemy);
|
||||
|
||||
if (nearbyEnemies.Count > 0)
|
||||
{
|
||||
Projectile separation = vfxData.SpawnVFX(vfxName, player, hitPosition, Quaternion.identity).GetComponentInChildren<Projectile>();
|
||||
nearbyEnemies.TryGetRandom(out Enemy randomTarget);
|
||||
Vector3 sepDirection = (randomTarget.centerPoint.position - hitPosition).normalized;
|
||||
separation.Initialize(player, this, false, 1, Fraction.Enemy)
|
||||
.SetAttackSubmodule<Projectile>(attackUnit)
|
||||
.SetTimeSubmodule<Projectile>(10f)
|
||||
.SetHitSubmodule<Projectile>()
|
||||
.SetLinearDirectionMoveModule<Projectile>(sepDirection, speed, 5f);
|
||||
separation.tags.Add("Separation");
|
||||
separation.hitSm.AddCheckedObject(enemy.gameObject);
|
||||
separation.SetRaycastSubmodule<Projectile>(default, 0.25f, 0.5f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -177,20 +197,26 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
vfxData.SpawnMuzzleVFX(vfxName, player, muzzle);
|
||||
NormalArea area = vfxData.SpawnVFX(vfxName, player).GetComponentInChildren<NormalArea>();
|
||||
|
||||
area.Initialize<NormalArea>(player, this, Fraction.Enemy)
|
||||
.SetAttackSubmodule<NormalArea>(attackUnit)
|
||||
.SetTimeSubmodule<NormalArea>(3f, 0.2f, 0.8f)
|
||||
.SetHitSubmodule<NormalArea>(0.1f, 4);
|
||||
area.SetImpulseSubmodule().WithSuction(4f);
|
||||
area.hitSm.AddHitSound(AK.EVENTS.FUTUREWAND_WEAKATTACKHIT);
|
||||
AudioManager.Post(AK.EVENTS.FUTUREWAND_GROUNDAREA, area.gameObject);
|
||||
|
||||
if (HasExtender<BlackHoleDisplacer>()) //扩展器测试
|
||||
if (!HasExtender<BlackHoleDisplacer>())
|
||||
{
|
||||
area.SetLinearDirectionMoveModule<NormalArea>(player.transform.forward, 0f, 25f, false, true, false);
|
||||
area.Initialize<NormalArea>(player, this, Fraction.Enemy)
|
||||
.SetAttackSubmodule<NormalArea>(attackUnit)
|
||||
.SetTimeSubmodule<NormalArea>(3f, 0.2f, 0.8f)
|
||||
.SetHitSubmodule<NormalArea>(0.1f, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
area.Initialize<NormalArea>(player, this, Fraction.Enemy)
|
||||
.SetAttackSubmodule<NormalArea>(attackUnit)
|
||||
.SetTimeSubmodule<NormalArea>(3f, 0.1f, 0.9f)
|
||||
.SetHitSubmodule<NormalArea>(0.1f, 8)
|
||||
.SetLinearDirectionMoveModule<NormalArea>(player.transform.forward, 25f, -50f, false, false, false);
|
||||
}
|
||||
|
||||
area.SetImpulseSubmodule().WithSuction(4f);
|
||||
area.hitSm.AddHitSound(AK.EVENTS.FUTUREWAND_WEAKATTACKHIT);
|
||||
int fusionStack = attackUnit.GetSubmodule<AttackUnit.ParameterSubmodule>().GetParameter<int>("Buff_Fusion_Stack");
|
||||
area.hitSm.AddHitEvent((enemy, hitPosition) =>
|
||||
{
|
||||
|
||||
@@ -152,12 +152,12 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
return;
|
||||
}
|
||||
|
||||
List<CharacterBase> availableEnemies = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 4);
|
||||
List<Enemy> availableEnemies = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 4);
|
||||
|
||||
//完美格挡+反击
|
||||
if (player.reactionSc.blockSm.afterPerfectBlockTimer > 0)
|
||||
{
|
||||
CharacterBase target = CombatManager.EnemySm.GetBestEnemy(availableEnemies);
|
||||
Enemy target = CombatManager.EnemySm.GetBestEnemy(availableEnemies);
|
||||
if (PlayTargetedAnimation("BlockParryAttack", target))
|
||||
{
|
||||
player.reactionSc.blockSm.afterPerfectBlockTimer = 0;
|
||||
@@ -212,8 +212,8 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
_ => "A"
|
||||
};
|
||||
|
||||
List<CharacterBase> disruptable = CombatManager.EnemySm.GetDisruptableEnemies(availableEnemies);
|
||||
CharacterBase target = CombatManager.EnemySm.GetScoredEnemies(availableEnemies)
|
||||
List<Enemy> disruptable = CombatManager.EnemySm.GetDisruptableEnemies(availableEnemies);
|
||||
Enemy target = CombatManager.EnemySm.GetScoredEnemies(availableEnemies)
|
||||
.ApplyScoreModifier(disruptable, 0f, 1f).BestEnemy();
|
||||
|
||||
if (PlayTargetedAnimation("DisruptionAttack" + suffix, target))
|
||||
@@ -247,7 +247,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
|
||||
if (functionSm["HeavyAttack"].IsAvailable())
|
||||
{
|
||||
CharacterBase target = CombatManager.EnemySm.GetBestEnemy(availableEnemies);
|
||||
Enemy target = CombatManager.EnemySm.GetBestEnemy(availableEnemies);
|
||||
string nextNodeName = comboSm.main.GetNextNodeName("R");
|
||||
bool keepAdsorption = nextNodeName is "RC";
|
||||
if (PlayTargetedAnimation("Attack" + nextNodeName, target, 1f, keepAdsorption))
|
||||
@@ -296,11 +296,11 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
player.reactionSc.blockSm.GetCurrentBlockSource().PerfectBlock(null, player.centerPosition);
|
||||
RemoveBlock();*/
|
||||
|
||||
/*player.operationSc.Dodge();
|
||||
player.operationSc.Dodge();
|
||||
DodgeSource defaultDodge = DodgeSource.Default(player);
|
||||
player.reactionSc.dodgeSm.ApplyDodge(defaultDodge);
|
||||
player.reactionSc.dodgeSm.GetCurrentDodgeSource().PerfectDodge();
|
||||
player.reactionSc.dodgeSm.RemoveDodge("DefaultDodge");*/
|
||||
player.reactionSc.dodgeSm.RemoveDodge("DefaultDodge");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,8 +319,8 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
|
||||
private void FAPF_GenerateAirNormalSlash(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
CustomFunction.PC_StringString p = rtFuncAnim.GetParams<CustomFunction.PC_StringString>();
|
||||
NormalArea slash = GenerateNormalSlash(p.str0, attackData[p.str1], "SingleNormalHit");
|
||||
CustomFunction.PC_StringString p = rtFuncAnim.GetParams<CustomFunction.PC_StringString>();
|
||||
GenerateNormalSlash(p.str0, attackData[p.str1], "SingleNormalHit");
|
||||
}
|
||||
|
||||
private void FAPF_GenerateHeavySlash(RuntimeFuncAnim rtFuncAnim)
|
||||
@@ -459,8 +459,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
AudioManager.Post(AK.EVENTS.DISRUPT, hitPosition);
|
||||
};
|
||||
|
||||
slash.hitSm
|
||||
.AddHitSound(AK.EVENTS.POLYCHROME_HEAVYATTACKLHIT)
|
||||
slash.hitSm.AddHitSound(AK.EVENTS.POLYCHROME_HEAVYATTACKLHIT)
|
||||
.AddHitEvent((enemy, hitPosition) =>
|
||||
{
|
||||
var positionShakeAction = feedbackSc.GetFeedbackData("HeavyHit").Action<CameraPositionShakeAction>("Camera");
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.MainGame.Buffs.Character;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using SLSUtilities.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 衰变加速线圈 / Decay Acceleration Coil (Passive, Graham)
|
||||
/// 玩家光环范围(15m)内的敌人,其 Decay 伤害间隔缩短 50%。离开光环后恢复。
|
||||
/// </summary>
|
||||
public class DecayAccelerationCoil : PassiveEquipmentBase
|
||||
{
|
||||
private const string EventKey = nameof(DecayAccelerationCoil);
|
||||
|
||||
/// <summary>
|
||||
/// 记录已被修改 Decay 间隔的敌人及其原始间隔值,用于离开光环时恢复。
|
||||
/// </summary>
|
||||
private readonly Dictionary<CharacterBase, float> _modifiedEnemies = new();
|
||||
|
||||
public override void OnObtained()
|
||||
{
|
||||
base.OnObtained();
|
||||
|
||||
auraSm = new AuraSubmodule(this, passiveAttributeSm.GetItemAttribute("AuraRadius"));
|
||||
|
||||
Action<CharacterBase> onEnter = OnEnemyEnterAura;
|
||||
Action<CharacterBase> onExit = OnEnemyExitAura;
|
||||
Action<CharacterBase> onStay = OnEnemyStayAura;
|
||||
|
||||
auraSm.onOtherEnterAura.Add(EventKey, onEnter.ToPrioritized());
|
||||
auraSm.onOtherExitAura.Add(EventKey, onExit.ToPrioritized());
|
||||
auraSm.onOtherStayAura.Add(EventKey, onStay.ToPrioritized());
|
||||
}
|
||||
|
||||
public override void OnDiscarded()
|
||||
{
|
||||
// 恢复所有被修改的 Decay 间隔
|
||||
auraSm?.Dispose();
|
||||
_modifiedEnemies.Clear();
|
||||
|
||||
base.OnDiscarded();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
auraSm?.Update(player.selfTimeSm.DeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 敌人进入光环时,尝试加速其 Decay 间隔。
|
||||
/// </summary>
|
||||
private void OnEnemyEnterAura(CharacterBase enemy)
|
||||
{
|
||||
TryAccelerateDecay(enemy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 敌人持续处于光环内时,检查是否有新施加的 Decay 需要加速。
|
||||
/// </summary>
|
||||
private void OnEnemyStayAura(CharacterBase enemy)
|
||||
{
|
||||
if (_modifiedEnemies.ContainsKey(enemy)) return;
|
||||
TryAccelerateDecay(enemy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 敌人离开光环时,恢复其 Decay 间隔。
|
||||
/// </summary>
|
||||
private void OnEnemyExitAura(CharacterBase enemy)
|
||||
{
|
||||
RestoreDecayInterval(enemy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试加速目标身上的 Decay 伤害间隔。
|
||||
/// </summary>
|
||||
private void TryAccelerateDecay(CharacterBase enemy)
|
||||
{
|
||||
if (!enemy.buffSm.TryGetBuff(out Decay decay)) return;
|
||||
if (_modifiedEnemies.ContainsKey(enemy)) return;
|
||||
|
||||
_modifiedEnemies[enemy] = decay.Interval;
|
||||
float acceleratedInterval = decay.Interval * passiveAttributeSm.GetItemAttribute("IntervalMultiplier");
|
||||
decay.timeSubmodule.intervalActions[0].SetInterval(acceleratedInterval);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复目标身上的 Decay 伤害间隔至原始值。
|
||||
/// </summary>
|
||||
private void RestoreDecayInterval(CharacterBase enemy)
|
||||
{
|
||||
if (!_modifiedEnemies.Remove(enemy, out float originalInterval)) return;
|
||||
if (!enemy.buffSm.TryGetBuff(out Decay decay)) return;
|
||||
decay.timeSubmodule.intervalActions[0].SetInterval(originalInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42c37456513125040b6ec211c2472168
|
||||
@@ -43,7 +43,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
private void SpreadDecay(Fusion fusion, AttackAreaBase attackArea)
|
||||
{
|
||||
CharacterBase target = fusion.attachedCharacter;
|
||||
List<CharacterBase> affectedTargets = CombatManager.EnemySm.GetEnemiesInRadius(target.centerPosition, 10f);
|
||||
List<Enemy> affectedTargets = CombatManager.EnemySm.GetEnemiesInRadius(target.centerPosition, 10f);
|
||||
float decayDamage = passiveAttributeSm.GetItemAttribute("DecayDamage");
|
||||
affectedTargets.ForEach(t => new Decay(decayDamage).Apply(t));
|
||||
}
|
||||
|
||||
154
Assets/Scripts/MainGame/Items/Submodules/AuraSubmodule.cs
Normal file
154
Assets/Scripts/MainGame/Items/Submodules/AuraSubmodule.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using SLSUtilities.General;
|
||||
using SoftCircuits.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory
|
||||
{
|
||||
/// <summary>
|
||||
/// 光环子模块。持续捕获指定半径内的角色,并通过 Enter / Stay / Exit 事件通知订阅方。
|
||||
/// 不使用 Data ScriptableObject,由道具在代码中直接构造。
|
||||
/// </summary>
|
||||
public class AuraSubmodule : SubmoduleBase<ItemBase>
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前处于光环内的角色集合。
|
||||
/// </summary>
|
||||
public HashSet<CharacterBase> charactersInAura = new HashSet<CharacterBase>();
|
||||
|
||||
/// <summary>
|
||||
/// 其他角色进入光环时触发,参数为进入的角色。
|
||||
/// </summary>
|
||||
public OrderedDictionary<string, PrioritizedAction<CharacterBase>> onOtherEnterAura = new();
|
||||
|
||||
/// <summary>
|
||||
/// 其他角色持续处于光环内时触发(按 stayInterval 间隔),参数为在光环内的角色。
|
||||
/// </summary>
|
||||
public OrderedDictionary<string, PrioritizedAction<CharacterBase>> onOtherStayAura = new();
|
||||
|
||||
/// <summary>
|
||||
/// 其他角色离开光环时触发,参数为离开的角色。
|
||||
/// </summary>
|
||||
public OrderedDictionary<string, PrioritizedAction<CharacterBase>> onOtherExitAura = new();
|
||||
|
||||
/// <summary>
|
||||
/// 光环半径。
|
||||
/// </summary>
|
||||
public float auraRadius;
|
||||
|
||||
/// <summary>
|
||||
/// Stay 计时器,到达 stayInterval 后对所有在光环内的角色触发 onOtherStayAura。
|
||||
/// </summary>
|
||||
public Timer stayTimer;
|
||||
|
||||
/// <summary>
|
||||
/// 物理检测的 LayerMask。
|
||||
/// </summary>
|
||||
private readonly int _detectionLayerMask;
|
||||
|
||||
private const int MaxDetectionCount = 32;
|
||||
private readonly Collider[] _overlapBuffer = new Collider[MaxDetectionCount];
|
||||
|
||||
/// <summary>
|
||||
/// 用于比较前后帧角色集合的临时缓冲。
|
||||
/// </summary>
|
||||
private readonly HashSet<CharacterBase> _currentFrameCharacters = new HashSet<CharacterBase>();
|
||||
|
||||
/// <summary>
|
||||
/// 构造光环子模块。
|
||||
/// </summary>
|
||||
/// <param name="owner">所属道具。</param>
|
||||
/// <param name="auraRadius">光环半径。</param>
|
||||
/// <param name="stayInterval">Stay 事件触发间隔(秒),默认 0.5 秒。</param>
|
||||
public AuraSubmodule(ItemBase owner, float auraRadius, float stayInterval = 0.5f) : base(owner)
|
||||
{
|
||||
this.auraRadius = auraRadius;
|
||||
|
||||
stayTimer = new Timer(stayInterval, isInfinite: true);
|
||||
stayTimer.onComplete.Add(new PrioritizedAction(OnStayTimerComplete));
|
||||
|
||||
_detectionLayerMask = LayerMask.GetMask("HurtBox");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每帧调用,驱动物理检测和事件派发。
|
||||
/// </summary>
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
DetectCharacters();
|
||||
stayTimer.Update(deltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有追踪状态,对仍在光环内的角色触发 Exit 事件。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var character in charactersInAura)
|
||||
{
|
||||
if (character != null)
|
||||
{
|
||||
onOtherExitAura.Invoke(character);
|
||||
}
|
||||
}
|
||||
|
||||
charactersInAura.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行球形物理检测,比较前后帧角色集合,触发 Enter / Exit 事件。
|
||||
/// </summary>
|
||||
private void DetectCharacters()
|
||||
{
|
||||
_currentFrameCharacters.Clear();
|
||||
|
||||
Vector3 center = owner.player.transform.position;
|
||||
int hitCount = Physics.OverlapSphereNonAlloc(center, auraRadius, _overlapBuffer, _detectionLayerMask);
|
||||
|
||||
for (int i = 0; i < hitCount; i++)
|
||||
{
|
||||
CharacterBase character = _overlapBuffer[i].GetComponentInParent<CharacterBase>();
|
||||
if (character == null || character == owner.player) continue;
|
||||
|
||||
_currentFrameCharacters.Add(character);
|
||||
}
|
||||
|
||||
// 检测新进入光环的角色
|
||||
foreach (var character in _currentFrameCharacters)
|
||||
{
|
||||
if (charactersInAura.Add(character))
|
||||
{
|
||||
onOtherEnterAura.Invoke(character);
|
||||
}
|
||||
}
|
||||
|
||||
// 检测离开光环的角色(从旧集合中移除不在当前帧的角色)
|
||||
charactersInAura.RemoveWhere(character =>
|
||||
{
|
||||
if (_currentFrameCharacters.Contains(character)) return false;
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
onOtherExitAura.Invoke(character);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stay 计时器到期时,对所有在光环内的角色触发 onOtherStayAura。
|
||||
/// </summary>
|
||||
private void OnStayTimerComplete()
|
||||
{
|
||||
foreach (var character in charactersInAura)
|
||||
{
|
||||
if (character != null)
|
||||
{
|
||||
onOtherStayAura.Invoke(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84bc61c526c4a824a9267b1a5c2e3ff4
|
||||
@@ -27,7 +27,7 @@ namespace Cielonos.MainGame
|
||||
//audioContainer.PlaySoundFX("Scan", scanOrigin.gameObject);
|
||||
scannerEffect.StartScan(1);
|
||||
|
||||
List<CharacterBase> enemies = CombatManager.EnemySm.GetEnemiesInRadius(scanOrigin.position, 50f);
|
||||
List<Enemy> enemies = CombatManager.EnemySm.GetEnemiesInRadius(scanOrigin.position, 50f);
|
||||
enemies.ForEach(enemy => new Scanner_WeaknessDetection().Apply(enemy));
|
||||
|
||||
functionSm["Scan"].Execute();
|
||||
|
||||
Reference in New Issue
Block a user