| 컴포넌트 | 책임 |
|---|---|
| DropTable | 아이템 프로토타입 저장 + 이름 기반 clone |
| RewardRepository | 몬스터별 드랍 규칙 저장 & 확률 계산 |
| RewardManager | 전투 보상 오케스트레이션 |
| ItemBootstrap | dat 파일 로딩 및 의존성 연결 |
| SimpleDataLoader | ITEM / EFFECT / ASCII / REWARD 파싱 |
ITEM|작은 포션|0
EFFECT|HealFlat|Health|30|0
ITEM|강화된 철 검(+15)|0
EFFECT|AddStatFlat|Attack|15|0
REWARD|VariableSlime
PICK|작은 포션|22
PICK|초보자의 검(+5)|5
PICK|초보자의 갑옷(+10)|3
ENDREWARD
추가 아이템은 30% 확률로 1개
PICK은 가중치 랜덤 선택
GUARANTEE는 특수 아이템만 허용
NONE 같은 예외 아이템 제거
std::vector<std::string>
RewardRepository::rollNames(const std::string& monsterName) const {
std::vector<std::string> result;
const Rule* rule = find(monsterName);
if (!rule) return result;
// 1) GUARANTEE
for (const auto& g : rule->guarantees)
result.push_back(g);
// 2) 30% 확률 체크
if (randInt(1, 100) > 30)
return result;
// 3) 가중치 PICK 1개
// ...
return result;
}
아이템 생성은 하지 않음
이름 목록만 반환
dat 규칙과 코드가 정확히 대응
monster_drops.dat에 VariableSlime REWARD 존재
게임에서는 “룰을 찾을 수 없음” 로그 출력
ItemBootstrap::build()
ITEM / EFFECT / ASCII만 로딩
REWARD 규칙을 RewardRepository에 주입하지 않음
SimpleDataLoader::loadRewards()
같은 dat 파일을 두 번 읽음
1회: DropTable (아이템 프로토타입)
1회: RewardRepository (드랍 규칙)
ItemBootstrap::build(ctx, ...);
// 아이템 프로토타입
loadToVector("monster_drops.dat"); // DropTable
// 드랍 규칙
loadRewards("monster_drops.dat"); // RewardRepository
구조는 단순
책임은 명확
SOLID 준수
Item* cloneByName(const std::string& name) const;
아이템 프로토타입 소유
아이템 생성 책임을 DropTable로 고정
std::vector<std::string>
rollNames(const std::string& monsterName);
몬스터별 드랍 아이템 이름 목록 반환
확률 계산만 담당
기존 구조 유지
generateRewards()
applyRewards()
displayRewards()
void bind(DropTable&, RewardRepository&, Inventory&);
RewardRepository → 드랍 이름 목록
DropTable.cloneByName() → 실제 아이템 생성
Inventory에 추가
std::vector<std::string> droppedItemNames;
Item* 대신 이름만 저장
소유권 / 메모리 문제 방지
UI / 지급 단계 분리
컴포넌트 책임
UIGrid 출력 / 입력 유틸
UIManager 화면 전환 / 메뉴 흐름
GameManager / BattleService 실제 게임 로직
╔════════════════════════════════════════════════════════════╗
║ 🎒 인 벤 토 리 🎒 ║
╚════════════════════════════════════════════════════════════╝
┌────────────────────────────────────────────────────────┐
│ 1. 💊 아이템 사용 │
│ 2. ⚔️ 장비 장착 │
│ 3. 🛡️ 장비 해제 │
│ 0. 🚪 나가기 │
└────────────────────────────────────────────────────────┘
╔════════════════════════════════════════════════════════════╗
║ 🛡️ 장 비 해 제 🛡️ ║
╚════════════════════════════════════════════════════════════╝
┌────────────────────────────────────────────────────────┐
│ 1. ⚔️ 무기 해제 │
│ 2. 🛡️ 방어구 해제 │
│ 0. ↩️ 취소 │
└────────────────────────────────────────────────────────┘
std::string::size() 기준 폭 계산
한글(2칸) + 영문(1칸) 혼합 시 Grid 붕괴
utf8NextCodepoint(...)
displayWidthUTF8(...)
fitWidthUTF8(...)
autoCellWidthUTF8(...)
UTF-8 코드포인트 기반 실제 표시 폭 계산
한글 / 영문 / 기호 혼합 환경에서도 Grid 안정화
if (isEquippedPtr(it)) {
std::cout <<; "장착 중인 아이템은 사용할 수 없습니다.\n";
return;
}
if (it->isEquipItem()) {
std::cout << "장비 아이템은 '사용'이 아니라 '장착'하세요.\n";
return;
}
UI가 아니라 Inventory 계층에서 방어