UI调整
This commit is contained in:
@@ -146,7 +146,7 @@ namespace Cielonos.MainGame
|
||||
if (hasPerfectDodge && owner.timeSm.enablingTimer <= 0.2f && characterDodgeSm.isPerfectDodging)
|
||||
{
|
||||
firstDodgeSource = characterDodgeSm.dodgeSources.Find(source => source.isDuringPerfectDodge);
|
||||
firstDodgeSource.PerfectDodge();
|
||||
firstDodgeSource.PerfectDodge(owner);
|
||||
perfectDodgeAction?.Invoke(dodger);
|
||||
|
||||
// 触发完美闪避成功事件
|
||||
@@ -155,7 +155,7 @@ namespace Cielonos.MainGame
|
||||
else
|
||||
{
|
||||
firstDodgeSource = characterDodgeSm.dodgeSources[0];
|
||||
firstDodgeSource.NormalDodge();
|
||||
firstDodgeSource.NormalDodge(owner);
|
||||
normalDodgeAction?.Invoke(dodger);
|
||||
|
||||
// 触发普通闪避成功事件
|
||||
|
||||
@@ -176,12 +176,18 @@ namespace Cielonos.MainGame.Buffs
|
||||
timeLeft = duration;
|
||||
}
|
||||
|
||||
public void StackDuration(TimeSubmodule other)
|
||||
public void AddDuration(TimeSubmodule other)
|
||||
{
|
||||
duration += other.duration;
|
||||
timeLeft += other.duration;
|
||||
}
|
||||
|
||||
public void AddDuration(float additionalDuration)
|
||||
{
|
||||
duration += additionalDuration;
|
||||
timeLeft += additionalDuration;
|
||||
}
|
||||
|
||||
|
||||
public class IntervalAction
|
||||
{
|
||||
|
||||
@@ -18,13 +18,21 @@ namespace Cielonos.MainGame.Characters
|
||||
[ShowInInspector]
|
||||
public bool isPerfectBlocking => blockSources.Any(source => source.hasPerfectBlock && source.isDuringPerfectBlock);
|
||||
|
||||
public float afterPerfectBlockTimer;
|
||||
public float afterNormalBlockTimer;
|
||||
public Timer afterPerfectBlockTimer;
|
||||
public CharacterBase perfectBlockedTarget;
|
||||
|
||||
public Timer afterNormalBlockTimer;
|
||||
public CharacterBase normalBlockedTarget;
|
||||
|
||||
public BlockSubmodule(ReactionSubcontroller owner) : base(owner)
|
||||
{
|
||||
blockSources = new List<BlockSource>();
|
||||
canBlock = true;
|
||||
|
||||
this.afterPerfectBlockTimer = new Timer(1);
|
||||
this.afterPerfectBlockTimer.onComplete.Add(new PrioritizedAction(() => perfectBlockedTarget = null));
|
||||
this.afterNormalBlockTimer = new Timer(1);
|
||||
this.afterPerfectBlockTimer.onComplete.Add(new PrioritizedAction(() => normalBlockedTarget = null));
|
||||
}
|
||||
|
||||
public void ApplyBlock(BlockSource source, bool refreshPerfect = false)
|
||||
@@ -99,8 +107,8 @@ namespace Cielonos.MainGame.Characters
|
||||
blockSources.RemoveAll(source => source.blockTime <= 0);
|
||||
}
|
||||
|
||||
afterPerfectBlockTimer -= owner.owner.selfTimeSm.DeltaTime;
|
||||
afterNormalBlockTimer -= owner.owner.selfTimeSm.DeltaTime;
|
||||
afterPerfectBlockTimer.Update(owner.owner.selfTimeSm.DeltaTime);
|
||||
afterNormalBlockTimer.Update(owner.owner.selfTimeSm.DeltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,11 +129,13 @@ namespace Cielonos.MainGame.Characters
|
||||
public GameObject perfectBlockEffect;
|
||||
public uint perfectBlockSound;
|
||||
public Action<AttackAreaBase> onPerfectBlock;
|
||||
|
||||
|
||||
public Breakthrough.Type normalBlockType;
|
||||
public GameObject normalBlockEffect;
|
||||
public uint normalBlockSound;
|
||||
public Action<AttackAreaBase> onNormalBlock;
|
||||
|
||||
|
||||
public float triggerTime;
|
||||
|
||||
@@ -173,11 +183,11 @@ namespace Cielonos.MainGame.Characters
|
||||
}
|
||||
|
||||
VFXObject.Spawn(perfectBlockEffect, sourceCharacter, blockEffectPosition, Quaternion.identity);
|
||||
//pObj.GetComponent<ParticleSystem>().Simulate(0.1f, true, true);
|
||||
//pObj.GetComponent<ParticleSystem>().Play();//TODO: 增加起点时间参数
|
||||
|
||||
sourceCharacter.reactionSc.blockSm.afterPerfectBlockTimer = blockBufferTime + 0.25f;
|
||||
onPerfectBlock?.Invoke(attackArea);
|
||||
|
||||
BlockSubmodule blockSm = sourceCharacter.reactionSc.blockSm;
|
||||
blockSm.perfectBlockedTarget = attackArea.creator;
|
||||
blockSm.afterPerfectBlockTimer.Reset(blockBufferTime + 0.25f);
|
||||
}
|
||||
|
||||
public void NormalBlock(AttackAreaBase attackArea, Vector3 blockEffectPosition)
|
||||
@@ -192,8 +202,11 @@ namespace Cielonos.MainGame.Characters
|
||||
}
|
||||
|
||||
VFXObject.Spawn(normalBlockEffect, sourceCharacter, blockEffectPosition, Quaternion.identity);
|
||||
sourceCharacter.reactionSc.blockSm.afterNormalBlockTimer = blockBufferTime + 0.25f;
|
||||
onNormalBlock?.Invoke(attackArea);
|
||||
|
||||
BlockSubmodule blockSm = sourceCharacter.reactionSc.blockSm;
|
||||
blockSm.normalBlockedTarget = attackArea.creator;
|
||||
blockSm.afterPerfectBlockTimer.Reset(blockBufferTime + 0.25f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,18 +12,25 @@ namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
public List<DodgeSource> dodgeSources;
|
||||
public bool canDodge;
|
||||
|
||||
[ShowInInspector]
|
||||
public bool isDodging => dodgeSources.Count > 0;
|
||||
public Timer afterNormalDodgeTimer;
|
||||
public CharacterBase normalDodgedTarget;
|
||||
|
||||
[ShowInInspector]
|
||||
public bool isPerfectDodging => dodgeSources.Any(source => source.hasPerfectDodge && source.isDuringPerfectDodge);
|
||||
|
||||
public float afterPerfectDodgeTimer;
|
||||
public float afterNormalDodgeTimer;
|
||||
public Timer afterPerfectDodgeTimer;
|
||||
public CharacterBase perfectDodgedTarget;
|
||||
|
||||
public DodgeSubmodule(ReactionSubcontroller owner) : base(owner)
|
||||
{
|
||||
dodgeSources = new List<DodgeSource>();
|
||||
canDodge = true;
|
||||
this.afterNormalDodgeTimer = new Timer(1f);
|
||||
this.afterNormalDodgeTimer.onComplete.Add(new PrioritizedAction(() => normalDodgedTarget = null));
|
||||
this.afterPerfectDodgeTimer = new Timer(1f);
|
||||
this.afterPerfectDodgeTimer.onComplete.Add(new PrioritizedAction(() => perfectDodgedTarget = null));
|
||||
}
|
||||
|
||||
public void ApplyDodge(DodgeSource source, bool refreshPerfect = false)
|
||||
@@ -92,8 +99,8 @@ namespace Cielonos.MainGame.Characters
|
||||
dodgeSources.RemoveAll(source => source.dodgeTime <= 0);
|
||||
}
|
||||
|
||||
afterPerfectDodgeTimer -= owner.owner.selfTimeSm.DeltaTime;
|
||||
afterNormalDodgeTimer -= owner.owner.selfTimeSm.DeltaTime;
|
||||
afterPerfectDodgeTimer.Update(owner.owner.selfTimeSm.DeltaTime);
|
||||
afterNormalDodgeTimer.Update(owner.owner.selfTimeSm.DeltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,8 +116,10 @@ namespace Cielonos.MainGame.Characters
|
||||
public bool isDuringPerfectDodge;
|
||||
public float perfectTime;
|
||||
public float dodgeTime;
|
||||
|
||||
public string perfectEffectName;
|
||||
public Action onPerfectDodge;
|
||||
|
||||
public string normalEffectName;
|
||||
public Action onNormalDodge;
|
||||
|
||||
@@ -144,6 +153,7 @@ namespace Cielonos.MainGame.Characters
|
||||
this.perfectTime = perfectTime;
|
||||
this.hasPerfectDodge = true;
|
||||
this.isDuringPerfectDodge = true;
|
||||
|
||||
|
||||
this._isTriggered = false;
|
||||
}
|
||||
@@ -164,7 +174,7 @@ namespace Cielonos.MainGame.Characters
|
||||
return defaultDodge;
|
||||
}
|
||||
|
||||
public void PerfectDodge()
|
||||
public void PerfectDodge(AttackAreaBase attackArea)
|
||||
{
|
||||
if(_isTriggered) return;
|
||||
_isTriggered = true;
|
||||
@@ -176,10 +186,13 @@ namespace Cielonos.MainGame.Characters
|
||||
sourceCharacter.feedbackSc.PlayFeedback("PerfectDodge");
|
||||
}
|
||||
|
||||
sourceCharacter.reactionSc.dodgeSm.afterPerfectDodgeTimer = dodgeBufferTime + 0.25f;
|
||||
DodgeSubmodule dodgeSm = sourceCharacter.reactionSc.dodgeSm;
|
||||
dodgeSm.perfectDodgedTarget = attackArea?.creator;
|
||||
dodgeSm.afterPerfectDodgeTimer.Reset(dodgeBufferTime + 0.5f);
|
||||
Debug.Log($"Perfect Dodge triggered by {sourceCharacter.name} against {attackArea?.creator?.name}");
|
||||
}
|
||||
|
||||
public void NormalDodge()
|
||||
public void NormalDodge(AttackAreaBase attackArea)
|
||||
{
|
||||
if(_isTriggered) return;
|
||||
_isTriggered = true;
|
||||
@@ -191,7 +204,9 @@ namespace Cielonos.MainGame.Characters
|
||||
sourceCharacter.feedbackSc.PlayFeedback("NormalDodge");
|
||||
}
|
||||
|
||||
sourceCharacter.reactionSc.dodgeSm.afterNormalDodgeTimer = dodgeBufferTime + 0.25f;
|
||||
DodgeSubmodule dodgeSm = sourceCharacter.reactionSc.dodgeSm;
|
||||
dodgeSm.normalDodgedTarget = attackArea?.creator;
|
||||
dodgeSm.afterNormalDodgeTimer.Reset(dodgeBufferTime + 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,6 +166,8 @@ namespace Cielonos.MainGame.Characters
|
||||
|
||||
public void SpawnShatters(Breakthrough.Type type, Vector3 direction)
|
||||
{
|
||||
if(particleEffects["Shatters"] == null) return;
|
||||
|
||||
Color shatterColor = MainGameManager.BaseCollection.outlineColorCollection[type] * Mathf.Pow(2, 4);
|
||||
var main = particleEffects["Shatters"].main;
|
||||
main.startColor = shatterColor;
|
||||
|
||||
@@ -280,6 +280,7 @@ namespace Cielonos.MainGame.Characters
|
||||
/// <param name="newDuration"></param>
|
||||
public override void Reset(float newDuration = -1f)
|
||||
{
|
||||
isCompleted = false;
|
||||
currentTime = 0f;
|
||||
duration = newDuration >= 0f ? newDuration : originalDuration;
|
||||
}
|
||||
|
||||
@@ -27,9 +27,8 @@ namespace Cielonos.MainGame.Characters
|
||||
backpackSm ??= new BackpackSubmodule(this);
|
||||
equipmentSm ??= new EquipmentSubmodule(this);
|
||||
|
||||
|
||||
backpackSm.ObtainItem<Polychrome>();
|
||||
//backpackSm.ObtainItem<DualHarmony>();
|
||||
backpackSm.ObtainItem<Polychrome>();
|
||||
backpackSm.ObtainItem<FutureWand>();
|
||||
backpackSm.ObtainItem<Ascension>();
|
||||
backpackSm.ObtainItem<BlackHoleDisplacer>();
|
||||
@@ -37,6 +36,8 @@ namespace Cielonos.MainGame.Characters
|
||||
backpackSm.ObtainItem<DecayAccelerationCoil>();
|
||||
backpackSm.ObtainItem<FusionInjector>();
|
||||
backpackSm.ObtainItem<MissileSeparationMembrane>();
|
||||
backpackSm.ObtainItem<PhotonPolarizer>();
|
||||
backpackSm.ObtainItem<PhotonDissociator>();
|
||||
|
||||
foreach (MainWeaponBase mainWeapon in backpackSm.mainWeapons)
|
||||
{
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Cielonos.MainGame
|
||||
|
||||
// 7. 淡入画面
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
ScreenFader.Instance.FadeToClear().Play();
|
||||
ScreenFader.Instance?.FadeToClear().Play();
|
||||
|
||||
// 8. 恢复玩家输入
|
||||
playerInput.inputActions.Player.Enable();
|
||||
|
||||
@@ -1,62 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.General;
|
||||
using SoftCircuits.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory
|
||||
{
|
||||
[CreateAssetMenu(fileName = "FunctionData", menuName = "Cielonos/Items/FunctionData")]
|
||||
public partial class FunctionData : SerializedScriptableObject
|
||||
{
|
||||
[DictionaryDrawerSettings(KeyLabel = "Function Unit", DisplayMode = DictionaryDisplayOptions.ExpandedFoldout)]
|
||||
[ListDrawerSettings(ShowIndexLabels = false, ListElementLabelName = "unitName", CustomAddFunction = "AddFunctionUnit")]
|
||||
[Searchable]
|
||||
public Dictionary<string, FunctionUnit> functionUnits = new Dictionary<string, FunctionUnit>();
|
||||
public List<FunctionUnit> functionUnitList = new List<FunctionUnit>();
|
||||
|
||||
[OnInspectorGUI("UpdateUnits")]
|
||||
public void UpdateUnits()
|
||||
{
|
||||
foreach (var unit in functionUnits.Values)
|
||||
foreach (var unit in functionUnitList)
|
||||
{
|
||||
unit.parentData = this;
|
||||
}
|
||||
}
|
||||
|
||||
private FunctionUnit AddFunctionUnit()
|
||||
{
|
||||
FunctionUnit newUnit = new FunctionUnit
|
||||
{
|
||||
unitName = $"Function {functionUnitList.Count + 1}",
|
||||
parentData = this
|
||||
};
|
||||
return newUnit;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class FunctionData
|
||||
{
|
||||
public enum IntervalReductionType
|
||||
public enum CooldownReductionType
|
||||
{
|
||||
None = 0,
|
||||
Cooldown = 1,
|
||||
AttackSpeed = 10
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class FunctionUnit
|
||||
{
|
||||
[ReadOnly]
|
||||
public FunctionData parentData;
|
||||
|
||||
|
||||
[TitleGroup("Information")]
|
||||
public string unitName;
|
||||
[TitleGroup("Information")]
|
||||
public bool shownInUI;
|
||||
[TitleGroup("Information")]
|
||||
[HideIf("shownInUI")]
|
||||
public bool isMain;
|
||||
[TitleGroup("Information")]
|
||||
[ShowIf("@shownInUI || isMain")]
|
||||
public Sprite icon;
|
||||
[TitleGroup("Information")]
|
||||
public List<string> operation;
|
||||
public List<string> operation = new List<string>();
|
||||
[TitleGroup("Information")]
|
||||
[ValueDropdown("Tags")]
|
||||
public List<string> tags = new List<string>();
|
||||
|
||||
[TitleGroup("Costs")]
|
||||
public float energyCost;
|
||||
[TitleGroup("Costs")]
|
||||
public int ammoCost;
|
||||
|
||||
[TitleGroup("Interval")]
|
||||
public float interval;
|
||||
[TitleGroup("Interval")]
|
||||
public float intervalLowerLimit;
|
||||
[TitleGroup("Interval")]
|
||||
public IntervalReductionType intervalReductionType;
|
||||
[FormerlySerializedAs("interval")] [TitleGroup("Cooldown")]
|
||||
public float cooldown;
|
||||
[FormerlySerializedAs("intervalLowerLimit")] [TitleGroup("Cooldown")]
|
||||
public float cooldownLowerLimit;
|
||||
[FormerlySerializedAs("intervalReductionType")] [TitleGroup("Cooldown")]
|
||||
public CooldownReductionType cooldownReductionType;
|
||||
|
||||
public static List<string> Tags = new()
|
||||
{
|
||||
"Disruption"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Assets/Scripts/MainGame/Items/Extenders/Polychrome.meta
Normal file
8
Assets/Scripts/MainGame/Items/Extenders/Polychrome.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9eeb85c7bb082046b999e5e098bca4e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 光子解离器 / Photon Dissociator
|
||||
/// Polychrome的扩展器,重攻击拆分为3段,每段伤害为原来的50%。
|
||||
/// </summary>
|
||||
public class PhotonDissociator : ExtenderBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfcb853ac3666fc45a65e43107958eaf
|
||||
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 光子偏振器 / Photon Polarizer
|
||||
/// Polychrome的扩展器,可以使完美格挡弹开敌人,阻止敌人的连招
|
||||
/// </summary>
|
||||
public class PhotonPolarizer : ExtenderBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6b2bf502e7cd5c4ca858a7492f332a9
|
||||
@@ -20,7 +20,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
RegisterFunctionsToAnimSc();
|
||||
if(!player.inputSc.IsMoving) PlayTargetedAnimation("Equip");
|
||||
viewObjects["Wand"].SetFadeAnim(0.5f);
|
||||
PlayerCanvas.MainWeaponUIArea.displayer.SetFrameOutline(1);
|
||||
//PlayerCanvas.MainWeaponUIArea.displayer.SetFrameOutline(1);
|
||||
}
|
||||
|
||||
public override void OnPrimaryPress()
|
||||
|
||||
@@ -4,6 +4,7 @@ using Cielonos.MainGame.Buffs.Character;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using Cielonos.MainGame.Effects.Feedback;
|
||||
using Cielonos.MainGame.UI;
|
||||
using SLSUtilities.Feedback;
|
||||
using SLSUtilities.General;
|
||||
using SLSUtilities.FunctionalAnimation;
|
||||
using SLSUtilities.WwiseAssistance;
|
||||
@@ -51,8 +52,6 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
viewObjects["Katana"].SetFadeAnim(0.2f);
|
||||
viewObjects["Saya"].SetFadeAnim(0.2f);
|
||||
|
||||
PlayerCanvas.MainWeaponUIArea.displayer.SetFrameOutline(0.4f);
|
||||
|
||||
PlayTargetedAnimation("EquipBlock");
|
||||
SetBlock(equipBlockData);
|
||||
player.selfTimeSm.AddLocalTimer(0.4f, () =>
|
||||
@@ -105,7 +104,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.inputSc.IsHoldingSpecialA && functionSm["LightAttack"].IsAvailable())
|
||||
/*if (player.inputSc.IsHoldingSpecialA && functionSm["LightAttack"].IsAvailable())
|
||||
{
|
||||
CharacterBase target = CombatManager.EnemySm.GetBestEnemy(5);
|
||||
if (PlayTargetedAnimation("AttackRC", target, 1f, true))
|
||||
@@ -114,7 +113,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
functionSm["LightAttack"].Execute();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (player.landMovementSc.isSprinting && functionSm["LightAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
@@ -153,30 +152,48 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
}
|
||||
|
||||
List<Enemy> availableEnemies = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 4);
|
||||
|
||||
//完美格挡+反击
|
||||
if (player.reactionSc.blockSm.afterPerfectBlockTimer > 0)
|
||||
BlockSubmodule blockSm = player.reactionSc.blockSm;
|
||||
if (!blockSm.afterPerfectBlockTimer.isCompleted)
|
||||
{
|
||||
Enemy target = CombatManager.EnemySm.GetBestEnemy(availableEnemies);
|
||||
if (PlayTargetedAnimation("BlockParryAttack", target))
|
||||
Enemy blockedTarget = blockSm.perfectBlockedTarget as Enemy;
|
||||
bool successPlayed;
|
||||
if (blockedTarget != null && !availableEnemies.Contains(blockedTarget))
|
||||
{
|
||||
player.reactionSc.blockSm.afterPerfectBlockTimer = 0;
|
||||
RemoveBlock();
|
||||
Debug.Log(blockedTarget.name);
|
||||
float distance = Vector3.Distance(player.transform.position.Flatten(), blockedTarget.transform.position.Flatten());
|
||||
successPlayed = PlayTargetedAnimation(distance > 2f ? "DodgeParryAttack" : "BlockParryAttack", blockedTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
successPlayed = PlayTargetedAnimation("BlockParryAttack");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//完美闪避+反击
|
||||
if (player.reactionSc.dodgeSm.afterPerfectDodgeTimer > 0)
|
||||
{
|
||||
CharacterBase target = CombatManager.EnemySm.GetBestEnemy(12);
|
||||
if (PlayTargetedAnimation("DodgeParryAttack", target))
|
||||
if(successPlayed)
|
||||
{
|
||||
player.reactionSc.dodgeSm.afterPerfectDodgeTimer = 0;
|
||||
blockSm.afterPerfectBlockTimer.Complete();
|
||||
RemoveBlock();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
DodgeSubmodule dodgeSm = player.reactionSc.dodgeSm;
|
||||
if (!dodgeSm.afterPerfectDodgeTimer.isCompleted)
|
||||
{
|
||||
Enemy dodgedTarget = dodgeSm.perfectDodgedTarget as Enemy;
|
||||
bool successPlayed;
|
||||
if (dodgedTarget != null && !availableEnemies.Contains(dodgedTarget))
|
||||
{
|
||||
float distance = Vector3.Distance(player.transform.position.Flatten(), dodgedTarget.transform.position.Flatten());
|
||||
successPlayed = PlayTargetedAnimation(distance > 2f ? "DodgeParryAttack" : "BlockParryAttack", dodgedTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
successPlayed = PlayTargetedAnimation("BlockParryAttack");
|
||||
}
|
||||
|
||||
if(successPlayed)
|
||||
{
|
||||
dodgeSm.afterPerfectDodgeTimer.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
if (player.reactionSc.blockSm.HaveBlockSource(blockData.blockName))
|
||||
@@ -202,46 +219,49 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.inputSc.IsHoldingSpecialA && functionSm["HeavyAttack"].IsAvailable())
|
||||
if (player.inputSc.IsHoldingSpecialA)
|
||||
{
|
||||
string suffix = techniqueScore switch
|
||||
if (functionSm["DisruptionAttack"].IsAvailable())
|
||||
{
|
||||
< 1f => "A",
|
||||
< 3f => "B",
|
||||
>= 3f => "C",
|
||||
_ => "A"
|
||||
};
|
||||
|
||||
List<Enemy> disruptable = CombatManager.EnemySm.GetDisruptableEnemies(availableEnemies);
|
||||
Enemy target = CombatManager.EnemySm.GetScoredEnemies(availableEnemies)
|
||||
.ApplyScoreModifier(disruptable, 0f, 1f).BestEnemy();
|
||||
|
||||
if (PlayTargetedAnimation("DisruptionAttack" + suffix, target))
|
||||
{
|
||||
if (disruptable.Count > 0)
|
||||
string suffix = techniqueScore switch
|
||||
{
|
||||
var timeScaleModifierClip = player.feedbackSc
|
||||
.GetFeedbackData("DisruptionStartup")
|
||||
.Clip<TimeScaleModifierAction>("Time");
|
||||
float duration = fullBodyFuncAnimSm.collection["DisruptionAttack" + suffix]
|
||||
.Interval(IntervalType.Startup).Duration * 2;
|
||||
|
||||
timeScaleModifierClip.duration = duration;
|
||||
player.feedbackSc.PlayFeedback("DisruptionStartup");
|
||||
}
|
||||
< 1f => "A",
|
||||
< 3f => "B",
|
||||
>= 3f => "C",
|
||||
_ => "A"
|
||||
};
|
||||
|
||||
if (suffix == "B")
|
||||
List<Enemy> disruptable = CombatManager.EnemySm.GetDisruptableEnemies(availableEnemies);
|
||||
Enemy target = CombatManager.EnemySm.GetScoredEnemies(availableEnemies)
|
||||
.ApplyScoreModifier(disruptable, 0f, 1f).BestEnemy();
|
||||
|
||||
if (PlayTargetedAnimation("DisruptionAttack" + suffix, target))
|
||||
{
|
||||
ModifyTechniqueScore(-1, false);
|
||||
if (disruptable.Count > 0)
|
||||
{
|
||||
FeedbackClip timeScaleModifierClip = player.feedbackSc.GetFeedbackData("DisruptionStartup")
|
||||
.Clip<TimeScaleModifierAction>("Time");
|
||||
float duration = fullBodyFuncAnimSm.collection["DisruptionAttack" + suffix]
|
||||
.Interval(IntervalType.Startup).Duration * 2;
|
||||
|
||||
timeScaleModifierClip.duration = duration;
|
||||
player.feedbackSc.PlayFeedback("DisruptionStartup");
|
||||
}
|
||||
|
||||
if (suffix == "B")
|
||||
{
|
||||
ModifyTechniqueScore(-1, false);
|
||||
}
|
||||
else if (suffix == "C")
|
||||
{
|
||||
ModifyTechniqueScore(-3, false);
|
||||
}
|
||||
|
||||
comboSm.main.Reset();
|
||||
functionSm["DisruptionAttack"].Execute();
|
||||
}
|
||||
else if (suffix == "C")
|
||||
{
|
||||
ModifyTechniqueScore(-3, false);
|
||||
}
|
||||
|
||||
comboSm.main.Reset();
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -299,7 +319,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
player.operationSc.Dodge();
|
||||
DodgeSource defaultDodge = DodgeSource.Default(player);
|
||||
player.reactionSc.dodgeSm.ApplyDodge(defaultDodge);
|
||||
player.reactionSc.dodgeSm.GetCurrentDodgeSource().PerfectDodge();
|
||||
player.reactionSc.dodgeSm.GetCurrentDodgeSource().PerfectDodge(null);
|
||||
player.reactionSc.dodgeSm.RemoveDodge("DefaultDodge");
|
||||
}
|
||||
}
|
||||
@@ -372,17 +392,15 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
float magnitude = hitFeedback == "SingleNormalHit" ? 0.12f : 0.06f;
|
||||
positionShakeAction.amplitude = vfxData.Get(vfxName).slashScreenPosition.normalized * magnitude;
|
||||
feedbackSc.PlayFeedback(hitFeedback);
|
||||
|
||||
new ElectronicParalysis.Progress(100f).Apply(enemy, player, this);
|
||||
|
||||
ModifyTechniqueScore(0.02f);
|
||||
if (attackUnit.unitName == "InstantAttack")
|
||||
/*if (attackUnit.unitName == "InstantAttack")
|
||||
{
|
||||
if (enemy.buffSm.HasBuff<ElectronicParalysis>())
|
||||
{
|
||||
slash.attackSm.attackValue.damage *= 2f;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
});
|
||||
|
||||
return slash;
|
||||
@@ -391,11 +409,25 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
private NormalArea GenerateHeavySlash(string vfxName, AttackUnit attackUnit)
|
||||
{
|
||||
NormalArea slash = vfxData.SpawnVFX(vfxName, player).GetComponentInChildren<NormalArea>();
|
||||
|
||||
slash.Initialize<NormalArea>(player, this, Fraction.Enemy);
|
||||
|
||||
if (!HasExtender<PhotonDissociator>())
|
||||
{
|
||||
slash.SetAttackSubmodule<NormalArea>(attackUnit)
|
||||
.SetTimeSubmodule<NormalArea>(1f, 0.04f)
|
||||
.SetHitSubmodule<NormalArea>();
|
||||
}
|
||||
else // 如果有Photon Dissociator,重攻击拆分为五段,每段伤害为原来的30%,时间和判定也相应调整
|
||||
{
|
||||
AttackUnit modifiedUnit = attackUnit.Clone();
|
||||
modifiedUnit.startDamage *= 0.5f;
|
||||
|
||||
slash.SetAttackSubmodule<NormalArea>(modifiedUnit)
|
||||
.SetTimeSubmodule<NormalArea>(1f, 0.04f, 0.4f)
|
||||
.SetHitSubmodule<NormalArea>(0.1f, 3);
|
||||
}
|
||||
|
||||
slash.Initialize<NormalArea>(player, this, Fraction.Enemy)
|
||||
.SetAttackSubmodule<NormalArea>(attackUnit)
|
||||
.SetTimeSubmodule<NormalArea>(1f, 0.04f)
|
||||
.SetHitSubmodule<NormalArea>();
|
||||
slash.SetImpulseSubmodule(1f).WithRepulsion(5f);
|
||||
|
||||
slash.attackSm.breakthroughAction = (enemy, hitPosition) =>
|
||||
@@ -500,8 +532,6 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
if (attackArea is NormalArea)
|
||||
{
|
||||
ModifyTechniqueScore(-0.1f);
|
||||
//attackArea.creator.GetHit(Breakthrough.Type.Disruption, out _);
|
||||
//attackArea.creator.movementSc.impulseSm.ApplyKnockback(player.transform.forward, 5f);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -516,12 +546,15 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
new Vector3(0f, 4f, -2f);
|
||||
|
||||
feedbackSc.PlayFeedback("PerfectBlock");
|
||||
|
||||
|
||||
if (attackArea is NormalArea)
|
||||
{
|
||||
ModifyTechniqueScore(0.2f);
|
||||
//attackArea.creator.GetHit(Breakthrough.Type.Disruption, out _);
|
||||
//attackArea.creator.movementSc.impulseSm.ApplyKnockback(player.transform.forward, 5f);
|
||||
if (HasExtender<PhotonPolarizer>())
|
||||
{
|
||||
attackArea.creator.GetHit(Breakthrough.Type.Forced, out _);
|
||||
attackArea.creator.movementSc.impulseSm.ApplyKnockback(player.transform.forward, 6f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using Cielonos.MainGame.Buffs;
|
||||
using Cielonos.MainGame.Buffs.Character;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using SLSUtilities.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 穿甲弹头 / Armor Piercing Round
|
||||
/// Kinetics 攻击命中时降低目标护甲。
|
||||
/// </summary>
|
||||
public partial class ArmorPiercingRound : PassiveEquipmentBase
|
||||
{
|
||||
private const string EventKey = nameof(ArmorPiercingRound);
|
||||
|
||||
public override void OnObtained()
|
||||
{
|
||||
base.OnObtained();
|
||||
Action<AttackAreaBase, CharacterBase, Attack.Context> onStartAttack = OnStartAttack;
|
||||
player.eventSm.onStartAttack.Add(EventKey, onStartAttack.ToPrioritized());
|
||||
}
|
||||
|
||||
public override void OnDiscarded()
|
||||
{
|
||||
player.eventSm.onStartAttack.Remove(EventKey);
|
||||
base.OnDiscarded();
|
||||
}
|
||||
|
||||
private void OnStartAttack(AttackAreaBase attackArea, CharacterBase target, Attack.Context context)
|
||||
{
|
||||
if (context.value.type == Attack.Type.Kinetics)
|
||||
{
|
||||
int armorReduction = Mathf.RoundToInt(passiveAttributeSm.GetItemAttribute("ArmorReduction"));
|
||||
new ArmorReduction(5f, armorReduction).Apply(target, player, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ArmorPiercingRound
|
||||
{
|
||||
/// <summary>
|
||||
/// 穿甲弹头的Buff,默认降低目标等于unitedStack层数的护甲,持续5秒
|
||||
/// </summary>
|
||||
public class ArmorReduction : CharacterBuffBase
|
||||
{
|
||||
public ArmorReduction(float duration, int stack)
|
||||
{
|
||||
Initialize(BuffType.Positive, BuffDispelLevel.Basic);
|
||||
|
||||
this.contentSubmodule = new ContentSubmodule(this);
|
||||
this.timeSubmodule = new TimeSubmodule(this, duration);
|
||||
this.unitedStackSubmodule = new UnitedStackSubmodule(this, stack);
|
||||
this.attributeSubmodule = new AttributeSubmodule(this);
|
||||
}
|
||||
|
||||
public override bool OnBuffApply(out CharacterBuffBase existingBuff)
|
||||
{
|
||||
if (FindExistingSameBuff(out ArmorReduction existing))
|
||||
{
|
||||
existingBuff = existing;
|
||||
existing.timeSubmodule.RefreshDuration();
|
||||
existing.unitedStackSubmodule.PickHigherStack(this.unitedStackSubmodule);
|
||||
existing.UpdateArmorValue(existing.unitedStackSubmodule.stackAmount);
|
||||
return false;
|
||||
}
|
||||
|
||||
existingBuff = null;
|
||||
UpdateArmorValue(unitedStackSubmodule.stackAmount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据当前层数更新护甲加成数值。
|
||||
/// </summary>
|
||||
private void UpdateArmorValue(int stacks)
|
||||
{
|
||||
attributeSubmodule.numericChange[CharacterAttribute.Armor] = -stacks;
|
||||
attributeSubmodule.RefreshAllModifiedAttributes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46a4b90939f53e04c8b0211785d581d5
|
||||
@@ -1,5 +1,3 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
public class Ascension : PassiveEquipmentBase
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using Cielonos.MainGame.Buffs.Character;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using SLSUtilities.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 助燃剂 / Combustion Enhancer
|
||||
/// 每次攻击到敌人后,如果敌人处于燃烧Buff,那么为燃烧Buff持续时间延长0.1秒
|
||||
/// </summary>
|
||||
public class CombustionEnhancer : PassiveEquipmentBase
|
||||
{
|
||||
private const string EventKey = nameof(CombustionEnhancer);
|
||||
|
||||
public override void OnObtained()
|
||||
{
|
||||
base.OnObtained();
|
||||
Action<AttackAreaBase, CharacterBase, Attack.Result> onFinishAttack = OnFinishAttack;
|
||||
player.eventSm.onFinishAttack.Add(EventKey, onFinishAttack.ToPrioritized());
|
||||
}
|
||||
|
||||
public override void OnDiscarded()
|
||||
{
|
||||
player.eventSm.onFinishAttack.Remove(EventKey);
|
||||
base.OnDiscarded();
|
||||
}
|
||||
|
||||
private void OnFinishAttack(AttackAreaBase attackArea, CharacterBase target, Attack.Result result)
|
||||
{
|
||||
Burn burn = target.buffSm.GetBuff<Burn>();
|
||||
burn?.timeSubmodule.AddDuration(0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9bbfaab77002de4f98d00e15a5b6466
|
||||
@@ -20,9 +20,9 @@ namespace Cielonos.MainGame.Inventory
|
||||
public FunctionSubmodule(ItemBase owner, FunctionData data) : base(owner)
|
||||
{
|
||||
functionUnits = new Dictionary<string, RuntimeFunctionUnit>();
|
||||
foreach (var kvp in data.functionUnits)
|
||||
foreach (var unit in data.functionUnitList)
|
||||
{
|
||||
functionUnits[kvp.Key] = new RuntimeFunctionUnit(this, kvp.Value);
|
||||
functionUnits[unit.unitName] = new RuntimeFunctionUnit(this, unit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Cielonos.MainGame.Inventory
|
||||
public RuntimeFunctionUnit(FunctionSubmodule owner, FunctionData.FunctionUnit data) : base(owner)
|
||||
{
|
||||
this.data = data;
|
||||
maxCooldown = data.interval;
|
||||
maxCooldown = data.cooldown;
|
||||
currentCooldown = 0f;
|
||||
}
|
||||
|
||||
@@ -60,30 +60,49 @@ namespace Cielonos.MainGame.Inventory
|
||||
{
|
||||
bool cooldownAvailable = currentCooldown <= 0f;
|
||||
bool energyAvailable = character.attributeSm[CharacterAttribute.Energy] >= GetEffectiveEnergyCost();
|
||||
return cooldownAvailable && energyAvailable;
|
||||
bool available = cooldownAvailable && energyAvailable;
|
||||
|
||||
if (!available)
|
||||
{
|
||||
if(character is Player && owner.owner is MainWeaponBase)
|
||||
{
|
||||
if (data.shownInUI || data.isMain)
|
||||
{
|
||||
PlayerCanvas.MainWeaponUIArea.functionIconDict[data.unitName].SetFrameOutline(0.25f, Color.red);
|
||||
}
|
||||
}
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
public RuntimeFunctionUnit Execute()
|
||||
{
|
||||
ResetCooldown();
|
||||
ConsumeEnergy();
|
||||
if(character is Player && owner.owner is MainWeaponBase)
|
||||
{
|
||||
if (data.shownInUI || data.isMain)
|
||||
{
|
||||
PlayerCanvas.MainWeaponUIArea.functionIconDict[data.unitName].SetFrameOutline(0.25f);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private const float MinCooldownMultiplier = 0.1f;
|
||||
private const float MaxCooldownReduction = 0.9f;
|
||||
private const float MaxEnergyCostReduction = 0.9f;
|
||||
|
||||
/// <summary> 计算应用 EnergyCostReduction 后的实际能量消耗。 </summary>
|
||||
private float GetEffectiveEnergyCost()
|
||||
{
|
||||
float ecr = Mathf.Clamp(character.attributeSm[CharacterAttribute.EnergyCostReduction], 0f, MaxEnergyCostReduction);
|
||||
return data.energyCost * (1f - ecr);
|
||||
float energyCostReduction = Mathf.Clamp(character.attributeSm[CharacterAttribute.EnergyCostReduction], 0f, MaxEnergyCostReduction);
|
||||
return data.energyCost * (1f - energyCostReduction);
|
||||
}
|
||||
|
||||
private void ResetCooldown()
|
||||
{
|
||||
float cdr = Mathf.Clamp(character.attributeSm[CharacterAttribute.CooldownReduction], 0f, 1f - MinCooldownMultiplier);
|
||||
currentCooldown = maxCooldown * (1f - cdr);
|
||||
float cooldownReduction = Mathf.Clamp(character.attributeSm[CharacterAttribute.CooldownReduction], 0f, MaxCooldownReduction);
|
||||
currentCooldown = maxCooldown * (1f - cooldownReduction);
|
||||
}
|
||||
|
||||
private void ConsumeEnergy()
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Cielonos.MainGame
|
||||
|
||||
OnEnemyDefeated?.Invoke(enemy);
|
||||
GrantCurrencyDrop(enemy);
|
||||
Debug.Log($"[CombatManager] 敌人死亡:{enemy.name},剩余:{owner.enemySm.activeEnemiesList.Count}");
|
||||
//Debug.Log($"[CombatManager] 敌人死亡:{enemy.name},剩余:{owner.enemySm.activeEnemiesList.Count}");
|
||||
|
||||
if (owner.enemySm.activeEnemiesList.Count > 0)
|
||||
{
|
||||
@@ -115,7 +115,7 @@ namespace Cielonos.MainGame
|
||||
// 所有敌人已清除
|
||||
roomState = CombatRoomState.Cleared;
|
||||
owner.enemySm.OnEnemyRemoved -= HandleEnemyRemoved;
|
||||
Debug.Log("[CombatManager] 战斗房间已清空,触发 OnRoomCleared。");
|
||||
//Debug.Log("[CombatManager] 战斗房间已清空,触发 OnRoomCleared。");
|
||||
OnRoomCleared?.Invoke();
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Cielonos.MainGame
|
||||
int amount = MainGameManager.Config.RollCurrency(typedEnemy.enemyRank, rng);
|
||||
|
||||
MainGameManager.Player.inventorySc.backpackSm.ObtainItem<RareMaterial>(amount);
|
||||
Debug.Log($"[CombatRoomSubmodule] 击杀 {typedEnemy.enemyRank} 敌人,获得稀有材料 x{amount}。");
|
||||
//Debug.Log($"[CombatRoomSubmodule] 击杀 {typedEnemy.enemyRank} 敌人,获得稀有材料 x{amount}。");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ MonoBehaviour:
|
||||
tags:
|
||||
- Normal
|
||||
barIndex: 0
|
||||
beatInBar: 6
|
||||
- time: 2.5958335
|
||||
beatInBar: 7
|
||||
- time: 2.6375
|
||||
tags:
|
||||
- Normal
|
||||
barIndex: 1
|
||||
|
||||
@@ -15,15 +15,14 @@ namespace Cielonos.MainGame.UI
|
||||
public Image frame;
|
||||
private Sequence frameOutlineSequence;
|
||||
|
||||
public void Initialize(Sprite icon, string weaponName)
|
||||
public void Initialize(RuntimeFunctionUnit functionUnit, string weaponName)
|
||||
{
|
||||
rectIcon.sprite = icon;
|
||||
rectIcon.sprite = functionUnit.data.icon;
|
||||
weaponNameText.text = weaponName;
|
||||
}
|
||||
|
||||
public void SetFrameOutline(float totalDuration, Color color = default, float intensity = 2f)
|
||||
{
|
||||
return;
|
||||
color = color == default ? Color.cyan : color;
|
||||
Color hdrColor = color * Mathf.Pow(2f, intensity);
|
||||
GlowFilter glowFilter = frame.GetComponent<GlowFilter>();
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using ChocDino.UIFX;
|
||||
using Cielonos.MainGame.Inventory;
|
||||
using DG.Tweening;
|
||||
using SLSUtilities.UI;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
@@ -8,31 +11,87 @@ namespace Cielonos.MainGame.UI
|
||||
{
|
||||
public class MainWeaponFunctionIcon : UIElementBase
|
||||
{
|
||||
public RuntimeFunctionUnit functionUnit;
|
||||
[FormerlySerializedAs("timerFillImage")] public Image frame;
|
||||
public Image frame;
|
||||
public Image iconImage;
|
||||
public Image timer;
|
||||
|
||||
public Image timerImage;
|
||||
public TMP_Text timerText;
|
||||
public TMP_Text costText;
|
||||
|
||||
private Sequence _frameOutlineSequence;
|
||||
private RuntimeFunctionUnit _functionUnit;
|
||||
public void Initialize(RuntimeFunctionUnit functionUnit)
|
||||
{
|
||||
this.functionUnit = functionUnit;
|
||||
this._functionUnit = functionUnit;
|
||||
iconImage.sprite = functionUnit.data.icon != null ? functionUnit.data.icon : null;
|
||||
|
||||
if (_functionUnit.maxCooldown <= 0)
|
||||
{
|
||||
timerImage.gameObject.SetActive(false);
|
||||
timerText.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if(_functionUnit.data.energyCost <= 0)
|
||||
{
|
||||
costText.gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
costText.text = Mathf.CeilToInt(_functionUnit.data.energyCost).ToString("D");
|
||||
}
|
||||
|
||||
if (_functionUnit.data.tags.Contains("Disruption"))
|
||||
{
|
||||
Color newColor = Color.yellow;
|
||||
iconImage.color = newColor;
|
||||
newColor.a = 0.5f;
|
||||
frame.color = newColor;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateUI()
|
||||
{
|
||||
float fillAmount;
|
||||
|
||||
if (functionUnit.maxCooldown <= 0f)
|
||||
if (_functionUnit.maxCooldown <= 0f)
|
||||
{
|
||||
fillAmount = 1f;
|
||||
//无冷却
|
||||
}
|
||||
else
|
||||
{
|
||||
fillAmount = 1f - functionUnit.currentCooldown / functionUnit.maxCooldown;
|
||||
float fillAmount = 1f - _functionUnit.currentCooldown / _functionUnit.maxCooldown;
|
||||
timerImage.fillAmount = fillAmount;
|
||||
|
||||
if (_functionUnit.currentCooldown > 0f)
|
||||
{
|
||||
timerText.text = _functionUnit.currentCooldown.ToString("F1");
|
||||
}
|
||||
else
|
||||
{
|
||||
timerText.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
frame.fillAmount = fillAmount;
|
||||
|
||||
if (_functionUnit.data.energyCost > 0)
|
||||
{
|
||||
float playerCurrentEnergy = MainGameManager.Player.attributeSm[CharacterAttribute.Energy];
|
||||
costText.color = playerCurrentEnergy >= _functionUnit.data.energyCost ? Color.cyan : Color.orangeRed;
|
||||
costText.text = Mathf.CeilToInt(_functionUnit.data.energyCost).ToString("D");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFrameOutline(float totalDuration, Color color = default, float intensity = 0.5f)
|
||||
{
|
||||
color = color == default ? Color.white : color;
|
||||
Color hdrColor = color * Mathf.Pow(2f, intensity);
|
||||
GlowFilter glowFilter = frame.GetComponent<GlowFilter>();
|
||||
glowFilter.Color = hdrColor;
|
||||
float fadeDuration = Mathf.Clamp(totalDuration / 2f, 0.2f, totalDuration);
|
||||
float stayDuration = totalDuration - fadeDuration;
|
||||
float strengthPeak = 0.5f;
|
||||
_frameOutlineSequence?.Kill(true);
|
||||
_frameOutlineSequence = DOTween.Sequence();
|
||||
_frameOutlineSequence.Append(DOTween.To(() => glowFilter.Strength, x => glowFilter.Strength = x, strengthPeak, fadeDuration).SetEase(Ease.OutQuad));
|
||||
_frameOutlineSequence.AppendInterval(stayDuration);
|
||||
_frameOutlineSequence.Append(DOTween.To(() => glowFilter.Strength, x => glowFilter.Strength = x, 0f, fadeDuration).SetEase(Ease.InQuad));
|
||||
_frameOutlineSequence.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,29 +14,40 @@ namespace Cielonos.MainGame.UI
|
||||
public RectTransform functionIconContainer;
|
||||
|
||||
public MainWeaponDisplayer displayer;
|
||||
public MainWeaponFunctionIcon mainFunctionIcon;
|
||||
public List<MainWeaponFunctionIcon> functionIcons;
|
||||
|
||||
public Dictionary<string, MainWeaponFunctionIcon> functionIconDict;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
functionIcons = new List<MainWeaponFunctionIcon>();
|
||||
functionIconDict = new Dictionary<string, MainWeaponFunctionIcon>();
|
||||
}
|
||||
|
||||
public void Initialize(MainWeaponBase mainWeapon)
|
||||
{
|
||||
this.mainWeapon = mainWeapon;
|
||||
Sprite rectIcon = mainWeapon.contentData.rectIcon;
|
||||
string weaponName = mainWeapon.contentData.itemClass.Name; //TODO: 后续改为本地化显示
|
||||
displayer.Initialize(rectIcon, weaponName);
|
||||
string weaponName = mainWeapon.contentData.itemClass.Name;
|
||||
|
||||
ClearIcons();
|
||||
|
||||
foreach (KeyValuePair<string, RuntimeFunctionUnit> unit in mainWeapon.functionSm.functionUnits)
|
||||
{
|
||||
if (!unit.Value.data.shownInUI) continue;
|
||||
|
||||
MainWeaponFunctionIcon icon = Instantiate(functionIconPrefab, functionIconContainer).GetComponent<MainWeaponFunctionIcon>();
|
||||
icon.Initialize(unit.Value);
|
||||
functionIcons.Add(icon);
|
||||
if (unit.Value.data.isMain)
|
||||
{
|
||||
mainFunctionIcon.Initialize(unit.Value);
|
||||
functionIconDict[unit.Key] = mainFunctionIcon;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!unit.Value.data.shownInUI) continue;
|
||||
|
||||
MainWeaponFunctionIcon icon = Instantiate(functionIconPrefab, functionIconContainer).GetComponent<MainWeaponFunctionIcon>();
|
||||
icon.Initialize(unit.Value);
|
||||
functionIcons.Add(icon);
|
||||
functionIconDict[unit.Key] = icon;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +64,7 @@ namespace Cielonos.MainGame.UI
|
||||
}
|
||||
|
||||
functionIcons.Clear();
|
||||
functionIconDict.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using ChocDino.UIFX;
|
||||
using Cielonos.MainGame.Inventory;
|
||||
using DG.Tweening;
|
||||
using SLSUtilities.UI;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
@@ -11,18 +12,21 @@ namespace Cielonos.MainGame.UI
|
||||
public class SupportEquipmentIcon : UIElementBase
|
||||
{
|
||||
public SupportEquipmentBase supportEquipment;
|
||||
public RuntimeFunctionUnit functionUnit;
|
||||
public Image frame;
|
||||
public Image iconImage;
|
||||
public Image timer;
|
||||
private Sequence frameOutlineSequence;
|
||||
public Image timerImage;
|
||||
public TMP_Text timerText;
|
||||
public TMP_Text costText;
|
||||
|
||||
private RuntimeFunctionUnit _functionUnit;
|
||||
private Sequence _frameOutlineSequence;
|
||||
|
||||
public void Initialize(SupportEquipmentBase supportEquipment)
|
||||
{
|
||||
if (supportEquipment == null)
|
||||
{
|
||||
iconImage.sprite = null;
|
||||
functionUnit = null;
|
||||
_functionUnit = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -35,40 +39,91 @@ namespace Cielonos.MainGame.UI
|
||||
|
||||
if (functionCount == 0)
|
||||
{
|
||||
|
||||
DisableAllParts();
|
||||
}
|
||||
else if (functionCount == 1)
|
||||
{
|
||||
functionUnit = supportEquipment.functionSm.functionUnits.Values.ToList()[0];
|
||||
_functionUnit = supportEquipment.functionSm.mainFunction;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//如果有多个功能单元,优先显示主功能单元
|
||||
_functionUnit = supportEquipment.functionSm.mainFunction;
|
||||
}
|
||||
|
||||
timerImage.gameObject.SetActive(true);
|
||||
timerText.gameObject.SetActive(true);
|
||||
costText.gameObject.SetActive(true);
|
||||
iconImage.color = Color.white;
|
||||
|
||||
if (_functionUnit.maxCooldown <= 0)
|
||||
{
|
||||
timerImage.gameObject.SetActive(false);
|
||||
timerText.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if(_functionUnit.data.energyCost <= 0)
|
||||
{
|
||||
costText.gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
costText.text = Mathf.CeilToInt(_functionUnit.data.energyCost).ToString("D");
|
||||
}
|
||||
|
||||
if (_functionUnit.data.tags.Contains("Disruption"))
|
||||
{
|
||||
Color newColor = Color.yellow;
|
||||
iconImage.color = newColor;
|
||||
newColor.a = 0.5f;
|
||||
frame.color = newColor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
DisableAllParts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DisableAllParts()
|
||||
{
|
||||
iconImage.sprite = null;
|
||||
iconImage.color = Color.clear;
|
||||
timerImage.gameObject.SetActive(false);
|
||||
timerText.gameObject.SetActive(false);
|
||||
costText.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
public override void UpdateUI()
|
||||
{
|
||||
if (functionUnit == null) return;
|
||||
if (_functionUnit == null) return;
|
||||
|
||||
float fillAmount;
|
||||
|
||||
if (functionUnit.maxCooldown <= 0f)
|
||||
if (_functionUnit.maxCooldown <= 0f)
|
||||
{
|
||||
fillAmount = 0f;
|
||||
//无冷却
|
||||
}
|
||||
else
|
||||
{
|
||||
fillAmount = functionUnit.currentCooldown / functionUnit.maxCooldown;
|
||||
float fillAmount = 1f - _functionUnit.currentCooldown / _functionUnit.maxCooldown;
|
||||
timerImage.fillAmount = fillAmount;
|
||||
|
||||
if (_functionUnit.currentCooldown > 0f)
|
||||
{
|
||||
timerText.text = _functionUnit.currentCooldown.ToString("F1");
|
||||
}
|
||||
else
|
||||
{
|
||||
timerText.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (_functionUnit.data.energyCost > 0)
|
||||
{
|
||||
float playerCurrentEnergy = MainGameManager.Player.attributeSm[CharacterAttribute.Energy];
|
||||
costText.color = playerCurrentEnergy >= _functionUnit.data.energyCost ? Color.cyan : Color.orangeRed;
|
||||
costText.text = Mathf.CeilToInt(_functionUnit.data.energyCost).ToString("D");
|
||||
}
|
||||
|
||||
timer.fillAmount = fillAmount;
|
||||
}
|
||||
|
||||
public void UseOutlineAnimation() => SetFrameOutline(0.4f, Color.white);
|
||||
@@ -85,13 +140,13 @@ namespace Cielonos.MainGame.UI
|
||||
float fadeDuration = Mathf.Clamp(totalDuration / 2f, 0.2f, totalDuration);
|
||||
float stayDuration = totalDuration - fadeDuration;
|
||||
float strengthPeak = 0.25f;
|
||||
frameOutlineSequence?.Kill(true);
|
||||
frameOutlineSequence = DOTween.Sequence();
|
||||
frameOutlineSequence.Append(DOTween.To(() => glowFilter.Strength, x => glowFilter.Strength = x, strengthPeak, fadeDuration).SetEase(Ease.OutQuad));
|
||||
frameOutlineSequence.AppendInterval(stayDuration);
|
||||
frameOutlineSequence.Append(DOTween.To(() => glowFilter.Strength, x => glowFilter.Strength = x, 0f, fadeDuration).SetEase(Ease.InQuad));
|
||||
frameOutlineSequence.OnComplete(() => GetComponent<Canvas>().sortingOrder = 0);
|
||||
frameOutlineSequence.Play();
|
||||
_frameOutlineSequence?.Kill(true);
|
||||
_frameOutlineSequence = DOTween.Sequence();
|
||||
_frameOutlineSequence.Append(DOTween.To(() => glowFilter.Strength, x => glowFilter.Strength = x, strengthPeak, fadeDuration).SetEase(Ease.OutQuad));
|
||||
_frameOutlineSequence.AppendInterval(stayDuration);
|
||||
_frameOutlineSequence.Append(DOTween.To(() => glowFilter.Strength, x => glowFilter.Strength = x, 0f, fadeDuration).SetEase(Ease.InQuad));
|
||||
_frameOutlineSequence.OnComplete(() => GetComponent<Canvas>().sortingOrder = 0);
|
||||
_frameOutlineSequence.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ namespace Cielonos.MainGame.UI
|
||||
private void Awake()
|
||||
{
|
||||
icons = GetComponentsInChildren<SupportEquipmentIcon>(true).ToList();
|
||||
icons.ForEach(icon => icon.DisableAllParts());
|
||||
}
|
||||
|
||||
public void Initialize(SupportEquipmentBase supportEquipment, int slotIndex)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLSUtilities.General
|
||||
@@ -9,47 +10,63 @@ namespace SLSUtilities.General
|
||||
public float duration;
|
||||
public float currentTime;
|
||||
public bool isInfinite;
|
||||
public bool keepActions;
|
||||
public float Percentage => duration > 0 ? Mathf.Clamp01(currentTime / duration) : 1f;
|
||||
public virtual bool IsCompleted => currentTime >= duration;
|
||||
public bool isCompleted;
|
||||
|
||||
public List<PrioritizedAction> onComplete;
|
||||
private IDisposable _autoObserver;
|
||||
|
||||
public Timer(float duration, bool isInfinite = false)
|
||||
public Timer(float duration, bool keepActions = true, bool isInfinite = false)
|
||||
{
|
||||
this.duration = duration;
|
||||
this.isInfinite = isInfinite;
|
||||
this.keepActions = keepActions;
|
||||
currentTime = 0f;
|
||||
onComplete = new List<PrioritizedAction>();
|
||||
}
|
||||
|
||||
public virtual void Update(float deltaTime)
|
||||
{
|
||||
if (!IsCompleted)
|
||||
currentTime += deltaTime;
|
||||
|
||||
if (!isInfinite && !isCompleted && currentTime >= duration)
|
||||
{
|
||||
currentTime += deltaTime;
|
||||
isCompleted = true;
|
||||
onComplete.Invoke();
|
||||
if (!keepActions)
|
||||
{
|
||||
onComplete.Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (isInfinite)
|
||||
{
|
||||
if (!isInfinite)
|
||||
{
|
||||
onComplete.Invoke();
|
||||
onComplete.Clear(); // 确保只调用一次
|
||||
}
|
||||
else
|
||||
{
|
||||
onComplete.Invoke();
|
||||
Reset();
|
||||
}
|
||||
onComplete.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Reset(float newDuration = -1f)
|
||||
{
|
||||
isCompleted = false;
|
||||
currentTime = 0f;
|
||||
if(newDuration >= 0f)
|
||||
{
|
||||
duration = newDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Complete(bool invokeAction = true)
|
||||
{
|
||||
currentTime = duration;
|
||||
isCompleted = true;
|
||||
if (invokeAction)
|
||||
{
|
||||
onComplete.Invoke();
|
||||
if (!keepActions)
|
||||
{
|
||||
onComplete.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user