Files
tiny_tackle_heroes/Assets/_Project/Scripts/Progression/ModifierDefinition.cs
T
2026-09-28 11:07:17 +09:00

158 lines
5.3 KiB
C#

using BumpCombat.Player;
using BumpCombat.Core;
using System;
using System.Globalization;
using UnityEngine;
namespace BumpCombat.Progression
{
[CreateAssetMenu(
fileName = "ModifierDefinition",
menuName = "BumpCombat/Progression/Modifier Definition")]
public sealed class ModifierDefinition : ScriptableObject
{
[SerializeField] private string modifierId;
[SerializeField] private string displayName;
[SerializeField] private CharacterStat stat;
[SerializeField] private ModifierOperation operation;
[SerializeField] private float value;
[SerializeField, Min(1)] private int maximumStacks = 1;
[SerializeField, Min(0f)] private float healOnApply;
public string ModifierId => modifierId;
public string DisplayName => displayName;
public CharacterStat Stat => stat;
public ModifierOperation Operation => operation;
public float Value => value;
public int MaximumStacks => maximumStacks;
public float HealOnApply => healOnApply;
public string GetKoreanDisplayName()
{
return $"{GetKoreanStatName(stat)} 강화";
}
public string GetKoreanEffectDescription(float effectMultiplier)
{
string statName = GetKoreanStatName(stat);
string effectValue = operation == ModifierOperation.Flat
? FormatSignedValue(value * effectMultiplier)
: $"{FormatSignedValue(value * effectMultiplier * 100f)}%";
string description = $"{statName} {effectValue}";
if (healOnApply > 0f)
{
description += $"\n즉시 체력 회복 {FormatSignedValue(healOnApply * effectMultiplier)}";
}
return description + "\n선택하면 즉시 적용됩니다.";
}
private static string GetKoreanStatName(CharacterStat targetStat)
{
switch (targetStat)
{
case CharacterStat.CollisionDamage: return "몸통박치기 피해";
case CharacterStat.MoveSpeed: return "이동 속도";
case CharacterStat.MaxHealth: return "최대 체력";
case CharacterStat.Armor: return "방어력";
case CharacterStat.LifeSteal: return "생명력 흡수";
case CharacterStat.ExperienceGain: return "경험치 획득량";
case CharacterStat.ExperiencePickupRange: return "경험치 획득 범위";
case CharacterStat.ArtifactGaugeGain: return "유물 게이지 획득량";
case CharacterStat.IncomingDamage: return "받는 피해";
case CharacterStat.AttackDamage: return "공격 피해";
default: return "능력치";
}
}
private static string FormatSignedValue(float amount)
{
if (Mathf.Approximately(amount, 0f))
{
return "0";
}
string formatted = amount.ToString("0.##", CultureInfo.InvariantCulture);
return amount > 0f ? $"+{formatted}" : formatted;
}
public bool CanApply(PlayerStats playerStats)
{
return CanApplyToModel(playerStats);
}
public bool CanApplyToModel(CharacterModel model)
{
return model != null
&& model.GetStackCount(modifierId, stat) < maximumStacks;
}
public bool TryApply(
PlayerStats playerStats,
PlayerHealth playerHealth)
{
Action<float> heal = playerHealth == null
? null
: amount => playerHealth.Heal(amount);
return TryApplyToModel(playerStats, heal);
}
public bool TryApplyToModel(
CharacterModel model,
Action<float> heal)
{
return TryApplyToModel(model, heal, 1f);
}
public bool TryApplyToModel(
CharacterModel model,
Action<float> heal,
float effectMultiplier)
{
if (float.IsNaN(effectMultiplier)
|| float.IsInfinity(effectMultiplier)
|| effectMultiplier < 0f)
{
return false;
}
if (!CanApplyToModel(model)
|| !model.AddModifier(new StatModifier(
modifierId,
stat,
operation,
value * effectMultiplier,
maximumStacks)))
{
return false;
}
if (healOnApply > 0f && heal != null)
{
heal(healOnApply * effectMultiplier);
}
return true;
}
#if UNITY_EDITOR
public void Configure(
string id,
string label,
CharacterStat targetStat,
ModifierOperation modifierOperation,
float modifierValue,
int maxStacks,
float immediateHeal = 0f)
{
modifierId = id;
displayName = label;
stat = targetStat;
operation = modifierOperation;
value = modifierValue;
maximumStacks = Mathf.Max(1, maxStacks);
healOnApply = Mathf.Max(0f, immediateHeal);
}
#endif
}
}