feat: 젬 테스트 경로 및 보스 기절 디버그 추가
- 다중 젬 슬롯용 타입을 별도 스크립트로 분리하고 테스트 젬/로드아웃 자산 생성 경로를 정리 - 젬 테스트 전용 공격 스킬과 분리된 애니메이션 자산을 추가해 베이스 스킬 검증 경로를 마련 - PlayerSkillDebugMenu와 MPP 디버그 메뉴를 보강해 젬 프리셋 적용, 원격 테스트, 보스 기절 디버그 메뉴를 추가 - BossCombatBehaviorContext와 공통 BT 액션이 기절 상태를 존중하도록 수정해 보스 추적과 패턴 실행을 중단 - Unity 리프레시와 외부 빌드 통과를 확인하고 드로그전 및 MPP 기준 젬 프리셋 적용 흐름을 검증
This commit is contained in:
@@ -7,6 +7,13 @@ using System.Collections.Generic;
|
||||
using Colosseum.Skills;
|
||||
using Colosseum.Weapons;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Colosseum.Player
|
||||
{
|
||||
/// <summary>
|
||||
@@ -18,9 +25,46 @@ namespace Colosseum.Player
|
||||
{
|
||||
private const int ExpectedSkillSlotCount = 7;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static readonly string[] TankLoadoutPaths =
|
||||
{
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_베기.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_도발.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_방어태세.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_돌진.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_철벽.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_찌르기.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_구르기.asset",
|
||||
};
|
||||
|
||||
private static readonly string[] SupportLoadoutPaths =
|
||||
{
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_베기.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_치유.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_광역치유.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_보호막.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_돌진.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_투사체.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_구르기.asset",
|
||||
};
|
||||
|
||||
private static readonly string[] DpsLoadoutPaths =
|
||||
{
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_베기.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_찌르기.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_회전베기.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_돌진.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_투사체.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_보호막.asset",
|
||||
"Assets/_Game/Data/Skills/Data_Skill_Player_구르기.asset",
|
||||
};
|
||||
#endif
|
||||
|
||||
[Header("Skill Slots")]
|
||||
[Tooltip("각 슬롯에 등록할 스킬 데이터 (6개 + 추가 슬롯)")]
|
||||
[SerializeField] private SkillData[] skillSlots = new SkillData[ExpectedSkillSlotCount];
|
||||
[Tooltip("각 슬롯의 베이스 스킬 + 젬 조합")]
|
||||
[SerializeField] private SkillLoadoutEntry[] skillLoadoutEntries = new SkillLoadoutEntry[ExpectedSkillSlotCount];
|
||||
|
||||
[Header("References")]
|
||||
[Tooltip("SkillController (없으면 자동 검색)")]
|
||||
@@ -35,6 +79,7 @@ namespace Colosseum.Player
|
||||
private InputSystem_Actions inputActions;
|
||||
|
||||
public SkillData[] SkillSlots => skillSlots;
|
||||
public SkillLoadoutEntry[] SkillLoadoutEntries => skillLoadoutEntries;
|
||||
|
||||
/// <summary>
|
||||
/// 스킬 슬롯 구성이 변경되었을 때 호출됩니다.
|
||||
@@ -44,6 +89,10 @@ namespace Colosseum.Player
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
SyncLegacySkillsToLoadoutEntries();
|
||||
EnsureRuntimeReferences();
|
||||
ApplyEditorMultiplayerLoadoutIfNeeded();
|
||||
|
||||
if (!IsOwner)
|
||||
{
|
||||
@@ -51,35 +100,6 @@ namespace Colosseum.Player
|
||||
return;
|
||||
}
|
||||
|
||||
// SkillController 참조 확인
|
||||
if (skillController == null)
|
||||
{
|
||||
skillController = GetComponent<SkillController>();
|
||||
if (skillController == null)
|
||||
{
|
||||
Debug.LogError("PlayerSkillInput: SkillController not found!");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// PlayerNetworkController 참조 확인
|
||||
if (networkController == null)
|
||||
{
|
||||
networkController = GetComponent<PlayerNetworkController>();
|
||||
}
|
||||
|
||||
// WeaponEquipment 참조 확인
|
||||
if (weaponEquipment == null)
|
||||
{
|
||||
weaponEquipment = GetComponent<WeaponEquipment>();
|
||||
}
|
||||
|
||||
if (actionState == null)
|
||||
{
|
||||
actionState = GetOrCreateActionState();
|
||||
}
|
||||
|
||||
InitializeInputActions();
|
||||
}
|
||||
|
||||
@@ -113,11 +133,15 @@ namespace Colosseum.Player
|
||||
private void Awake()
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
SyncLegacySkillsToLoadoutEntries();
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
SyncLegacySkillsToLoadoutEntries();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
@@ -150,6 +174,63 @@ namespace Colosseum.Player
|
||||
skillSlots = resizedSlots;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 슬롯별 로드아웃 엔트리 배열을 보정합니다.
|
||||
/// </summary>
|
||||
private void EnsureSkillLoadoutCapacity()
|
||||
{
|
||||
if (skillLoadoutEntries == null || skillLoadoutEntries.Length != ExpectedSkillSlotCount)
|
||||
{
|
||||
SkillLoadoutEntry[] resizedEntries = new SkillLoadoutEntry[ExpectedSkillSlotCount];
|
||||
if (skillLoadoutEntries != null)
|
||||
{
|
||||
int copyCount = Mathf.Min(skillLoadoutEntries.Length, resizedEntries.Length);
|
||||
for (int i = 0; i < copyCount; i++)
|
||||
{
|
||||
resizedEntries[i] = skillLoadoutEntries[i];
|
||||
}
|
||||
}
|
||||
|
||||
skillLoadoutEntries = resizedEntries;
|
||||
}
|
||||
|
||||
for (int i = 0; i < skillLoadoutEntries.Length; i++)
|
||||
{
|
||||
if (skillLoadoutEntries[i] == null)
|
||||
skillLoadoutEntries[i] = new SkillLoadoutEntry();
|
||||
|
||||
skillLoadoutEntries[i].EnsureGemSlotCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 기존 SkillData 직렬화와 새 로드아웃 엔트리 구조를 동기화합니다.
|
||||
/// </summary>
|
||||
private void SyncLegacySkillsToLoadoutEntries()
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
|
||||
for (int i = 0; i < skillSlots.Length; i++)
|
||||
{
|
||||
SkillLoadoutEntry entry = skillLoadoutEntries[i];
|
||||
SkillData legacySkill = skillSlots[i];
|
||||
|
||||
if (entry.BaseSkill == null && legacySkill != null)
|
||||
{
|
||||
entry.SetBaseSkill(legacySkill);
|
||||
}
|
||||
else if (legacySkill == null && entry.BaseSkill != null)
|
||||
{
|
||||
skillSlots[i] = entry.BaseSkill;
|
||||
}
|
||||
else if (entry.BaseSkill != legacySkill)
|
||||
{
|
||||
skillSlots[i] = entry.BaseSkill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanupInputActions()
|
||||
{
|
||||
if (inputActions != null)
|
||||
@@ -166,7 +247,8 @@ namespace Colosseum.Player
|
||||
if (slotIndex < 0 || slotIndex >= skillSlots.Length)
|
||||
return;
|
||||
|
||||
SkillData skill = skillSlots[slotIndex];
|
||||
SkillLoadoutEntry loadoutEntry = GetSkillLoadout(slotIndex);
|
||||
SkillData skill = loadoutEntry != null ? loadoutEntry.BaseSkill : null;
|
||||
if (skill == null)
|
||||
{
|
||||
Debug.Log($"Skill slot {slotIndex + 1} is empty");
|
||||
@@ -191,7 +273,7 @@ namespace Colosseum.Player
|
||||
}
|
||||
|
||||
// 마나 비용 체크 (무기 배율 적용)
|
||||
float actualManaCost = GetActualManaCost(skill);
|
||||
float actualManaCost = GetActualManaCost(loadoutEntry);
|
||||
if (networkController != null && networkController.Mana < actualManaCost)
|
||||
{
|
||||
Debug.Log($"Not enough mana for skill: {skill.SkillName}");
|
||||
@@ -211,7 +293,8 @@ namespace Colosseum.Player
|
||||
if (slotIndex < 0 || slotIndex >= skillSlots.Length)
|
||||
return;
|
||||
|
||||
SkillData skill = skillSlots[slotIndex];
|
||||
SkillLoadoutEntry loadoutEntry = GetSkillLoadout(slotIndex);
|
||||
SkillData skill = loadoutEntry != null ? loadoutEntry.BaseSkill : null;
|
||||
if (skill == null) return;
|
||||
|
||||
// 서버에서 다시 검증
|
||||
@@ -222,7 +305,7 @@ namespace Colosseum.Player
|
||||
return;
|
||||
|
||||
// 마나 비용 체크 (무기 배율 적용)
|
||||
float actualManaCost = GetActualManaCost(skill);
|
||||
float actualManaCost = GetActualManaCost(loadoutEntry);
|
||||
if (networkController != null && networkController.Mana < actualManaCost)
|
||||
return;
|
||||
|
||||
@@ -245,21 +328,22 @@ namespace Colosseum.Player
|
||||
if (slotIndex < 0 || slotIndex >= skillSlots.Length)
|
||||
return;
|
||||
|
||||
SkillData skill = skillSlots[slotIndex];
|
||||
SkillLoadoutEntry loadoutEntry = GetSkillLoadout(slotIndex);
|
||||
SkillData skill = loadoutEntry != null ? loadoutEntry.BaseSkill : null;
|
||||
if (skill == null) return;
|
||||
|
||||
// 모든 클라이언트에서 스킬 실행 (애니메이션 포함)
|
||||
skillController.ExecuteSkill(skill);
|
||||
skillController.ExecuteSkill(loadoutEntry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 무기 마나 배율이 적용된 실제 마나 비용 계산
|
||||
/// </summary>
|
||||
private float GetActualManaCost(SkillData skill)
|
||||
private float GetActualManaCost(SkillLoadoutEntry loadoutEntry)
|
||||
{
|
||||
if (skill == null) return 0f;
|
||||
if (loadoutEntry == null || loadoutEntry.BaseSkill == null) return 0f;
|
||||
|
||||
float baseCost = skill.ManaCost;
|
||||
float baseCost = loadoutEntry.GetResolvedManaCost();
|
||||
float multiplier = weaponEquipment != null ? weaponEquipment.ManaCostMultiplier : 1f;
|
||||
|
||||
return baseCost * multiplier;
|
||||
@@ -271,10 +355,27 @@ namespace Colosseum.Player
|
||||
public SkillData GetSkill(int slotIndex)
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
SyncLegacySkillsToLoadoutEntries();
|
||||
|
||||
if (slotIndex < 0 || slotIndex >= skillSlots.Length)
|
||||
return null;
|
||||
return skillSlots[slotIndex];
|
||||
return skillLoadoutEntries[slotIndex] != null ? skillLoadoutEntries[slotIndex].BaseSkill : skillSlots[slotIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 슬롯 엔트리 접근자
|
||||
/// </summary>
|
||||
public SkillLoadoutEntry GetSkillLoadout(int slotIndex)
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
SyncLegacySkillsToLoadoutEntries();
|
||||
|
||||
if (slotIndex < 0 || slotIndex >= skillLoadoutEntries.Length)
|
||||
return null;
|
||||
|
||||
return skillLoadoutEntries[slotIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -288,6 +389,7 @@ namespace Colosseum.Player
|
||||
return;
|
||||
|
||||
skillSlots[slotIndex] = skill;
|
||||
skillLoadoutEntries[slotIndex].SetBaseSkill(skill);
|
||||
OnSkillSlotsChanged?.Invoke();
|
||||
}
|
||||
|
||||
@@ -305,16 +407,92 @@ namespace Colosseum.Player
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
skillSlots[i] = skills[i];
|
||||
skillLoadoutEntries[i].SetBaseSkill(skills[i]);
|
||||
}
|
||||
|
||||
for (int i = count; i < skillSlots.Length; i++)
|
||||
{
|
||||
skillSlots[i] = null;
|
||||
skillLoadoutEntries[i].SetBaseSkill(null);
|
||||
}
|
||||
|
||||
OnSkillSlotsChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 슬롯 엔트리를 직접 설정합니다.
|
||||
/// </summary>
|
||||
public void SetSkillLoadout(int slotIndex, SkillLoadoutEntry loadoutEntry)
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
|
||||
if (slotIndex < 0 || slotIndex >= skillLoadoutEntries.Length)
|
||||
return;
|
||||
|
||||
skillLoadoutEntries[slotIndex] = loadoutEntry != null ? loadoutEntry.CreateCopy() : new SkillLoadoutEntry();
|
||||
skillLoadoutEntries[slotIndex].EnsureGemSlotCapacity();
|
||||
skillSlots[slotIndex] = skillLoadoutEntries[slotIndex].BaseSkill;
|
||||
OnSkillSlotsChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 슬롯의 특정 젬 슬롯을 갱신합니다.
|
||||
/// </summary>
|
||||
public void SetSkillGem(int slotIndex, int gemSlotIndex, SkillGemData gem)
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
|
||||
if (slotIndex < 0 || slotIndex >= skillLoadoutEntries.Length)
|
||||
return;
|
||||
|
||||
skillLoadoutEntries[slotIndex].SetGem(gemSlotIndex, gem);
|
||||
skillSlots[slotIndex] = skillLoadoutEntries[slotIndex].BaseSkill;
|
||||
OnSkillSlotsChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 슬롯 엔트리 전체를 한 번에 갱신합니다.
|
||||
/// </summary>
|
||||
public void SetSkillLoadouts(IReadOnlyList<SkillLoadoutEntry> loadouts)
|
||||
{
|
||||
EnsureSkillSlotCapacity();
|
||||
EnsureSkillLoadoutCapacity();
|
||||
|
||||
if (loadouts == null)
|
||||
return;
|
||||
|
||||
int count = Mathf.Min(skillLoadoutEntries.Length, loadouts.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
skillLoadoutEntries[i] = loadouts[i] != null ? loadouts[i].CreateCopy() : new SkillLoadoutEntry();
|
||||
skillLoadoutEntries[i].EnsureGemSlotCapacity();
|
||||
skillSlots[i] = skillLoadoutEntries[i].BaseSkill;
|
||||
}
|
||||
|
||||
for (int i = count; i < skillLoadoutEntries.Length; i++)
|
||||
{
|
||||
skillLoadoutEntries[i] = new SkillLoadoutEntry();
|
||||
skillLoadoutEntries[i].EnsureGemSlotCapacity();
|
||||
skillSlots[i] = null;
|
||||
}
|
||||
|
||||
OnSkillSlotsChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 프리셋 기반으로 슬롯 엔트리를 일괄 적용합니다.
|
||||
/// </summary>
|
||||
public void ApplyLoadoutPreset(PlayerLoadoutPreset preset)
|
||||
{
|
||||
if (preset == null)
|
||||
return;
|
||||
|
||||
preset.EnsureSlotCapacity();
|
||||
SetSkillLoadouts(preset.Slots);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 남은 쿨타임 조회
|
||||
/// </summary>
|
||||
@@ -331,6 +509,8 @@ namespace Colosseum.Player
|
||||
/// </summary>
|
||||
public bool CanUseSkill(int slotIndex)
|
||||
{
|
||||
EnsureRuntimeReferences();
|
||||
|
||||
SkillData skill = GetSkill(slotIndex);
|
||||
if (skill == null) return false;
|
||||
|
||||
@@ -352,6 +532,44 @@ namespace Colosseum.Player
|
||||
OnSkillInput(slotIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 서버 권한에서 특정 슬롯 스킬을 강제로 실행합니다.
|
||||
/// 멀티플레이 테스트 시 원격 플레이어 스킬을 호스트에서 검증할 때 사용합니다.
|
||||
/// </summary>
|
||||
public bool DebugExecuteSkillAsServer(int slotIndex)
|
||||
{
|
||||
if (!IsServer)
|
||||
return false;
|
||||
|
||||
EnsureRuntimeReferences();
|
||||
|
||||
if (slotIndex < 0 || slotIndex >= skillSlots.Length)
|
||||
return false;
|
||||
|
||||
SkillLoadoutEntry loadoutEntry = GetSkillLoadout(slotIndex);
|
||||
SkillData skill = loadoutEntry != null ? loadoutEntry.BaseSkill : null;
|
||||
if (skill == null)
|
||||
return false;
|
||||
|
||||
if (actionState != null && !actionState.CanStartSkill(skill))
|
||||
return false;
|
||||
|
||||
if (skillController == null || skillController.IsExecutingSkill || skillController.IsOnCooldown(skill))
|
||||
return false;
|
||||
|
||||
float actualManaCost = GetActualManaCost(loadoutEntry);
|
||||
if (networkController != null && networkController.Mana < actualManaCost)
|
||||
return false;
|
||||
|
||||
if (networkController != null && actualManaCost > 0f)
|
||||
{
|
||||
networkController.UseManaRpc(actualManaCost);
|
||||
}
|
||||
|
||||
BroadcastSkillExecutionRpc(slotIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnSkill1Performed(InputAction.CallbackContext context) => OnSkillInput(0);
|
||||
|
||||
private void OnSkill2Performed(InputAction.CallbackContext context) => OnSkillInput(1);
|
||||
@@ -374,5 +592,123 @@ namespace Colosseum.Player
|
||||
|
||||
return gameObject.AddComponent<PlayerActionState>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 로컬/원격 여부와 관계없이 런타임 참조를 보정합니다.
|
||||
/// 서버에서 원격 플레이어 스킬을 디버그 실행할 때도 동일한 검증 경로를 쓰기 위해 필요합니다.
|
||||
/// </summary>
|
||||
private void EnsureRuntimeReferences()
|
||||
{
|
||||
if (skillController == null)
|
||||
{
|
||||
skillController = GetComponent<SkillController>();
|
||||
}
|
||||
|
||||
if (networkController == null)
|
||||
{
|
||||
networkController = GetComponent<PlayerNetworkController>();
|
||||
}
|
||||
|
||||
if (weaponEquipment == null)
|
||||
{
|
||||
weaponEquipment = GetComponent<WeaponEquipment>();
|
||||
}
|
||||
|
||||
if (actionState == null)
|
||||
{
|
||||
actionState = GetOrCreateActionState();
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// MPP 환경에서는 메인 에디터에 탱커, 가상 플레이어 복제본에 지원 프리셋을 자동 적용합니다.
|
||||
/// </summary>
|
||||
private void ApplyEditorMultiplayerLoadoutIfNeeded()
|
||||
{
|
||||
if (!ShouldApplyMppmLoadout())
|
||||
return;
|
||||
|
||||
string[] loadoutPaths = GetMppmLoadoutPathsForOwner();
|
||||
List<SkillData> loadout = LoadSkillAssets(loadoutPaths);
|
||||
if (loadout == null || loadout.Count == 0)
|
||||
return;
|
||||
|
||||
SetSkills(loadout);
|
||||
Debug.Log($"[MPP] 자동 프리셋 적용: {GetMppmLoadoutLabel()} (OwnerClientId={OwnerClientId})");
|
||||
}
|
||||
|
||||
private static bool ShouldApplyMppmLoadout()
|
||||
{
|
||||
string systemDataPath = GetMppmSystemDataPath();
|
||||
if (string.IsNullOrEmpty(systemDataPath) || !File.Exists(systemDataPath))
|
||||
return false;
|
||||
|
||||
string json = File.ReadAllText(systemDataPath);
|
||||
if (!json.Contains("\"IsMppmActive\": true", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
return Regex.Matches(json, "\"Active\"\\s*:\\s*true").Count > 1;
|
||||
}
|
||||
|
||||
private static string GetMppmSystemDataPath()
|
||||
{
|
||||
string projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
|
||||
if (IsVirtualProjectCloneInEditor())
|
||||
return Path.GetFullPath(Path.Combine(projectRoot, "..", "SystemData.json"));
|
||||
|
||||
return Path.Combine(projectRoot, "Library", "VP", "SystemData.json");
|
||||
}
|
||||
|
||||
private static bool IsVirtualProjectCloneInEditor()
|
||||
{
|
||||
string[] arguments = Environment.GetCommandLineArgs();
|
||||
for (int i = 0; i < arguments.Length; i++)
|
||||
{
|
||||
if (string.Equals(arguments[i], "--virtual-project-clone", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<SkillData> LoadSkillAssets(IReadOnlyList<string> assetPaths)
|
||||
{
|
||||
List<SkillData> skills = new List<SkillData>(assetPaths.Count);
|
||||
for (int i = 0; i < assetPaths.Count; i++)
|
||||
{
|
||||
SkillData skill = AssetDatabase.LoadAssetAtPath<SkillData>(assetPaths[i]);
|
||||
if (skill == null)
|
||||
{
|
||||
Debug.LogWarning($"[MPP] 스킬 에셋을 찾지 못했습니다: {assetPaths[i]}");
|
||||
return null;
|
||||
}
|
||||
|
||||
skills.Add(skill);
|
||||
}
|
||||
|
||||
return skills;
|
||||
}
|
||||
|
||||
private string[] GetMppmLoadoutPathsForOwner()
|
||||
{
|
||||
return OwnerClientId switch
|
||||
{
|
||||
0 => TankLoadoutPaths,
|
||||
1 => SupportLoadoutPaths,
|
||||
_ => DpsLoadoutPaths,
|
||||
};
|
||||
}
|
||||
|
||||
private string GetMppmLoadoutLabel()
|
||||
{
|
||||
return OwnerClientId switch
|
||||
{
|
||||
0 => "탱커",
|
||||
1 => "지원",
|
||||
_ => "딜러",
|
||||
};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user