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

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