This commit is contained in:
SoulliesOfficial
2026-05-27 15:15:28 -04:00
parent 76f498ae2b
commit 72756712f7
669 changed files with 5361 additions and 12268 deletions

View File

@@ -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);
// 触发普通闪避成功事件

View File

@@ -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
{

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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)
{

View File

@@ -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();

View File

@@ -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"
};
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f9eeb85c7bb082046b999e5e098bca4e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using UnityEngine;
namespace Cielonos.MainGame.Inventory.Collections
{
/// <summary>
/// 光子解离器 / Photon Dissociator
/// Polychrome的扩展器重攻击拆分为3段每段伤害为原来的50%。
/// </summary>
public class PhotonDissociator : ExtenderBase
{
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: dfcb853ac3666fc45a65e43107958eaf

View File

@@ -0,0 +1,13 @@
using UnityEngine;
namespace Cielonos.MainGame.Inventory.Collections
{
/// <summary>
/// 光子偏振器 / Photon Polarizer
/// Polychrome的扩展器可以使完美格挡弹开敌人阻止敌人的连招
/// </summary>
public class PhotonPolarizer : ExtenderBase
{
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b6b2bf502e7cd5c4ca858a7492f332a9

View File

@@ -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()

View File

@@ -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);
}
}
};

View File

@@ -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();
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 46a4b90939f53e04c8b0211785d581d5

View File

@@ -1,5 +1,3 @@
using UnityEngine;
namespace Cielonos.MainGame.Inventory.Collections
{
public class Ascension : PassiveEquipmentBase

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d9bbfaab77002de4f98d00e15a5b6466

View File

@@ -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()

View File

@@ -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}。");
}
}
}

View File

@@ -64,8 +64,8 @@ MonoBehaviour:
tags:
- Normal
barIndex: 0
beatInBar: 6
- time: 2.5958335
beatInBar: 7
- time: 2.6375
tags:
- Normal
barIndex: 1

View File

@@ -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>();

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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)