feat: 드로그 집행 개시 패턴 및 낙인 디버프 추가
- 드로그 시그니처 패턴 역할과 집행 개시 패턴 데이터를 추가하고 BT 브랜치에 연결 - 시그니처 차단 성공과 실패 흐름을 BossCombatBehaviorContext에 구현하고 authoring 그래프를 재구성 - 집행자의 낙인 이상상태를 추가하고 받는 피해 배율 증가가 플레이어 대미지 계산에 반영되도록 정리 - 집행 실패 시 광역 피해, 넉백, 다운, 낙인 부여 설정을 드로그 프리팹에 연결 - 성공 경로 검증 중 확인된 보스 Hit 트리거 오류를 방어 로직으로 수정 - Unity 플레이 검증으로 집행 개시 실패와 성공 분기를 모두 확인하고 설계값은 원복
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: b08cc671f858a3b409170a5356e960a0, type: 3}
|
||||||
|
m_Name: "Data_Abnormality_Player_\uC9D1\uD589\uC790\uC758\uB099\uC778"
|
||||||
|
m_EditorClassIdentifier: Colosseum.Game::Colosseum.Abnormalities.AbnormalityData
|
||||||
|
abnormalityName: "\uC9D1\uD589\uC790\uC758 \uB099\uC778"
|
||||||
|
icon: {fileID: 0}
|
||||||
|
duration: 0
|
||||||
|
level: 1
|
||||||
|
isDebuff: 1
|
||||||
|
statModifiers: []
|
||||||
|
periodicInterval: 0
|
||||||
|
periodicValue: 0
|
||||||
|
controlType: 0
|
||||||
|
slowMultiplier: 0.5
|
||||||
|
incomingDamageMultiplier: 1.1
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bc74f1485ad140c28cc14b821e22c127
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
23
Assets/_Game/Data/Patterns/Data_Pattern_Drog_집행개시.asset
Normal file
23
Assets/_Game/Data/Patterns/Data_Pattern_Drog_집행개시.asset
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 0ce956e0878565343974c31b8111c0c6, type: 3}
|
||||||
|
m_Name: "Data_Pattern_Drog_\uC9D1\uD589\uAC1C\uC2DC"
|
||||||
|
m_EditorClassIdentifier: Colosseum.Game::Colosseum.AI.BossPatternData
|
||||||
|
patternName: "\uC9D1\uD589\uAC1C\uC2DC"
|
||||||
|
steps:
|
||||||
|
- Type: 0
|
||||||
|
Skill: {fileID: 11400000, guid: 99de24df2cb0464d9d4f633efde8dbdb, type: 2}
|
||||||
|
Duration: 0
|
||||||
|
- Type: 1
|
||||||
|
Skill: {fileID: 0}
|
||||||
|
Duration: 6.5
|
||||||
|
cooldown: 45
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5e732b41722c45288bb6234f3e3fa638
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
29
Assets/_Game/Data/Skills/Data_Skill_Drog_집행개시.asset
Normal file
29
Assets/_Game/Data/Skills/Data_Skill_Drog_집행개시.asset
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 94f0a76cebcac2f4fb5daf1b675fd79f, type: 3}
|
||||||
|
m_Name: "Data_Skill_Drog_\uC9D1\uD589\uAC1C\uC2DC"
|
||||||
|
m_EditorClassIdentifier: Colosseum.Game::Colosseum.Skills.SkillData
|
||||||
|
skillName: "\uC9D1\uD589\uAC1C\uC2DC"
|
||||||
|
description: "\uB4DC\uB85C\uADF8\uAC00 \uD798\uC744 \uB04C\uC5B4\uBAA8\uC73C\uBA70 \uC9D1\uD589\uC744 \uC900\uBE44\uD569\uB2C8\uB2E4."
|
||||||
|
icon: {fileID: 0}
|
||||||
|
skillClip: {fileID: -5764696784021583549, guid: 5eaeca917bbeb494eb14ad0e0552c42f, type: 3}
|
||||||
|
endClip: {fileID: 0}
|
||||||
|
animationSpeed: 1
|
||||||
|
useRootMotion: 0
|
||||||
|
ignoreRootMotionY: 0
|
||||||
|
jumpToTarget: 0
|
||||||
|
blockMovementWhileCasting: 1
|
||||||
|
blockJumpWhileCasting: 1
|
||||||
|
blockOtherSkillsWhileCasting: 1
|
||||||
|
cooldown: 0
|
||||||
|
manaCost: 0
|
||||||
|
effects: []
|
||||||
8
Assets/_Game/Data/Skills/Data_Skill_Drog_집행개시.asset.meta
Normal file
8
Assets/_Game/Data/Skills/Data_Skill_Drog_집행개시.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 99de24df2cb0464d9d4f633efde8dbdb
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -2190,6 +2190,7 @@ MonoBehaviour:
|
|||||||
slamPattern: {fileID: 11400000, guid: 4a52d59d590b4eaa9ef92b7984eb08c7, type: 2}
|
slamPattern: {fileID: 11400000, guid: 4a52d59d590b4eaa9ef92b7984eb08c7, type: 2}
|
||||||
leapPattern: {fileID: 11400000, guid: 88e6cc7cab28baf4c8f8a742247000ec, type: 2}
|
leapPattern: {fileID: 11400000, guid: 88e6cc7cab28baf4c8f8a742247000ec, type: 2}
|
||||||
downPunishPattern: {fileID: 11400000, guid: fe5100f855d14c0faac44b6d4f2c771e, type: 2}
|
downPunishPattern: {fileID: 11400000, guid: fe5100f855d14c0faac44b6d4f2c771e, type: 2}
|
||||||
|
signaturePattern: {fileID: 11400000, guid: 5e732b41722c45288bb6234f3e3fa638, type: 2}
|
||||||
phase2HealthThreshold: 0.75
|
phase2HealthThreshold: 0.75
|
||||||
phase3HealthThreshold: 0.4
|
phase3HealthThreshold: 0.4
|
||||||
targetRefreshInterval: 0.2
|
targetRefreshInterval: 0.2
|
||||||
@@ -2198,6 +2199,16 @@ MonoBehaviour:
|
|||||||
phase1SlamInterval: 3
|
phase1SlamInterval: 3
|
||||||
phase2SlamInterval: 2
|
phase2SlamInterval: 2
|
||||||
phase3SlamInterval: 2
|
phase3SlamInterval: 2
|
||||||
|
signatureMinPhase: 2
|
||||||
|
signatureRequiredDamageRatio: 0.1
|
||||||
|
signatureSuccessStaggerDuration: 2
|
||||||
|
signatureFailureAbnormality: {fileID: 11400000, guid: bc74f1485ad140c28cc14b821e22c127, type: 2}
|
||||||
|
signatureFailureDamage: 40
|
||||||
|
signatureFailureKnockbackRadius: 8
|
||||||
|
signatureFailureDownRadius: 3
|
||||||
|
signatureFailureKnockbackSpeed: 12
|
||||||
|
signatureFailureKnockbackDuration: 0.35
|
||||||
|
signatureFailureDownDuration: 2
|
||||||
disableBehaviorGraph: 0
|
disableBehaviorGraph: 0
|
||||||
debugMode: 1
|
debugMode: 1
|
||||||
--- !u!114 &7544406269366897481
|
--- !u!114 &7544406269366897481
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Colosseum.Enemy;
|
||||||
|
|
||||||
|
using Unity.Behavior;
|
||||||
|
using Unity.Properties;
|
||||||
|
|
||||||
|
using Action = Unity.Behavior.Action;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 시그니처 패턴 사용 가능 여부를 확인하는 체크 액션입니다.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, GeneratePropertyBag]
|
||||||
|
[NodeDescription(
|
||||||
|
name: "Check Signature Pattern Ready",
|
||||||
|
story: "시그니처 패턴 준비 여부 확인",
|
||||||
|
category: "Action",
|
||||||
|
id: "b3b2916257134e0eb3a71a5f544a8d6f")]
|
||||||
|
public partial class CheckSignaturePatternReadyAction : Action
|
||||||
|
{
|
||||||
|
protected override Status OnStart()
|
||||||
|
{
|
||||||
|
BossCombatBehaviorContext context = GameObject.GetComponent<BossCombatBehaviorContext>();
|
||||||
|
return context != null && context.IsSignaturePatternReady()
|
||||||
|
? Status.Success
|
||||||
|
: Status.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b27f3137292d5704d802b5cfb58037e4
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Colosseum.Enemy;
|
||||||
|
|
||||||
|
using Unity.Behavior;
|
||||||
|
using Unity.Properties;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
using Action = Unity.Behavior.Action;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 보스 공통 시그니처 패턴을 실행하는 액션입니다.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, GeneratePropertyBag]
|
||||||
|
[NodeDescription(
|
||||||
|
name: "Use Signature Pattern",
|
||||||
|
story: "시그니처 패턴 실행",
|
||||||
|
category: "Action",
|
||||||
|
id: "178f8888d56042c6a75b4d6ee8a7a7d4")]
|
||||||
|
public partial class UseSignaturePatternAction : Action
|
||||||
|
{
|
||||||
|
[SerializeReference]
|
||||||
|
public BlackboardVariable<GameObject> Target;
|
||||||
|
|
||||||
|
private BossCombatBehaviorContext combatBehaviorContext;
|
||||||
|
private bool started;
|
||||||
|
|
||||||
|
protected override Status OnStart()
|
||||||
|
{
|
||||||
|
combatBehaviorContext = GameObject.GetComponent<BossCombatBehaviorContext>();
|
||||||
|
if (combatBehaviorContext == null)
|
||||||
|
return Status.Failure;
|
||||||
|
|
||||||
|
GameObject target = Target != null ? Target.Value : null;
|
||||||
|
started = combatBehaviorContext.TryStartSignaturePattern(target);
|
||||||
|
return started ? Status.Running : Status.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Status OnUpdate()
|
||||||
|
{
|
||||||
|
if (!started || combatBehaviorContext == null)
|
||||||
|
return Status.Failure;
|
||||||
|
|
||||||
|
return combatBehaviorContext.IsSignaturePatternActive
|
||||||
|
? Status.Running
|
||||||
|
: Status.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnEnd()
|
||||||
|
{
|
||||||
|
started = false;
|
||||||
|
combatBehaviorContext = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0680aed4d244d7844918883e06e718d5
|
||||||
@@ -84,6 +84,11 @@ namespace Colosseum.Abnormalities
|
|||||||
[Range(0f, 1f)]
|
[Range(0f, 1f)]
|
||||||
public float slowMultiplier = 0.5f;
|
public float slowMultiplier = 0.5f;
|
||||||
|
|
||||||
|
[Header("피해 배율")]
|
||||||
|
[Tooltip("이상 상태가 적용된 동안 받는 피해 배율 (1 = 기본, 1.1 = 10% 증가)")]
|
||||||
|
[Min(0f)]
|
||||||
|
public float incomingDamageMultiplier = 1f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 영구 효과인지 확인
|
/// 영구 효과인지 확인
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -98,5 +103,10 @@ namespace Colosseum.Abnormalities
|
|||||||
/// 제어 효과가 있는지 확인
|
/// 제어 효과가 있는지 확인
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasControlEffect => controlType != ControlType.None;
|
public bool HasControlEffect => controlType != ControlType.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 받는 피해 배율 변경 여부
|
||||||
|
/// </summary>
|
||||||
|
public bool HasIncomingDamageModifier => !Mathf.Approximately(incomingDamageMultiplier, 1f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ namespace Colosseum.Abnormalities
|
|||||||
private int silenceCount;
|
private int silenceCount;
|
||||||
private int invincibleCount;
|
private int invincibleCount;
|
||||||
private float slowMultiplier = 1f;
|
private float slowMultiplier = 1f;
|
||||||
|
private float incomingDamageMultiplier = 1f;
|
||||||
|
|
||||||
// 클라이언트 판정용 제어 효과 동기화 변수
|
// 클라이언트 판정용 제어 효과 동기화 변수
|
||||||
private NetworkVariable<int> syncedStunCount = new NetworkVariable<int>(0);
|
private NetworkVariable<int> syncedStunCount = new NetworkVariable<int>(0);
|
||||||
@@ -62,6 +63,11 @@ namespace Colosseum.Abnormalities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public float MoveSpeedMultiplier => GetCurrentSlowMultiplier();
|
public float MoveSpeedMultiplier => GetCurrentSlowMultiplier();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 받는 피해 배율 (1.0 = 기본, 1.1 = 10% 증가)
|
||||||
|
/// </summary>
|
||||||
|
public float IncomingDamageMultiplier => incomingDamageMultiplier;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 행동 가능 여부 (기절이 아닐 때)
|
/// 행동 가능 여부 (기절이 아닐 때)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -238,6 +244,7 @@ namespace Colosseum.Abnormalities
|
|||||||
|
|
||||||
ApplyStatModifiers(newAbnormality);
|
ApplyStatModifiers(newAbnormality);
|
||||||
ApplyControlEffect(data);
|
ApplyControlEffect(data);
|
||||||
|
RecalculateIncomingDamageMultiplier();
|
||||||
SyncAbnormalityAdd(newAbnormality, source);
|
SyncAbnormalityAdd(newAbnormality, source);
|
||||||
|
|
||||||
OnAbnormalityAdded?.Invoke(newAbnormality);
|
OnAbnormalityAdded?.Invoke(newAbnormality);
|
||||||
@@ -282,6 +289,7 @@ namespace Colosseum.Abnormalities
|
|||||||
{
|
{
|
||||||
RemoveStatModifiers(abnormality);
|
RemoveStatModifiers(abnormality);
|
||||||
RemoveControlEffect(abnormality.Data);
|
RemoveControlEffect(abnormality.Data);
|
||||||
|
RecalculateIncomingDamageMultiplier();
|
||||||
SyncAbnormalityRemove(abnormality);
|
SyncAbnormalityRemove(abnormality);
|
||||||
activeAbnormalities.Remove(abnormality);
|
activeAbnormalities.Remove(abnormality);
|
||||||
|
|
||||||
@@ -488,6 +496,20 @@ namespace Colosseum.Abnormalities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RecalculateIncomingDamageMultiplier()
|
||||||
|
{
|
||||||
|
incomingDamageMultiplier = 1f;
|
||||||
|
|
||||||
|
for (int i = 0; i < activeAbnormalities.Count; i++)
|
||||||
|
{
|
||||||
|
AbnormalityData data = activeAbnormalities[i].Data;
|
||||||
|
if (data == null || !data.HasIncomingDamageModifier)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
incomingDamageMultiplier *= Mathf.Max(0f, data.incomingDamageMultiplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int GetCurrentStunCount() => IsServer ? stunCount : syncedStunCount.Value;
|
private int GetCurrentStunCount() => IsServer ? stunCount : syncedStunCount.Value;
|
||||||
|
|
||||||
private int GetCurrentSilenceCount() => IsServer ? silenceCount : syncedSilenceCount.Value;
|
private int GetCurrentSilenceCount() => IsServer ? silenceCount : syncedSilenceCount.Value;
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ namespace Colosseum.Editor
|
|||||||
object repeatNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.RepeaterModifier", true), new Vector2(420f, -470f));
|
object repeatNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.RepeaterModifier", true), new Vector2(420f, -470f));
|
||||||
object selectorNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SelectorComposite", true), new Vector2(420f, -280f));
|
object selectorNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SelectorComposite", true), new Vector2(420f, -280f));
|
||||||
|
|
||||||
|
object signatureSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(-1020f, -40f));
|
||||||
object downSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(-620f, -40f));
|
object downSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(-620f, -40f));
|
||||||
object leapSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(-220f, -40f));
|
object leapSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(-220f, -40f));
|
||||||
object slamSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(180f, -40f));
|
object slamSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(180f, -40f));
|
||||||
@@ -74,6 +75,11 @@ namespace Colosseum.Editor
|
|||||||
object slamFallbackSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(980f, -40f));
|
object slamFallbackSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(980f, -40f));
|
||||||
object chaseSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(1380f, -40f));
|
object chaseSequence = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, runtimeAssembly.GetType("Unity.Behavior.SequenceComposite", true), new Vector2(1380f, -40f));
|
||||||
|
|
||||||
|
object signatureRefreshNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(RefreshPrimaryTargetAction), new Vector2(-1140f, 240f));
|
||||||
|
object signatureHasTargetNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(ValidateTargetAction), new Vector2(-1020f, 240f));
|
||||||
|
object signatureReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckSignaturePatternReadyAction), new Vector2(-900f, 240f));
|
||||||
|
object signatureUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UseSignaturePatternAction), new Vector2(-780f, 240f));
|
||||||
|
|
||||||
object downSelectNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(SelectNearestDownedTargetAction), new Vector2(-740f, 240f));
|
object downSelectNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(SelectNearestDownedTargetAction), new Vector2(-740f, 240f));
|
||||||
object downReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckPunishPatternReadyAction), new Vector2(-620f, 240f));
|
object downReadyNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(CheckPunishPatternReadyAction), new Vector2(-620f, 240f));
|
||||||
object downUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UsePunishPatternAction), new Vector2(-500f, 240f));
|
object downUseNode = CreateNode(graphAsset, createNodeMethod, getNodeInfoMethod, typeof(UsePunishPatternAction), new Vector2(-500f, 240f));
|
||||||
@@ -107,8 +113,9 @@ namespace Colosseum.Editor
|
|||||||
|
|
||||||
Connect(graphAsset, connectEdgeMethod, GetDefaultOutputPort(startNode), GetDefaultInputPort(repeatNode));
|
Connect(graphAsset, connectEdgeMethod, GetDefaultOutputPort(startNode), GetDefaultInputPort(repeatNode));
|
||||||
Connect(graphAsset, connectEdgeMethod, GetDefaultOutputPort(repeatNode), GetDefaultInputPort(selectorNode));
|
Connect(graphAsset, connectEdgeMethod, GetDefaultOutputPort(repeatNode), GetDefaultInputPort(selectorNode));
|
||||||
ConnectChildren(graphAsset, connectEdgeMethod, selectorNode, downSequence, leapSequence, slamSequence, mainSequence, slamFallbackSequence, chaseSequence);
|
ConnectChildren(graphAsset, connectEdgeMethod, selectorNode, signatureSequence, downSequence, leapSequence, slamSequence, mainSequence, slamFallbackSequence, chaseSequence);
|
||||||
|
|
||||||
|
ConnectChildren(graphAsset, connectEdgeMethod, signatureSequence, signatureRefreshNode, signatureHasTargetNode, signatureReadyNode, signatureUseNode);
|
||||||
ConnectChildren(graphAsset, connectEdgeMethod, downSequence, downSelectNode, downReadyNode, downUseNode);
|
ConnectChildren(graphAsset, connectEdgeMethod, downSequence, downSelectNode, downReadyNode, downUseNode);
|
||||||
ConnectChildren(graphAsset, connectEdgeMethod, leapSequence, leapSelectNode, leapReadyNode, leapUseNode);
|
ConnectChildren(graphAsset, connectEdgeMethod, leapSequence, leapSelectNode, leapReadyNode, leapUseNode);
|
||||||
ConnectChildren(graphAsset, connectEdgeMethod, slamSequence, slamRefreshNode, slamHasTargetNode, slamRangeNode, slamTurnNode, slamReadyNode, slamUseNode);
|
ConnectChildren(graphAsset, connectEdgeMethod, slamSequence, slamRefreshNode, slamHasTargetNode, slamRangeNode, slamTurnNode, slamReadyNode, slamUseNode);
|
||||||
@@ -116,6 +123,9 @@ namespace Colosseum.Editor
|
|||||||
ConnectChildren(graphAsset, connectEdgeMethod, slamFallbackSequence, fallbackRefreshNode, fallbackHasTargetNode, fallbackRangeNode, fallbackReadyNode, fallbackUseNode);
|
ConnectChildren(graphAsset, connectEdgeMethod, slamFallbackSequence, fallbackRefreshNode, fallbackHasTargetNode, fallbackRangeNode, fallbackReadyNode, fallbackUseNode);
|
||||||
ConnectChildren(graphAsset, connectEdgeMethod, chaseSequence, chaseRefreshNode, chaseHasTargetNode, chaseUseNode);
|
ConnectChildren(graphAsset, connectEdgeMethod, chaseSequence, chaseRefreshNode, chaseHasTargetNode, chaseUseNode);
|
||||||
|
|
||||||
|
LinkTarget(signatureRefreshNode, targetVariable);
|
||||||
|
LinkTarget(signatureHasTargetNode, targetVariable);
|
||||||
|
LinkTarget(signatureUseNode, targetVariable);
|
||||||
LinkTarget(downSelectNode, targetVariable);
|
LinkTarget(downSelectNode, targetVariable);
|
||||||
LinkTarget(downUseNode, targetVariable);
|
LinkTarget(downUseNode, targetVariable);
|
||||||
LinkTarget(leapSelectNode, targetVariable);
|
LinkTarget(leapSelectNode, targetVariable);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Colosseum.AI;
|
using Colosseum.AI;
|
||||||
|
using Colosseum.Abnormalities;
|
||||||
using Colosseum.Combat;
|
using Colosseum.Combat;
|
||||||
using Colosseum.Player;
|
using Colosseum.Player;
|
||||||
using Colosseum.Skills;
|
using Colosseum.Skills;
|
||||||
@@ -46,6 +47,9 @@ namespace Colosseum.Enemy
|
|||||||
[FormerlySerializedAs("downPunishPattern")]
|
[FormerlySerializedAs("downPunishPattern")]
|
||||||
[SerializeField] protected BossPatternData punishPattern;
|
[SerializeField] protected BossPatternData punishPattern;
|
||||||
|
|
||||||
|
[Tooltip("파티 누킹을 시험하는 시그니처 패턴")]
|
||||||
|
[SerializeField] protected BossPatternData signaturePattern;
|
||||||
|
|
||||||
[Header("Phase Thresholds")]
|
[Header("Phase Thresholds")]
|
||||||
[Tooltip("2페이즈 진입 체력 비율")]
|
[Tooltip("2페이즈 진입 체력 비율")]
|
||||||
[Range(0f, 1f)] [SerializeField] protected float phase2HealthThreshold = 0.75f;
|
[Range(0f, 1f)] [SerializeField] protected float phase2HealthThreshold = 0.75f;
|
||||||
@@ -79,6 +83,37 @@ namespace Colosseum.Enemy
|
|||||||
[FormerlySerializedAs("phase3SlamInterval")]
|
[FormerlySerializedAs("phase3SlamInterval")]
|
||||||
[Min(1)] [SerializeField] protected int phase3SecondaryInterval = 2;
|
[Min(1)] [SerializeField] protected int phase3SecondaryInterval = 2;
|
||||||
|
|
||||||
|
[Header("Signature Pattern")]
|
||||||
|
[Tooltip("시그니처 패턴을 사용하기 시작하는 최소 페이즈")]
|
||||||
|
[Min(1)] [SerializeField] protected int signatureMinPhase = 2;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 패턴 차단에 필요한 누적 피해 비율")]
|
||||||
|
[Range(0f, 1f)] [SerializeField] protected float signatureRequiredDamageRatio = 0.1f;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 차단 성공 시 보스가 멈추는 시간")]
|
||||||
|
[Min(0f)] [SerializeField] protected float signatureSuccessStaggerDuration = 2f;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 실패 시 모든 플레이어에게 적용할 디버프")]
|
||||||
|
[SerializeField] protected AbnormalityData signatureFailureAbnormality;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 실패 시 모든 플레이어에게 주는 기본 피해")]
|
||||||
|
[Min(0f)] [SerializeField] protected float signatureFailureDamage = 40f;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 실패 시 넉백이 적용되는 반경")]
|
||||||
|
[Min(0f)] [SerializeField] protected float signatureFailureKnockbackRadius = 8f;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 실패 시 다운이 적용되는 반경")]
|
||||||
|
[Min(0f)] [SerializeField] protected float signatureFailureDownRadius = 3f;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 실패 시 넉백 속도")]
|
||||||
|
[Min(0f)] [SerializeField] protected float signatureFailureKnockbackSpeed = 12f;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 실패 시 넉백 지속 시간")]
|
||||||
|
[Min(0f)] [SerializeField] protected float signatureFailureKnockbackDuration = 0.35f;
|
||||||
|
|
||||||
|
[Tooltip("시그니처 실패 시 다운 지속 시간")]
|
||||||
|
[Min(0f)] [SerializeField] protected float signatureFailureDownDuration = 2f;
|
||||||
|
|
||||||
[Header("Behavior")]
|
[Header("Behavior")]
|
||||||
[Tooltip("전용 컨텍스트 사용 시 기존 BehaviorGraph를 비활성화할지 여부")]
|
[Tooltip("전용 컨텍스트 사용 시 기존 BehaviorGraph를 비활성화할지 여부")]
|
||||||
[SerializeField] protected bool disableBehaviorGraph = true;
|
[SerializeField] protected bool disableBehaviorGraph = true;
|
||||||
@@ -92,6 +127,9 @@ namespace Colosseum.Enemy
|
|||||||
protected GameObject currentTarget;
|
protected GameObject currentTarget;
|
||||||
protected float nextTargetRefreshTime;
|
protected float nextTargetRefreshTime;
|
||||||
protected int meleePatternCounter;
|
protected int meleePatternCounter;
|
||||||
|
protected bool isSignaturePatternActive;
|
||||||
|
protected float signatureAccumulatedDamage;
|
||||||
|
protected float signatureRequiredDamage;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 전용 컨텍스트 사용 시 BehaviorGraph를 비활성화할지 여부
|
/// 전용 컨텍스트 사용 시 BehaviorGraph를 비활성화할지 여부
|
||||||
@@ -108,6 +146,11 @@ namespace Colosseum.Enemy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public float PunishSearchRadius => punishSearchRadius;
|
public float PunishSearchRadius => punishSearchRadius;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 시그니처 패턴 진행 여부
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSignaturePatternActive => isSignaturePatternActive;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 디버그 로그 출력 여부
|
/// 디버그 로그 출력 여부
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -185,6 +228,7 @@ namespace Colosseum.Enemy
|
|||||||
BossCombatPatternRole.Secondary => secondaryPattern,
|
BossCombatPatternRole.Secondary => secondaryPattern,
|
||||||
BossCombatPatternRole.Mobility => mobilityPattern,
|
BossCombatPatternRole.Mobility => mobilityPattern,
|
||||||
BossCombatPatternRole.Punish => punishPattern,
|
BossCombatPatternRole.Punish => punishPattern,
|
||||||
|
BossCombatPatternRole.Signature => signaturePattern,
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -321,6 +365,40 @@ namespace Colosseum.Enemy
|
|||||||
Debug.Log($"[{source}] {message}");
|
Debug.Log($"[{source}] {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 시그니처 패턴 사용 가능 여부를 반환합니다.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSignaturePatternReady()
|
||||||
|
{
|
||||||
|
if (!IsServer || bossEnemy == null || skillController == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (CurrentPatternPhase < signatureMinPhase)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (activePatternCoroutine != null || isSignaturePatternActive)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bossEnemy.IsDead || bossEnemy.IsTransitioning || skillController.IsPlayingAnimation)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return UsePatternAction.IsPatternReady(gameObject, signaturePattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 시그니처 패턴을 시작합니다.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryStartSignaturePattern(GameObject target)
|
||||||
|
{
|
||||||
|
if (!IsSignaturePatternReady())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GameObject resolvedTarget = IsValidHostileTarget(target) ? target : FindNearestLivingTarget();
|
||||||
|
currentTarget = resolvedTarget;
|
||||||
|
activePatternCoroutine = StartCoroutine(RunSignaturePatternCoroutine(signaturePattern, resolvedTarget));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual bool TryStartPrimaryLoopPattern()
|
protected virtual bool TryStartPrimaryLoopPattern()
|
||||||
{
|
{
|
||||||
if (currentTarget == null)
|
if (currentTarget == null)
|
||||||
@@ -515,6 +593,220 @@ namespace Colosseum.Enemy
|
|||||||
|
|
||||||
if (behaviorGraphAgent == null)
|
if (behaviorGraphAgent == null)
|
||||||
behaviorGraphAgent = GetComponent<BehaviorGraphAgent>();
|
behaviorGraphAgent = GetComponent<BehaviorGraphAgent>();
|
||||||
|
|
||||||
|
if (enemyBase != null)
|
||||||
|
{
|
||||||
|
enemyBase.OnDamageTaken -= HandleBossDamageTaken;
|
||||||
|
enemyBase.OnDamageTaken += HandleBossDamageTaken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkDespawn()
|
||||||
|
{
|
||||||
|
if (enemyBase != null)
|
||||||
|
{
|
||||||
|
enemyBase.OnDamageTaken -= HandleBossDamageTaken;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnNetworkDespawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator RunSignaturePatternCoroutine(BossPatternData pattern, GameObject target)
|
||||||
|
{
|
||||||
|
StopMovement();
|
||||||
|
|
||||||
|
isSignaturePatternActive = true;
|
||||||
|
signatureAccumulatedDamage = 0f;
|
||||||
|
signatureRequiredDamage = bossEnemy.MaxHealth * signatureRequiredDamageRatio;
|
||||||
|
|
||||||
|
bool interrupted = false;
|
||||||
|
bool completed = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < pattern.Steps.Count; i++)
|
||||||
|
{
|
||||||
|
if (HasMetSignatureBreakThreshold())
|
||||||
|
{
|
||||||
|
interrupted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternStep step = pattern.Steps[i];
|
||||||
|
if (step.Type == PatternStepType.Wait)
|
||||||
|
{
|
||||||
|
float remaining = step.Duration;
|
||||||
|
while (remaining > 0f)
|
||||||
|
{
|
||||||
|
if (HasMetSignatureBreakThreshold())
|
||||||
|
{
|
||||||
|
interrupted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bossEnemy == null || bossEnemy.IsDead)
|
||||||
|
{
|
||||||
|
completed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining -= Time.deltaTime;
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interrupted || !completed)
|
||||||
|
break;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step.Skill == null)
|
||||||
|
{
|
||||||
|
completed = false;
|
||||||
|
Debug.LogWarning($"[{GetType().Name}] 시그니처 패턴 스텝 스킬이 비어 있습니다. Pattern={pattern.PatternName}, Index={i}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skillController.ExecuteSkill(step.Skill))
|
||||||
|
{
|
||||||
|
completed = false;
|
||||||
|
LogDebug(GetType().Name, $"시그니처 스킬 실행 실패: {step.Skill.SkillName}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (skillController != null && skillController.IsPlayingAnimation)
|
||||||
|
{
|
||||||
|
if (HasMetSignatureBreakThreshold())
|
||||||
|
{
|
||||||
|
interrupted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bossEnemy == null || bossEnemy.IsDead)
|
||||||
|
{
|
||||||
|
completed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interrupted || !completed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interrupted)
|
||||||
|
{
|
||||||
|
skillController?.CancelSkill(SkillCancelReason.Interrupt);
|
||||||
|
UsePatternAction.MarkPatternUsed(gameObject, pattern);
|
||||||
|
LogDebug(GetType().Name, $"시그니처 차단 성공: 누적 피해 {signatureAccumulatedDamage:F1} / 필요 {signatureRequiredDamage:F1}");
|
||||||
|
|
||||||
|
if (signatureSuccessStaggerDuration > 0f)
|
||||||
|
{
|
||||||
|
if (enemyBase != null && enemyBase.Animator != null &&
|
||||||
|
HasAnimatorParameter(enemyBase.Animator, "Hit", AnimatorControllerParameterType.Trigger))
|
||||||
|
{
|
||||||
|
enemyBase.Animator.SetTrigger("Hit");
|
||||||
|
}
|
||||||
|
|
||||||
|
float endTime = Time.time + signatureSuccessStaggerDuration;
|
||||||
|
while (Time.time < endTime && bossEnemy != null && !bossEnemy.IsDead)
|
||||||
|
{
|
||||||
|
StopMovement();
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (completed)
|
||||||
|
{
|
||||||
|
UsePatternAction.MarkPatternUsed(gameObject, pattern);
|
||||||
|
LogDebug(GetType().Name, $"시그니처 실패: 누적 피해 {signatureAccumulatedDamage:F1} / 필요 {signatureRequiredDamage:F1}");
|
||||||
|
ExecuteSignatureFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
isSignaturePatternActive = false;
|
||||||
|
signatureAccumulatedDamage = 0f;
|
||||||
|
signatureRequiredDamage = 0f;
|
||||||
|
activePatternCoroutine = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteSignatureFailure()
|
||||||
|
{
|
||||||
|
PlayerNetworkController[] players = FindObjectsByType<PlayerNetworkController>(FindObjectsSortMode.None);
|
||||||
|
for (int i = 0; i < players.Length; i++)
|
||||||
|
{
|
||||||
|
PlayerNetworkController player = players[i];
|
||||||
|
if (player == null || player.IsDead || !player.gameObject.activeInHierarchy)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GameObject target = player.gameObject;
|
||||||
|
if (!IsValidHostileTarget(target))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
player.TakeDamage(signatureFailureDamage, gameObject);
|
||||||
|
|
||||||
|
AbnormalityManager abnormalityManager = target.GetComponent<AbnormalityManager>();
|
||||||
|
if (abnormalityManager != null && signatureFailureAbnormality != null)
|
||||||
|
{
|
||||||
|
abnormalityManager.ApplyAbnormality(signatureFailureAbnormality, gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
HitReactionController hitReactionController = target.GetComponent<HitReactionController>();
|
||||||
|
if (hitReactionController == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float distance = Vector3.Distance(transform.position, target.transform.position);
|
||||||
|
if (distance <= signatureFailureDownRadius)
|
||||||
|
{
|
||||||
|
hitReactionController.ApplyDown(signatureFailureDownDuration);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distance > signatureFailureKnockbackRadius)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector3 knockbackDirection = target.transform.position - transform.position;
|
||||||
|
knockbackDirection.y = 0f;
|
||||||
|
if (knockbackDirection.sqrMagnitude < 0.0001f)
|
||||||
|
{
|
||||||
|
knockbackDirection = transform.forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
hitReactionController.ApplyKnockback(knockbackDirection.normalized * signatureFailureKnockbackSpeed, signatureFailureKnockbackDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasMetSignatureBreakThreshold()
|
||||||
|
{
|
||||||
|
if (!isSignaturePatternActive)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (signatureRequiredDamage <= 0f)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return signatureAccumulatedDamage >= signatureRequiredDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasAnimatorParameter(Animator animator, string parameterName, AnimatorControllerParameterType parameterType)
|
||||||
|
{
|
||||||
|
if (animator == null || string.IsNullOrEmpty(parameterName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AnimatorControllerParameter[] parameters = animator.parameters;
|
||||||
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
AnimatorControllerParameter parameter = parameters[i];
|
||||||
|
if (parameter.type == parameterType && parameter.name == parameterName)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleBossDamageTaken(float damage)
|
||||||
|
{
|
||||||
|
if (!IsServer || !isSignaturePatternActive || damage <= 0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
signatureAccumulatedDamage += damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Colosseum.Enemy
|
|||||||
Secondary = 1,
|
Secondary = 1,
|
||||||
Mobility = 2,
|
Mobility = 2,
|
||||||
Punish = 3,
|
Punish = 3,
|
||||||
|
Signature = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -103,7 +103,9 @@ namespace Colosseum.Player
|
|||||||
{
|
{
|
||||||
if (isDead.Value || IsDamageImmune()) return;
|
if (isDead.Value || IsDamageImmune()) return;
|
||||||
|
|
||||||
currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage);
|
float finalDamage = damage * GetIncomingDamageMultiplier();
|
||||||
|
float actualDamage = Mathf.Min(finalDamage, currentHealth.Value);
|
||||||
|
currentHealth.Value = Mathf.Max(0f, currentHealth.Value - actualDamage);
|
||||||
|
|
||||||
if (currentHealth.Value <= 0f)
|
if (currentHealth.Value <= 0f)
|
||||||
{
|
{
|
||||||
@@ -272,8 +274,9 @@ namespace Colosseum.Player
|
|||||||
{
|
{
|
||||||
if (!IsServer || isDead.Value || IsDamageImmune()) return 0f;
|
if (!IsServer || isDead.Value || IsDamageImmune()) return 0f;
|
||||||
|
|
||||||
float actualDamage = Mathf.Min(damage, currentHealth.Value);
|
float finalDamage = damage * GetIncomingDamageMultiplier();
|
||||||
currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage);
|
float actualDamage = Mathf.Min(finalDamage, currentHealth.Value);
|
||||||
|
currentHealth.Value = Mathf.Max(0f, currentHealth.Value - actualDamage);
|
||||||
|
|
||||||
if (currentHealth.Value <= 0f)
|
if (currentHealth.Value <= 0f)
|
||||||
{
|
{
|
||||||
@@ -300,6 +303,14 @@ namespace Colosseum.Player
|
|||||||
{
|
{
|
||||||
return abnormalityManager != null && abnormalityManager.IsInvincible;
|
return abnormalityManager != null && abnormalityManager.IsInvincible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float GetIncomingDamageMultiplier()
|
||||||
|
{
|
||||||
|
if (abnormalityManager == null)
|
||||||
|
return 1f;
|
||||||
|
|
||||||
|
return Mathf.Max(0f, abnormalityManager.IncomingDamageMultiplier);
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace Colosseum.Skills
|
|||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Manual,
|
Manual,
|
||||||
|
Interrupt,
|
||||||
Death,
|
Death,
|
||||||
Stun,
|
Stun,
|
||||||
HitReaction,
|
HitReaction,
|
||||||
|
|||||||
Reference in New Issue
Block a user