using System.Collections.Generic; // 리스트와 딕셔너리 기능을 사용할거에요 -> System.Collections.Generic을 using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을 using UnityEngine.Events; // 유니티 이벤트 기능을 사용할거에요 -> UnityEngine.Events를 /// /// 보스 카운터 시스템 메인 컨트롤러. /// (수정됨: Config나 Player가 없어도 에러가 나지 않도록 안전장치가 추가된 버전) /// public class BossCounterSystem : MonoBehaviour // 클래스를 선언할거에요 -> MonoBehaviour를 상속받는 BossCounterSystem을 { [Header("참조")] // 인스펙터 창에 제목을 표시할거에요 -> 참조 를 [SerializeField] private BossCounterConfig config; // 변수를 선언할거에요 -> 카운터 설정 파일(BossCounterConfig)을 담을 config를 [Header("이벤트 (연출/UI 연동)")] // 인스펙터 창에 제목을 표시할거에요 -> 이벤트 (연출/UI 연동) 을 [Tooltip("카운터 모드가 활성화될 때 발생 (CounterType 전달)")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을 public UnityEvent OnCounterActivated; // 이벤트를 선언할거에요 -> 카운터가 켜질 때 알릴 OnCounterActivated를 [Tooltip("카운터 모드가 비활성화될 때 발생")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을 public UnityEvent OnCounterDeactivated; // 이벤트를 선언할거에요 -> 카운터가 꺼질 때 알릴 OnCounterDeactivated를 [Tooltip("보스가 카운터 패턴을 선택했을 때 발생 (패턴 이름 전달)")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을 public UnityEvent OnCounterPatternSelected; // 이벤트를 선언할거에요 -> 패턴이 선택됐을 때 알릴 OnCounterPatternSelected를 [Tooltip("플레이어가 습관을 바꿔서 보상받을 때 발생")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을 public UnityEvent OnHabitChangeRewarded; // 이벤트를 선언할거에요 -> 습관 변경 보상을 알릴 OnHabitChangeRewarded를 // ── 카운터 모드 상태 ── private Dictionary activeCounters = new Dictionary() // 변수를 선언하고 초기화할거에요 -> 각 카운터 타입의 활성 상태를 저장할 딕셔너리 activeCounters를 { { CounterType.Dodge, false }, // 값을 넣을거에요 -> 회피 카운터 초기값을 거짓(false)으로 { CounterType.Aim, false }, // 값을 넣을거에요 -> 조준 카운터 초기값을 거짓(false)으로 { CounterType.Pierce, false } // 값을 넣을거에요 -> 관통 카운터 초기값을 거짓(false)으로 }; // ── Decay 타이머 ── private Dictionary counterTimers = new Dictionary() // 변수를 선언하고 초기화할거에요 -> 각 카운터의 남은 지속 시간을 저장할 딕셔너리 counterTimers를 { { CounterType.Dodge, 0f }, // 값을 넣을거에요 -> 회피 카운터 타이머를 0으로 { CounterType.Aim, 0f }, // 값을 넣을거에요 -> 조준 카운터 타이머를 0으로 { CounterType.Pierce, 0f } // 값을 넣을거에요 -> 관통 카운터 타이머를 0으로 }; // ── 쿨타임 ── private float lastCounterPatternTime = -100f; // 변수를 선언할거에요 -> 마지막 카운터 패턴 사용 시간을 lastCounterPatternTime에 (초기값 -100) // ── 습관 변경 보상 추적 ── private HashSet previousRunCounters = new HashSet(); // 변수를 선언할거에요 -> 지난 런에서 당했던 카운터 목록을 previousRunCounters에 private HashSet currentRunActivatedCounters = new HashSet(); // 변수를 선언할거에요 -> 이번 런에서 발동된 카운터 목록을 currentRunActivatedCounters에 private bool habitChangeRewardGranted = false; // 변수를 선언할거에요 -> 보상이 이미 지급되었는지 여부를 habitChangeRewardGranted에 // ── 보스 패턴 가중치 정의 ── [System.Serializable] // 직렬화할거에요 -> 인스펙터에서 수정할 수 있게 BossPattern 클래스를 public class BossPattern // 내부 클래스를 선언할거에요 -> 보스 패턴 정보를 담을 BossPattern을 { public string patternName; // 변수를 선언할거에요 -> 패턴 이름을 저장할 patternName을 public float baseWeight = 1f; // 변수를 선언할거에요 -> 기본 가중치(확률)를 baseWeight에 [Tooltip("이 패턴이 카운터 패턴인 경우 해당 타입")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을 public CounterType counterType = CounterType.None; // 변수를 선언할거에요 -> 이 패턴이 어떤 카운터 타입인지 지정할 counterType을 [Tooltip("카운터 발동 시 보조적으로 가중치가 올라가는 타입")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을 public CounterType subCounterType = CounterType.None; // 변수를 선언할거에요 -> 보너스 가중치를 받을 보조 카운터 타입 subCounterType을 } [Header("보스 패턴 목록")] // 인스펙터 창에 제목을 표시할거에요 -> 보스 패턴 목록 을 [SerializeField] private List bossPatterns = new List(); // 리스트를 선언할거에요 -> 보스의 모든 패턴 정보를 담을 bossPatterns를 // ═══════════════════════════════════════════ // 초기화 // ═══════════════════════════════════════════ private void Start() // 함수를 실행할거에요 -> 게임 시작 시 호출되는 Start를 { // 🚨 [안전장치] 설정 파일이 없으면 경고 출력 if (config == null) // 조건이 맞으면 실행할거에요 -> 설정 파일(config)이 연결되지 않았다면 { Debug.LogWarning("⚠ [BossCounterSystem] Config(설정 파일)가 없습니다! AI가 정상 작동하지 않을 수 있습니다."); // 경고 로그를 출력할거에요 -> 설정 파일 누락 경고를 } } /// /// 보스 전투 시작 시 호출. 이전 런에서 발동된 카운터 정보를 기반으로 초기화. /// public void InitializeBattle() // 함수를 선언할거에요 -> 전투 시작 시 초기화를 담당할 InitializeBattle을 { // 이전 런에서 잠금 해제된 카운터들을 기록 (습관 변경 보상 판단용) previousRunCounters.Clear(); // 비울거에요 -> 이전 런 카운터 목록을 currentRunActivatedCounters.Clear(); // 비울거에요 -> 이번 런 카운터 목록을 habitChangeRewardGranted = false; // 초기화할거에요 -> 보상 지급 여부를 거짓(false)으로 var persistence = BossCounterPersistence.Instance; // 변수를 가져올거에요 -> 영구 저장소 인스턴스를 persistence에 if (persistence != null) // 조건이 맞으면 실행할거에요 -> 저장소가 존재한다면 { if (persistence.Data.dodgeCounterActivations > 0 && persistence.IsUnlocked(CounterType.Dodge)) // 조건이 맞으면 실행할거에요 -> 회피 카운터 기록이 있고 해제되었다면 previousRunCounters.Add(CounterType.Dodge); // 추가할거에요 -> 회피 타입을 이전 런 목록에 if (persistence.Data.aimCounterActivations > 0 && persistence.IsUnlocked(CounterType.Aim)) // 조건이 맞으면 실행할거에요 -> 조준 카운터 기록이 있고 해제되었다면 previousRunCounters.Add(CounterType.Aim); // 추가할거에요 -> 조준 타입을 이전 런 목록에 if (persistence.Data.pierceCounterActivations > 0 && persistence.IsUnlocked(CounterType.Pierce)) // 조건이 맞으면 실행할거에요 -> 관통 카운터 기록이 있고 해제되었다면 previousRunCounters.Add(CounterType.Pierce); // 추가할거에요 -> 관통 타입을 이전 런 목록에 } // 모든 카운터 모드 OFF foreach (var type in new[] { CounterType.Dodge, CounterType.Aim, CounterType.Pierce }) // 반복할거에요 -> 모든 카운터 타입(회피, 조준, 관통)에 대해 { activeCounters[type] = false; // 상태를 바꿀거에요 -> 해당 카운터를 비활성화(false)로 counterTimers[type] = 0f; // 값을 바꿀거에요 -> 해당 카운터 타이머를 0으로 } lastCounterPatternTime = -100f; // 값을 초기화할거에요 -> 마지막 패턴 사용 시간을 -100으로 Debug.Log($"[BossCounter] 전투 시작. 이전 런 카운터: [{string.Join(", ", previousRunCounters)}]"); // 로그를 출력할거에요 -> 전투 시작 알림과 이전 런 정보를 } // ═══════════════════════════════════════════ // 매 프레임 업데이트 // ═══════════════════════════════════════════ private void Update() // 함수를 실행할거에요 -> 매 프레임마다 호출되는 Update를 { // 🚨 [안전장치] 플레이어 트래커나 설정 파일이 없으면 실행 중지 (NullReferenceException 방지) if (PlayerBehaviorTracker.Instance == null || config == null) return; // 조건이 맞으면 중단할거에요 -> 플레이어 추적기나 설정 파일이 없다면 EvaluateCounters(); // 함수를 실행할거에요 -> 카운터 발동 조건을 검사하는 EvaluateCounters를 DecayCounters(); // 함수를 실행할거에요 -> 카운터 지속 시간을 관리하는 DecayCounters를 CheckHabitChangeReward(); // 함수를 실행할거에요 -> 습관 변경 보상을 체크하는 CheckHabitChangeReward를 } /// 플레이어 행동 데이터를 읽고 카운터 모드 ON/OFF 판단 private void EvaluateCounters() // 함수를 선언할거에요 -> 카운터 발동 여부를 판단하는 EvaluateCounters를 { // 🚨 [안전장치] Config가 없으면 계산 불가 if (config == null) return; // 조건이 맞으면 중단할거에요 -> 설정 파일이 없다면 var tracker = PlayerBehaviorTracker.Instance; // 변수를 가져올거에요 -> 플레이어 행동 추적기를 tracker에 var persistence = BossCounterPersistence.Instance; // 변수를 가져올거에요 -> 영구 저장소를 persistence에 // ── 회피 카운터 ── bool dodgeUnlocked = persistence != null && persistence.IsUnlocked(CounterType.Dodge); // 값을 확인할거에요 -> 회피 카운터가 해금되었는지 여부를 int dodgeThreshold = config.GetEffectiveDodgeThreshold(dodgeUnlocked); // 값을 가져올거에요 -> 현재 적용할 회피 임계값을 if (tracker.DodgeCount >= dodgeThreshold) // 조건이 맞으면 실행할거에요 -> 플레이어 회피 횟수가 임계값 이상이라면 { // 잠금 해제 안 된 상태면 첫 발동 시 잠금 해제 (첫 런에서도 발동 가능) // 잠금 해제된 상태면 더 낮은 임계치로 발동 ActivateCounter(CounterType.Dodge); // 함수를 실행할거에요 -> 회피 카운터를 발동시키는 ActivateCounter를 } // ── 조준 카운터 ── bool aimUnlocked = persistence != null && persistence.IsUnlocked(CounterType.Aim); // 값을 확인할거에요 -> 조준 카운터가 해금되었는지 여부를 float aimThreshold = config.GetEffectiveAimThreshold(aimUnlocked); // 값을 가져올거에요 -> 현재 적용할 조준 임계값을 if (tracker.AimHoldTime >= aimThreshold) // 조건이 맞으면 실행할거에요 -> 플레이어 조준 시간이 임계값 이상이라면 { ActivateCounter(CounterType.Aim); // 함수를 실행할거에요 -> 조준 카운터를 발동시키는 ActivateCounter를 } // ── 관통 카운터 ── bool pierceUnlocked = persistence != null && persistence.IsUnlocked(CounterType.Pierce); // 값을 확인할거에요 -> 관통 카운터가 해금되었는지 여부를 float pierceThreshold = config.GetEffectivePierceThreshold(pierceUnlocked); // 값을 가져올거에요 -> 현재 적용할 관통 임계값을 if (tracker.TotalShotsInWindow >= config.minShotsForPierceCheck // 조건이 맞으면 실행할거에요 -> 최소 발사 횟수를 충족하고 && tracker.PierceRatio >= pierceThreshold) // 조건이 맞으면 실행할거에요 -> 관통 공격 비율이 임계값 이상이라면 { ActivateCounter(CounterType.Pierce); // 함수를 실행할거에요 -> 관통 카운터를 발동시키는 ActivateCounter를 } } private void ActivateCounter(CounterType type) // 함수를 선언할거에요 -> 특정 카운터를 활성화하는 ActivateCounter를 { // 🚨 [안전장치] Config 확인 if (config == null) return; // 조건이 맞으면 중단할거에요 -> 설정 파일이 없다면 counterTimers[type] = config.counterDecayTime; // 값을 설정할거에요 -> 해당 카운터의 지속 시간을 설정값으로 if (!activeCounters[type]) // 조건이 맞으면 실행할거에요 -> 해당 카운터가 현재 꺼져 있다면 { activeCounters[type] = true; // 상태를 바꿀거에요 -> 카운터 활성 상태를 참(true)으로 currentRunActivatedCounters.Add(type); // 추가할거에요 -> 이번 런 발동 목록에 해당 타입을 // 영구 잠금 해제 var persistence = BossCounterPersistence.Instance; // 변수를 가져올거에요 -> 영구 저장소를 persistence에 if (persistence != null) // 조건이 맞으면 실행할거에요 -> 저장소가 있다면 { persistence.UnlockCounter(type); // 함수를 실행할거에요 -> 해당 카운터를 영구 해금하는 UnlockCounter를 persistence.RecordActivation(type); // 함수를 실행할거에요 -> 발동 횟수를 기록하는 RecordActivation을 } OnCounterActivated?.Invoke(type); // 이벤트를 실행할거에요 -> 카운터 활성화 알림 이벤트를 Debug.Log($"[BossCounter] {type} 카운터 활성화!"); // 로그를 출력할거에요 -> 카운터 활성화 메시지를 } } /// 시간이 지나면 카운터 모드 자동 해제 private void DecayCounters() // 함수를 선언할거에요 -> 시간이 지나면 카운터를 끄는 DecayCounters를 { foreach (var type in new[] { CounterType.Dodge, CounterType.Aim, CounterType.Pierce }) // 반복할거에요 -> 모든 카운터 타입에 대해 { if (!activeCounters[type]) continue; // 조건이 맞으면 건너뛸거에요 -> 이미 꺼져 있는 카운터라면 counterTimers[type] -= Time.deltaTime; // 값을 뺄거에요 -> 남은 시간에서 프레임 시간을 if (counterTimers[type] <= 0f) // 조건이 맞으면 실행할거에요 -> 남은 시간이 0 이하라면 { activeCounters[type] = false; // 상태를 바꿀거에요 -> 카운터 활성 상태를 거짓(false)으로 OnCounterDeactivated?.Invoke(type); // 이벤트를 실행할거에요 -> 카운터 비활성화 알림 이벤트를 Debug.Log($"[BossCounter] {type} 카운터 Decay로 비활성화"); // 로그를 출력할거에요 -> 시간 만료로 인한 비활성화 메시지를 } } } // ═══════════════════════════════════════════ // 습관 변경 보상 // ═══════════════════════════════════════════ /// /// 이전 런에서 카운터 당한 습관을 이번 런에서 보이지 않으면 보상. /// 전투 중반(30초 이후) 한 번 체크. /// private float battleTimer = 0f; // 변수를 초기화할거에요 -> 전투 진행 시간을 0으로 private const float HABIT_CHECK_TIME = 30f; // 상수를 정의할거에요 -> 습관 체크 시점을 30초로 private void CheckHabitChangeReward() // 함수를 선언할거에요 -> 습관 변경 보상을 확인하는 CheckHabitChangeReward를 { if (habitChangeRewardGranted || previousRunCounters.Count == 0) return; // 조건이 맞으면 중단할거에요 -> 이미 보상을 받았거나 이전 런 기록이 없다면 battleTimer += Time.deltaTime; // 값을 더할거에요 -> 전투 진행 시간에 프레임 시간을 if (battleTimer < HABIT_CHECK_TIME) return; // 조건이 맞으면 중단할거에요 -> 아직 30초가 안 지났다면 // 이전 런에서 발동된 카운터 중, 이번 런에서 아직 발동 안 된 것이 있으면 보상 foreach (var prevCounter in previousRunCounters) // 반복할거에요 -> 이전 런 카운터 목록에 대해 { if (!currentRunActivatedCounters.Contains(prevCounter)) // 조건이 맞으면 실행할거에요 -> 이번 런 목록에 해당 카운터가 없다면 (습관 고침) { habitChangeRewardGranted = true; // 상태를 바꿀거에요 -> 보상 지급 여부를 참(true)으로 OnHabitChangeRewarded?.Invoke(); // 이벤트를 실행할거에요 -> 보상 지급 알림 이벤트를 Debug.Log($"[BossCounter] 플레이어가 {prevCounter} 습관을 바꿈! 보상 부여"); // 로그를 출력할거에요 -> 보상 부여 메시지를 break; // 중단할거에요 -> 보상은 한 번만 주므로 반복문을 } } } // ═══════════════════════════════════════════ // 패턴 선택 (보스 AI에서 호출) // ═══════════════════════════════════════════ /// /// 현재 카운터 상태를 반영하여 보스 패턴을 가중치 랜덤으로 선택합니다. /// 보스 AI의 패턴 선택 시점에 호출하세요. /// /// 선택된 패턴 이름 public string SelectBossPattern() // 함수를 선언할거에요 -> 보스 패턴을 선택해서 반환하는 SelectBossPattern을 { if (bossPatterns.Count == 0) // 조건이 맞으면 실행할거에요 -> 등록된 패턴이 하나도 없다면 { Debug.LogWarning("[BossCounter] 보스 패턴이 등록되지 않았습니다!"); // 경고 로그를 출력할거에요 -> 패턴 없음 경고를 return "Normal"; // 값을 반환할거에요 -> 기본값 "Normal"을 } // 🚨 [안전장치] Config가 없으면 그냥 랜덤 패턴 반환 (멈춤 방지) if (config == null) // 조건이 맞으면 실행할거에요 -> 설정 파일이 없다면 { int randomIndex = Random.Range(0, bossPatterns.Count); // 값을 랜덤으로 뽑을거에요 -> 0부터 패턴 개수 사이의 인덱스를 return bossPatterns[randomIndex].patternName; // 값을 반환할거에요 -> 랜덤하게 뽑힌 패턴 이름을 } // 가중치 계산 float[] weights = new float[bossPatterns.Count]; // 배열을 만들거에요 -> 각 패턴의 가중치를 담을 weights 배열을 float totalWeight = 0f; // 변수를 초기화할거에요 -> 전체 가중치 합계를 0으로 float counterWeight = 0f; // 변수를 초기화할거에요 -> 카운터 패턴들의 가중치 합계를 0으로 for (int i = 0; i < bossPatterns.Count; i++) // 반복할거에요 -> 모든 패턴에 대해 { weights[i] = bossPatterns[i].baseWeight; // 값을 넣을거에요 -> 기본 가중치를 배열에 // 카운터 패턴 가중치 증가 if (bossPatterns[i].counterType != CounterType.None // 조건이 맞으면 실행할거에요 -> 카운터 타입이 설정되어 있고 && activeCounters.ContainsKey(bossPatterns[i].counterType) // 조건이 맞으면 실행할거에요 -> 딕셔너리에 키가 존재하며 && activeCounters[bossPatterns[i].counterType]) // 조건이 맞으면 실행할거에요 -> 해당 카운터가 활성 상태라면 { // 쿨타임 체크 if (Time.time - lastCounterPatternTime >= config.counterCooldown) // 조건이 맞으면 실행할거에요 -> 마지막 사용 후 쿨타임이 지났다면 { weights[i] += config.counterWeightBonus; // 값을 더할거에요 -> 카운터 보너스 가중치를 } } // 보조 카운터 가중치 if (bossPatterns[i].subCounterType != CounterType.None // 조건이 맞으면 실행할거에요 -> 보조 카운터 타입이 설정되어 있고 && activeCounters.ContainsKey(bossPatterns[i].subCounterType) // 조건이 맞으면 실행할거에요 -> 딕셔너리에 키가 존재하며 && activeCounters[bossPatterns[i].subCounterType]) // 조건이 맞으면 실행할거에요 -> 해당 보조 카운터가 활성 상태라면 { weights[i] += config.counterSubWeightBonus; // 값을 더할거에요 -> 보조 카운터 보너스 가중치를 } // 습관 변경 보상: 일반 패턴 가중치 감소 (= 난이도 완화) if (habitChangeRewardGranted && bossPatterns[i].counterType == CounterType.None) // 조건이 맞으면 실행할거에요 -> 보상을 받았고 일반 패턴이라면 { weights[i] *= (1f - config.habitChangeRewardRatio); // 값을 곱할거에요 -> 가중치를 감소 비율만큼 줄여서 } totalWeight += weights[i]; // 값을 더할거에요 -> 전체 가중치 합계에 현재 가중치를 if (bossPatterns[i].counterType != CounterType.None) // 조건이 맞으면 실행할거에요 -> 카운터 패턴이라면 counterWeight += weights[i]; // 값을 더할거에요 -> 카운터 가중치 합계에 } // 빈도 상한 체크: 카운터 패턴 총 비율이 maxCounterFrequency를 넘지 않도록 if (totalWeight > 0f && counterWeight / totalWeight > config.maxCounterFrequency) // 조건이 맞으면 실행할거에요 -> 카운터 패턴 비율이 최대 허용치를 초과한다면 { float allowedCounterWeight = (totalWeight - counterWeight) * config.maxCounterFrequency / (1f - config.maxCounterFrequency); // 값을 계산할거에요 -> 허용 가능한 카운터 총 가중치를 float scale = allowedCounterWeight / counterWeight; // 값을 계산할거에요 -> 가중치를 줄일 비율(scale)을 for (int i = 0; i < bossPatterns.Count; i++) // 반복할거에요 -> 모든 패턴에 대해 { if (bossPatterns[i].counterType != CounterType.None) // 조건이 맞으면 실행할거에요 -> 카운터 패턴이라면 { float bonus = weights[i] - bossPatterns[i].baseWeight; // 값을 계산할거에요 -> 추가된 보너스 가중치를 weights[i] = bossPatterns[i].baseWeight + bonus * scale; // 값을 수정할거에요 -> 보너스를 비율대로 줄여서 다시 적용 } } // 총 가중치 재계산 totalWeight = 0f; // 값을 초기화할거에요 -> 전체 합계를 0으로 for (int i = 0; i < weights.Length; i++) // 반복할거에요 -> 모든 가중치에 대해 totalWeight += weights[i]; // 값을 더할거에요 -> 전체 합계에 } // 가중치 랜덤 선택 float roll = Random.Range(0f, totalWeight); // 값을 랜덤으로 뽑을거에요 -> 0부터 전체 가중치 사이의 값을 roll에 float cumulative = 0f; // 변수를 초기화할거에요 -> 누적 가중치를 0으로 for (int i = 0; i < bossPatterns.Count; i++) // 반복할거에요 -> 모든 패턴에 대해 { cumulative += weights[i]; // 값을 더할거에요 -> 누적 가중치에 현재 가중치를 if (roll <= cumulative) // 조건이 맞으면 실행할거에요 -> 랜덤값이 누적치보다 작거나 같다면 (당첨) { string selected = bossPatterns[i].patternName; // 값을 저장할거에요 -> 선택된 패턴 이름을 selected에 // 카운터 패턴이면 쿨타임 기록 if (bossPatterns[i].counterType != CounterType.None) // 조건이 맞으면 실행할거에요 -> 카운터 패턴이라면 { lastCounterPatternTime = Time.time; // 값을 저장할거에요 -> 현재 시간을 마지막 사용 시간으로 } OnCounterPatternSelected?.Invoke(selected); // 이벤트를 실행할거에요 -> 패턴 선택 알림 이벤트를 return selected; // 값을 반환할거에요 -> 선택된 패턴 이름을 } } return bossPatterns[bossPatterns.Count - 1].patternName; // 값을 반환할거에요 -> 만약 선택되지 않았다면 마지막 패턴을 (안전장치) } // ═══════════════════════════════════════════ // 외부 조회 (여기가 아까 빠졌던 부분입니다!) // ═══════════════════════════════════════════ /// 특정 카운터 모드가 현재 활성 상태인지 확인 public bool IsCounterActive(CounterType type) // 함수를 선언할거에요 -> 특정 카운터 활성 여부를 반환하는 IsCounterActive를 { return activeCounters.ContainsKey(type) && activeCounters[type]; // 값을 반환할거에요 -> 딕셔너리에 키가 있고 값이 참(true)인지를 } /// 현재 활성화된 모든 카운터 타입 반환 public List GetActiveCounters() // 함수를 선언할거에요 -> 활성화된 모든 카운터 목록을 반환하는 GetActiveCounters를 { var result = new List(); // 리스트를 만들거에요 -> 결과를 담을 result 리스트를 foreach (var kvp in activeCounters) // 반복할거에요 -> 모든 카운터 상태에 대해 { if (kvp.Value) result.Add(kvp.Key); // 조건이 맞으면 실행할거에요 -> 활성화(true) 상태라면 리스트에 추가하기를 } return result; // 값을 반환할거에요 -> 완성된 리스트를 } /// 습관 변경 보상이 활성화되었는지 확인 public bool IsHabitChangeRewarded => habitChangeRewardGranted; // 프로퍼티를 선언할거에요 -> 보상 지급 여부를 외부에서 읽을 수 있는 IsHabitChangeRewarded를 }