2026-04-08 스킬시스템 진행중, 폴더구조 변경등

This commit is contained in:
2026-04-08 05:36:01 +09:00
parent 0844a07902
commit 4eca51b885
1334 changed files with 61947 additions and 14859 deletions

Binary file not shown.

View File

@@ -1 +1 @@
public enum PlayerState { Idle, Walk, Run, Dodge, Jump, Fall, Attack, Charge, Hit, Dead, Inertia, Action, Trans, None }
public enum PlayerState { Idle, Walk, Run, Dodge, Jump, Fall, Attack, Charge, Cast, Channel, Hit, Dead, Inertia, Action, Trans, None }

View File

@@ -57,7 +57,7 @@ public void SetMaxJumpCount(int maxCount)
#region
//지상 이동이 가능한가?
public bool CanMove() => IsGrounded && !IsMoveCut && (CurrentState == PlayerState.Idle || CurrentState == PlayerState.Walk || CurrentState == PlayerState.Run) && (CurrentState != PlayerState.Inertia && CurrentState != PlayerState.Action && CurrentState != PlayerState.Trans);
public bool CanMove() => IsGrounded && !IsMoveCut && (CurrentState == PlayerState.Idle || CurrentState == PlayerState.Walk || CurrentState == PlayerState.Run) && (CurrentState != PlayerState.Inertia && CurrentState != PlayerState.Action && CurrentState != PlayerState.Trans && CurrentState != PlayerState.Cast && CurrentState != PlayerState.Channel);
//점프가 가능한 상태인가?
public bool CanJump()
{
@@ -81,6 +81,7 @@ public bool CanDodge()
if (CurrentState == PlayerState.Inertia) return false;
if (CurrentState == PlayerState.Action) return false;
if (CurrentState == PlayerState.Trans) return false;
if (CurrentState == PlayerState.Cast || CurrentState == PlayerState.Channel) return false;
// 스태미나 시스템이 있다면 체크
// if (CurrentStamina < DodgeCost) return false;
@@ -92,8 +93,9 @@ public bool CanDodge()
//공격이 가능한 상태인가?
public bool CanAttack()
{
//이미 공격 중이거나 차징 중이면 공격 불가 (연속기가 있다면 바뀔수 있음)
if (CurrentState == PlayerState.Attack || CurrentState == PlayerState.Charge)
//이미 공격 중이거나 차징/캐스팅/채널링 중이면 공격 불가
if (CurrentState == PlayerState.Attack || CurrentState == PlayerState.Charge
|| CurrentState == PlayerState.Cast || CurrentState == PlayerState.Channel)
return false;
//피격(Hit) 중이거나 죽었다면(Dead) 공격 불가

View File

@@ -1,7 +1,7 @@
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : Health
public class PlayerHealth : Health, IDamageable
{
[SerializeField] PlayerStat _pstat;
@@ -33,6 +33,13 @@ private void Update()
public void TakeDamage(int damage)
{
ChangeHP(currentHp - Mathf.Clamp(damage,0,currentHp));
ChangeHP(currentHp - Mathf.Clamp(damage, 0, currentHp));
}
public void TakeDamage(int damage, Transform source)
{
TakeDamage(damage);
}
public Transform GetTransform() => transform;
}

View File

@@ -0,0 +1,30 @@
using UnityEngine;
public enum DebuffType
{
DamageOverTime,
Slow,
StatReduction,
Stun
}
[CreateAssetMenu(menuName = "Skill/DebuffData")]
public class DebuffData : ScriptableObject
{
[Header("기본 정보")]
public string DebuffName;
public Sprite Icon;
[Header("디버프 설정")]
public DebuffType DebuffType;
public float Duration;
public float TickInterval;
public float Value;
[Header("스택")]
public bool Stackable;
public int MaxStacks;
[Header("이펙트")]
public GameObject EffectPrefab;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 71747801caf54d74cafaf0cabbc6bed7

View File

@@ -2,11 +2,39 @@
/*
:
Project에서 Create Skill/SkillData로 ,
Create Skill/WeaponSkillSet로
SkillManager + Effect
LoadWeaponSkills(skillSet)
SkillInput(slotIndex, inputState)
1. Project에서 Create Skill/SkillData로 ,
2. Create Skill/WeaponSkillSet로
3. Create Skill/DebuffData로 ()
4. SkillModule + Effect
(DamageEffect, AreaEffect, ZoneEffect, BuffEffect, ProjectileEffect)
5. StatusEffectReceiver + IDamageable
6. LoadWeaponSkills(skillSet)
7. SkillInput(slotIndex, inputState)
8. Remote AreaConfirmInput(inputState)
ActivationType ( ):
Instant
Charge ,
Cast (/ )
Channel (/ )
CastMethod ( ):
Self ( ActivationType )
Remote ( ActivationType )
TargetType ( ):
Single DamageEffect
Area AreaEffect
Self BuffEffect
Projectile ProjectileEffect
Zone ZoneEffect ()
:
Instant + Self
Instant + Remote
Cast + Remote
Channel + Remote
Charge + Remote
*/
[CreateAssetMenu(menuName = "Skill/SkillData")]
@@ -20,6 +48,7 @@ public class SkillData : ScriptableObject
[Header("스킬 분류")]
public SkillType SkillType;
public ActivationType ActivationType;
public CastMethod CastMethod;
public TargetType TargetType;
[Header("애니메이션")]
@@ -28,9 +57,15 @@ public class SkillData : ScriptableObject
[Header("이펙트")]
public GameObject EffectPrefab;
[Header("범위 지정 (AreaSelect용)")]
[Header("범위 지정 (Remote용)")]
public GameObject AreaIndicatorPrefab;
[Header("설치형 (Zone)")]
public GameObject ZonePrefab;
[Header("디버프")]
public DebuffData[] AppliedDebuffs;
[Header("레벨별 수치")]
public SkillLevelData[] Levels;
@@ -50,8 +85,13 @@ public class SkillLevelData
public float ManaCost;
public float Duration;
public float ChargeTimeMax;
public float CastTime;
public float ChannelDuration;
public float TickInterval;
public float TickDamage;
}
public enum SkillType { Active, Passive }
public enum ActivationType { Instant, Charge, AreaSelect }
public enum TargetType { Self, Single, Area, Projectile }
public enum ActivationType { Instant, Charge, Cast, Channel }
public enum CastMethod { Self, Remote }
public enum TargetType { Self, Single, Area, Projectile, Zone }

View File

@@ -4,10 +4,19 @@ public class AreaEffect : MonoBehaviour, ISkillEffect
{
public void Execute(SkillInstance skill, Transform caster, float chargeRatio)
{
SkillLevelData levelData = skill.CurrentLevelData;
float finalDamage = levelData.Damage * chargeRatio;
Vector3 center = caster.position + caster.forward * skill.CurrentLevelData.Range;
ApplyArea(skill, caster, center, chargeRatio);
}
Vector3 center = caster.position + caster.forward * levelData.Range;
public void ExecuteAtPosition(SkillInstance skill, Transform caster, Vector3 targetPos, float chargeRatio)
{
ApplyArea(skill, caster, targetPos, chargeRatio);
}
private void ApplyArea(SkillInstance skill, Transform caster, Vector3 center, float chargeRatio)
{
SkillLevelData levelData = skill.CurrentLevelData;
int damage = Mathf.RoundToInt(levelData.Damage * chargeRatio);
if (skill.Data.EffectPrefab != null)
{
@@ -19,25 +28,21 @@ public void Execute(SkillInstance skill, Transform caster, float chargeRatio)
{
if (hit.transform == caster) continue;
Debug.Log($"[범위] {hit.name}에게 {finalDamage} 데미지");
IDamageable target = hit.GetComponent<IDamageable>();
if (target != null)
{
target.TakeDamage(damage, caster);
if (skill.Data.AppliedDebuffs != null)
{
StatusEffectReceiver receiver = hit.GetComponent<StatusEffectReceiver>();
if (receiver != null)
{
foreach (var debuff in skill.Data.AppliedDebuffs)
receiver.ApplyDebuff(debuff);
}
}
public void ExecuteAtPosition(SkillInstance skill, Transform caster, Vector3 targetPos)
{
SkillLevelData levelData = skill.CurrentLevelData;
if (skill.Data.EffectPrefab != null)
{
Instantiate(skill.Data.EffectPrefab, targetPos, Quaternion.identity);
}
Collider[] hits = Physics.OverlapSphere(targetPos, levelData.Range);
foreach (Collider hit in hits)
{
if (hit.transform == caster) continue;
Debug.Log($"[범위지정] {hit.name}에게 {levelData.Damage} 데미지");
}
}
}

View File

@@ -11,4 +11,10 @@ public void Execute(SkillInstance skill, Transform caster, float chargeRatio)
Debug.Log($"버프 적용: {skill.Data.SkillName}, 지속시간 {levelData.Duration}초");
}
public void ExecuteAtPosition(SkillInstance skill, Transform caster, Vector3 targetPos, float chargeRatio)
{
// 버프는 항상 시전자 대상이므로 Execute와 동일
Execute(skill, caster, chargeRatio);
}
}

View File

@@ -4,17 +4,40 @@ public class DamageEffect : MonoBehaviour, ISkillEffect
{
public void Execute(SkillInstance skill, Transform caster, float chargeRatio)
{
SkillLevelData levelData = skill.CurrentLevelData;
float finalDamage = levelData.Damage * chargeRatio;
float range = levelData.Range;
Vector3 center = caster.position + caster.forward * skill.CurrentLevelData.Range * 0.5f;
ApplyDamage(skill, caster, center, chargeRatio);
}
Collider[] hits = Physics.OverlapSphere(caster.position + caster.forward * range * 0.5f, range);
public void ExecuteAtPosition(SkillInstance skill, Transform caster, Vector3 targetPos, float chargeRatio)
{
ApplyDamage(skill, caster, targetPos, chargeRatio);
}
private void ApplyDamage(SkillInstance skill, Transform caster, Vector3 center, float chargeRatio)
{
SkillLevelData levelData = skill.CurrentLevelData;
int damage = Mathf.RoundToInt(levelData.Damage * chargeRatio);
Collider[] hits = Physics.OverlapSphere(center, levelData.Range);
foreach (Collider hit in hits)
{
if (hit.transform == caster) continue;
// IDamageable 등 인터페이스가 있으면 여기서 적용
Debug.Log($"{hit.name}에게 {finalDamage} 데미지");
IDamageable target = hit.GetComponent<IDamageable>();
if (target != null)
{
target.TakeDamage(damage, caster);
if (skill.Data.AppliedDebuffs != null)
{
StatusEffectReceiver receiver = hit.GetComponent<StatusEffectReceiver>();
if (receiver != null)
{
foreach (var debuff in skill.Data.AppliedDebuffs)
receiver.ApplyDebuff(debuff);
}
}
}
}
}
}

View File

@@ -3,4 +3,5 @@
public interface ISkillEffect
{
void Execute(SkillInstance skill, Transform caster, float chargeRatio);
void ExecuteAtPosition(SkillInstance skill, Transform caster, Vector3 targetPos, float chargeRatio);
}

View File

@@ -12,4 +12,17 @@ public void Execute(SkillInstance skill, Transform caster, float chargeRatio)
// 투사체에 데미지 정보 전달
// proj.GetComponent<Projectile>()?.Init(skill.CurrentLevelData.Damage * chargeRatio);
}
public void ExecuteAtPosition(SkillInstance skill, Transform caster, Vector3 targetPos, float chargeRatio)
{
if (skill.Data.EffectPrefab == null) return;
Vector3 spawnPos = caster.position + Vector3.up;
Vector3 direction = (targetPos - spawnPos).normalized;
Quaternion rotation = Quaternion.LookRotation(direction);
GameObject proj = Instantiate(skill.Data.EffectPrefab, spawnPos, rotation);
// 투사체에 데미지 정보 전달
// proj.GetComponent<Projectile>()?.Init(skill.CurrentLevelData.Damage * chargeRatio);
}
}

View File

@@ -0,0 +1,29 @@
using UnityEngine;
public class ZoneEffect : MonoBehaviour, ISkillEffect
{
public void Execute(SkillInstance skill, Transform caster, float chargeRatio)
{
Vector3 center = caster.position + caster.forward * skill.CurrentLevelData.Range;
SpawnZone(skill, center);
}
public void ExecuteAtPosition(SkillInstance skill, Transform caster, Vector3 targetPos, float chargeRatio)
{
SpawnZone(skill, targetPos);
}
private void SpawnZone(SkillInstance skill, Vector3 position)
{
if (skill.Data.ZonePrefab == null) return;
GameObject zoneObj = Instantiate(skill.Data.ZonePrefab, position, Quaternion.identity);
ZoneEntity entity = zoneObj.GetComponent<ZoneEntity>();
if (entity != null)
{
SkillLevelData data = skill.CurrentLevelData;
float tickDmg = data.TickDamage > 0 ? data.TickDamage : data.Damage;
entity.Init(tickDmg, data.Range, data.Duration, data.TickInterval, skill.Data.AppliedDebuffs);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 681ba972155e69d4e903ca9f06ade300

View File

@@ -0,0 +1,61 @@
using UnityEngine;
public class ZoneEntity : MonoBehaviour
{
private float _damage;
private float _radius;
private float _duration;
private float _tickInterval;
private DebuffData[] _debuffs;
private float _lifeTimer;
private float _tickAccumulator;
public void Init(float damage, float radius, float duration, float tickInterval, DebuffData[] debuffs)
{
_damage = damage;
_radius = radius;
_duration = duration;
_tickInterval = tickInterval > 0 ? tickInterval : 1f;
_debuffs = debuffs;
}
private void Update()
{
_lifeTimer += Time.deltaTime;
_tickAccumulator += Time.deltaTime;
if (_tickAccumulator >= _tickInterval)
{
_tickAccumulator -= _tickInterval;
ApplyTickDamage();
}
if (_lifeTimer >= _duration)
{
Destroy(gameObject);
}
}
private void ApplyTickDamage()
{
Collider[] hits = Physics.OverlapSphere(transform.position, _radius);
foreach (Collider col in hits)
{
IDamageable target = col.GetComponent<IDamageable>();
if (target == null) continue;
target.TakeDamage(Mathf.RoundToInt(_damage), transform);
if (_debuffs != null)
{
StatusEffectReceiver receiver = col.GetComponent<StatusEffectReceiver>();
if (receiver != null)
{
foreach (var debuff in _debuffs)
receiver.ApplyDebuff(debuff);
}
}
}
}
}

View File

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

View File

@@ -16,11 +16,26 @@ public class SkillModule : MonoBehaviour
private int _chargingSlot = -1;
private float _chargeTimer = 0f;
// 범위 지정
// 캐스팅
private int _castingSlot = -1;
private float _castTimer = 0f;
// 채널링
private int _channelingSlot = -1;
private float _channelTimer = 0f;
private float _channelTickAccumulator = 0f;
// 범위 지정 (Remote)
private int _areaSelectSlot = -1;
private GameObject _areaIndicator;
// Remote 타겟 위치
private Vector3 _remoteTargetPos;
private bool _hasRemoteTarget;
public bool IsAreaSelecting => _areaSelectSlot >= 0;
public bool IsCasting => _castingSlot >= 0;
public bool IsChanneling => _channelingSlot >= 0;
private void Awake()
{
@@ -36,6 +51,8 @@ private void Update()
{
TickCooldowns();
TickCharge();
TickCast();
TickChannel();
TickAreaSelect();
}
@@ -67,6 +84,7 @@ private ISkillEffect ResolveEffect(TargetType targetType)
TargetType.Single => GetComponent<DamageEffect>(),
TargetType.Area => GetComponent<AreaEffect>(),
TargetType.Projectile => GetComponent<ProjectileEffect>(),
TargetType.Zone => GetComponent<ZoneEffect>(),
_ => null
};
}
@@ -92,18 +110,16 @@ public void SkillInput(int slotIndex, InputState inputState)
if (!CanUseSkill(skill)) return;
if (skill.Data.ActivationType == ActivationType.Instant)
{
ExecuteSkill(slotIndex);
}
else if (skill.Data.ActivationType == ActivationType.Charge)
{
StartCharge(slotIndex);
}
else if (skill.Data.ActivationType == ActivationType.AreaSelect)
// Remote 스킬: 먼저 범위 지정 모드 진입
if (skill.Data.CastMethod == CastMethod.Remote)
{
StartAreaSelect(slotIndex);
}
else
{
// Self 스킬: 바로 ActivationType 흐름
BeginActivation(slotIndex);
}
}
else if (inputState == InputState.Canceled)
{
@@ -111,6 +127,10 @@ public void SkillInput(int slotIndex, InputState inputState)
{
ReleaseCharge();
}
else if (_channelingSlot == slotIndex)
{
CancelChannel();
}
}
}
@@ -128,6 +148,32 @@ public void AreaConfirmInput(InputState inputState)
}
#endregion
#region
/// <summary>
/// ActivationType에 따라 적절한 흐름을 시작
/// </summary>
private void BeginActivation(int slotIndex)
{
SkillInstance skill = _equippedSkills[slotIndex];
switch (skill.Data.ActivationType)
{
case ActivationType.Instant:
ExecuteSkill(slotIndex);
break;
case ActivationType.Charge:
StartCharge(slotIndex);
break;
case ActivationType.Cast:
StartCast(slotIndex);
break;
case ActivationType.Channel:
StartChannel(slotIndex);
break;
}
}
#endregion
#region
private bool CanUseSkill(SkillInstance skill)
{
@@ -136,7 +182,8 @@ private bool CanUseSkill(SkillInstance skill)
PlayerState state = _stateMachine.CurrentState;
if (state == PlayerState.Dead || state == PlayerState.Hit
|| state == PlayerState.Dodge || state == PlayerState.Trans
|| state == PlayerState.Action)
|| state == PlayerState.Action || state == PlayerState.Cast
|| state == PlayerState.Channel)
return false;
return true;
@@ -159,12 +206,26 @@ private void ExecuteSkill(int slotIndex)
chargeRatio = maxCharge > 0 ? Mathf.Clamp01(_chargeTimer / maxCharge) : 1f;
}
// CastMethod에 따라 실행 방식 분기
if (_hasRemoteTarget)
{
effect?.ExecuteAtPosition(skill, _transform, _remoteTargetPos, chargeRatio);
ClearRemoteTarget();
}
else
{
effect?.Execute(skill, _transform, chargeRatio);
}
skill.StartCooldown();
_chargeTimer = 0f;
_chargingSlot = -1;
}
private void ClearRemoteTarget()
{
_hasRemoteTarget = false;
}
#endregion
#region
@@ -200,7 +261,150 @@ private void TickCharge()
}
#endregion
#region
#region
private void StartCast(int slotIndex)
{
_castingSlot = slotIndex;
_castTimer = 0f;
_stateMachine.ChangeState(PlayerState.Cast);
SkillInstance skill = _equippedSkills[slotIndex];
if (!string.IsNullOrEmpty(skill.Data.AnimTrigger))
_anim.SetTrigger(skill.Data.AnimTrigger);
_stateMachine.OnStateChanged += OnCastInterrupted;
}
private void TickCast()
{
if (_castingSlot < 0) return;
_castTimer += Time.deltaTime;
SkillInstance skill = _equippedSkills[_castingSlot];
if (_castTimer >= skill.CurrentLevelData.CastTime)
{
CompleteCast();
}
}
private void CompleteCast()
{
if (_castingSlot < 0) return;
_stateMachine.OnStateChanged -= OnCastInterrupted;
ExecuteSkill(_castingSlot);
_castingSlot = -1;
_castTimer = 0f;
}
private void CancelCast()
{
_stateMachine.OnStateChanged -= OnCastInterrupted;
_castingSlot = -1;
_castTimer = 0f;
ClearRemoteTarget();
}
private void OnCastInterrupted(PlayerState newState)
{
if (newState == PlayerState.Hit || newState == PlayerState.Dead
|| newState == PlayerState.Dodge)
{
CancelCast();
}
}
#endregion
#region
private void StartChannel(int slotIndex)
{
_channelingSlot = slotIndex;
_channelTimer = 0f;
_channelTickAccumulator = 0f;
_stateMachine.ChangeState(PlayerState.Channel);
SkillInstance skill = _equippedSkills[slotIndex];
if (!string.IsNullOrEmpty(skill.Data.AnimTrigger))
_anim.SetTrigger(skill.Data.AnimTrigger);
// 첫 틱 즉시 실행
ISkillEffect effect = _skillEffects[slotIndex];
ExecuteChannelTick(skill, effect);
// 쿨다운은 채널 시작 시점에 시작
skill.StartCooldown();
_stateMachine.OnStateChanged += OnChannelInterrupted;
}
private void TickChannel()
{
if (_channelingSlot < 0) return;
SkillInstance skill = _equippedSkills[_channelingSlot];
SkillLevelData data = skill.CurrentLevelData;
_channelTimer += Time.deltaTime;
_channelTickAccumulator += Time.deltaTime;
if (_channelTickAccumulator >= data.TickInterval)
{
_channelTickAccumulator -= data.TickInterval;
ISkillEffect effect = _skillEffects[_channelingSlot];
ExecuteChannelTick(skill, effect);
}
if (_channelTimer >= data.ChannelDuration)
{
EndChannel();
}
}
private void ExecuteChannelTick(SkillInstance skill, ISkillEffect effect)
{
if (_hasRemoteTarget)
{
effect?.ExecuteAtPosition(skill, _transform, _remoteTargetPos, 1f);
}
else
{
effect?.Execute(skill, _transform, 1f);
}
}
private void EndChannel()
{
_stateMachine.OnStateChanged -= OnChannelInterrupted;
_stateMachine.ChangeState(PlayerState.Idle);
_channelingSlot = -1;
_channelTimer = 0f;
_channelTickAccumulator = 0f;
ClearRemoteTarget();
}
private void CancelChannel()
{
_stateMachine.OnStateChanged -= OnChannelInterrupted;
_channelingSlot = -1;
_channelTimer = 0f;
_channelTickAccumulator = 0f;
ClearRemoteTarget();
}
private void OnChannelInterrupted(PlayerState newState)
{
if (newState == PlayerState.Hit || newState == PlayerState.Dead
|| newState == PlayerState.Dodge)
{
CancelChannel();
}
}
#endregion
#region (Remote)
private void StartAreaSelect(int slotIndex)
{
_areaSelectSlot = slotIndex;
@@ -229,26 +433,19 @@ private void ConfirmAreaSelect()
{
if (_areaSelectSlot < 0) return;
SkillInstance skill = _equippedSkills[_areaSelectSlot];
ISkillEffect effect = _skillEffects[_areaSelectSlot];
int slotIndex = _areaSelectSlot;
_stateMachine.ChangeState(PlayerState.Attack);
if (!string.IsNullOrEmpty(skill.Data.AnimTrigger))
_anim.SetTrigger(skill.Data.AnimTrigger);
Vector3 targetPos = _areaIndicator.transform.position;
if (effect is AreaEffect areaEffect)
{
areaEffect.ExecuteAtPosition(skill, _transform, targetPos);
}
skill.StartCooldown();
// 타겟 위치 저장
_remoteTargetPos = _areaIndicator.transform.position;
_hasRemoteTarget = true;
// 인디케이터 정리
Destroy(_areaIndicator);
_areaIndicator = null;
_areaSelectSlot = -1;
// ActivationType에 따라 흐름 시작
BeginActivation(slotIndex);
}
public void CancelAreaSelect()

View File

@@ -0,0 +1,55 @@
using UnityEngine;
public class DebuffInstance
{
public DebuffData Data { get; private set; }
public float RemainingTime { get; private set; }
public bool IsExpired => RemainingTime <= 0f;
private StatusEffectReceiver _receiver;
private float _tickAccumulator;
private GameObject _visualInstance;
public DebuffInstance(DebuffData data, StatusEffectReceiver receiver)
{
Data = data;
_receiver = receiver;
RemainingTime = data.Duration;
_tickAccumulator = 0f;
}
public void OnApply()
{
if (Data.EffectPrefab != null)
{
_visualInstance = Object.Instantiate(Data.EffectPrefab, _receiver.transform);
}
}
public void Tick(float deltaTime)
{
RemainingTime -= deltaTime;
if (Data.DebuffType == DebuffType.DamageOverTime && Data.TickInterval > 0)
{
_tickAccumulator += deltaTime;
if (_tickAccumulator >= Data.TickInterval)
{
_tickAccumulator -= Data.TickInterval;
IDamageable damageable = _receiver.GetComponent<IDamageable>();
damageable?.TakeDamage(Mathf.RoundToInt(Data.Value), null);
}
}
}
public void OnRemove()
{
if (_visualInstance != null)
Object.Destroy(_visualInstance);
}
public void RefreshDuration()
{
RemainingTime = Data.Duration;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 17e97bf635cef364781fdf5cedc5dfee

View File

@@ -0,0 +1,7 @@
using UnityEngine;
public interface IDamageable
{
void TakeDamage(int damage, Transform source);
Transform GetTransform();
}

View File

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

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using UnityEngine;
public class StatusEffectReceiver : MonoBehaviour
{
private List<DebuffInstance> _activeDebuffs = new List<DebuffInstance>();
public void ApplyDebuff(DebuffData data)
{
if (data == null) return;
if (!data.Stackable)
{
DebuffInstance existing = _activeDebuffs.Find(d => d.Data == data);
if (existing != null)
{
existing.RefreshDuration();
return;
}
}
else
{
int count = 0;
foreach (var d in _activeDebuffs)
if (d.Data == data) count++;
if (count >= data.MaxStacks) return;
}
DebuffInstance instance = new DebuffInstance(data, this);
_activeDebuffs.Add(instance);
instance.OnApply();
}
private void Update()
{
for (int i = _activeDebuffs.Count - 1; i >= 0; i--)
{
_activeDebuffs[i].Tick(Time.deltaTime);
if (_activeDebuffs[i].IsExpired)
{
_activeDebuffs[i].OnRemove();
_activeDebuffs.RemoveAt(i);
}
}
}
public bool HasDebuff(DebuffType type)
{
foreach (var d in _activeDebuffs)
if (d.Data.DebuffType == type) return true;
return false;
}
public float GetDebuffValue(DebuffType type)
{
float total = 0f;
foreach (var d in _activeDebuffs)
if (d.Data.DebuffType == type) total += d.Data.Value;
return total;
}
public void ClearAllDebuffs()
{
for (int i = _activeDebuffs.Count - 1; i >= 0; i--)
_activeDebuffs[i].OnRemove();
_activeDebuffs.Clear();
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
dumy

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 806fca355ae44fc4594198b3b11cbb7f
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 513a171cf5a52ed4a8ea91b31db367ff
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_01_StormTornado/Effect_01_LightningTornado.prefab
uploadId: 865912

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 318ac0a9a9480c44c8ff11aec9fb3a43
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_01_StormTornado/Effect_01_LightningTornado/Effect_01_LightningTornado(Base).prefab
uploadId: 865912

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: a66ac8cc7bfe6f7449bdb1d44b77fe3e
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_01_StormTornado/Effect_01_StormTornado.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 80cf365a0d4411a4099f07a0bec382d7
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_02_BlackHole/Effect_02_BlackHole.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: b744a3721083c254eb7cef9b9f835947
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_03_OrbitalStrike/Effect_03_OrbitalAnnihilationBeam.prefab
uploadId: 865912

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: a762a5f89618ce24d9193fe9a13cab77
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_03_OrbitalStrike/Effect_03_OrbitalStrike.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: e18c351ac3f6ed7409f6cae5ba1302eb
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_04_ChargeShot/Effect_04_ChargeAndRelease.prefab
uploadId: 865912

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 1a0c3309985528645b50dffd642237b3
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_04_ChargeShot/Effect_04_ChargeShot.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 4a7a46686d4340c4b832ba8d02f87a99
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_12_CosmicHorror/Effect_12_CosmicHorror.prefab
uploadId: 865912

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 9e119eba0536a144a8bf50ea32e75c28
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_12_CosmicHorror/Effect_12_UniverseEater.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 96aacb3c58028424aa3e5f4e5c55f12e
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_18_TimeField/Effect_18_TimeField.prefab
uploadId: 865912

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 79be24f135d46964e9f186bacaca2299
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_21_GroundScatter/Effect_21_GroundScatter.prefab
uploadId: 865912

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: c2ae8d95860915148bdf28559c527cbb
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_21_GroundScatter/Effect_21_GroundScatter/Effect_21_GroundScatter(Base).prefab
uploadId: 865912

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 519ff72a211faaf43b8be89b3169a13d
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_21_GroundScatter/Effect_21_InnertcoreRelease.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: f8b6a6f913bc4c748af1eb91ba8d1a23
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_28_PurifierBeam/Effect_28_PurifierBeam.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: b7016aae7bbdcd54fb4bf3a41bf01a60
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_31_LumenJudgement/Effect_31_LumenJudgement.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 9a504412f1061444bb18de98ad4be8d6
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_34_WindTurbulance/Effect_34_WindTurbulance.prefab
uploadId: 865912

View File

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

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 34d0599d37e35ac4ebeab64c18d00ccc
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 171146
packageName: 100 Special Skills Effects Pack
packageVersion: 26.2
assetPath: Assets/SpecialSkillsEffectsPack/AllEffects/EffectsSet_1(NotScriptBased)/Effects/Effect_38_GloryBoundary/Effect_38_GloryBoundary.prefab
uploadId: 865912

View File

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

Some files were not shown because too many files have changed in this diff Show More