오늘은 콘솔 RPG 프로젝트에서 아이템 시스템 전반을 구조적으로 정리하는 데 집중했다.
실제 UI와 상점 기능을 바로 구현하기보다는,
데이터 기반 아이템 관리 + 안전한 Shop–Inventory 연결 구조를 먼저 설계하는 것이 목표였다.
1️⃣ 오늘 작업의 핵심 요약
아이템 정보와 ASCII 아트를 .dat 파일로 분리
SimpleDataLoader를 통해 .dat를 파싱
Item 생성은 ItemFactory로 집중
아이템 효과는 Effect 중심 구조로 통합
Shop / Inventory UI는 아직 아이디어·스케치 단계
Shop 연동 방식으로 “clone 기반 구매” 구조를 설계
👉 현재 상태는 적용 완료가 아니라 설계 + 기반 구축 단계이며,
이 구조를 바탕으로 다음 단계에서 실제 기능을 붙일 예정이다.
2️⃣ Item 설계 방향 – Effect 중심 구조
회복 아이템, 버프 아이템, 장비 아이템을
각각 다른 로직으로 처리하지 않고 Effect 하나로 통합했다.
enum class EffectType {
HealFlat,
AddStatFlat,
InvincibleHits
};
enum class StatType {
Attack,
MaxHealth
};
struct Effect {
EffectType type;
StatType stat; // AddStatFlat일 때만 의미
int value; // 회복량 / 증감량 / 무적 횟수
int duration; // 지속 턴 (0이면 즉시)
};
Item은 Effect 데이터를 제공만
실제 스탯 변경은 EffectManager에서 처리
Inventory는 “언제 적용/원복할지”만 판단
3️⃣ .dat 파일 기반 아이템 + ASCII 아트 관리
아이템과 아스키 아트를 코드에 하드코딩하지 않고
텍스트 기반 .dat 파일에서 관리하도록 설계했다.
📄 items.dat 예시
ITEM|Small Potion|50
EFFECT|HealFlat|Health|50|0
ITEM|One Hand Sword|300
EFFECT|AddStatFlat|Attack|10|0
ASCII|Small Potion
__
| |
|__|
END
ASCII|One Hand Sword
/\
||====>
\/
END
ITEM|이름|가격
EFFECT|타입|스탯|값|duration
ASCII|이름 ~ END 사이가 아스키 아트
4️⃣ SimpleDataLoader – 현재 구현된 핵심
SimpleDataLoader는 .dat 파일을 읽어 다음을 수행한다.
ITEM 블록 파싱
EFFECT 누적
아이템 flush 시점에 ItemFactory로 Item 생성
ASCII 블록은 MapAsciiArtRepository에 저장
아이템 생성 분기(요약)
const Effect& e = currentEffects.front();
Item* item = nullptr;
if (e.type == EffectType::HealFlat)
item = ItemFactory::createHeal(currentItemName, currentPrice, e.value);
else if (e.duration > 0)
item = ItemFactory::createBuff(currentItemName, currentPrice, currentEffects);
else if (e.stat == StatType::Attack)
item = ItemFactory::createWeapon(currentItemName, currentPrice, e.value);
else if (e.stat == StatType::MaxHealth)
item = ItemFactory::createArmor(currentItemName, currentPrice, e.value);
if (item) inv.add(item);
⚠️ 현재는 Inventory에 바로 add하는 구조
→ 다음 단계에서 Shop 전용 로딩으로 확장 예정
5️⃣ Shop / Inventory UI – 현재는 “스케치 단계”
아직 실제 적용은 하지 않았고,
콘솔 UI 흐름만 아이디어로 정리한 상태다.
Shop UI 스케치
=== Shop ===
0) Small Potion - 50 G
1) One Hand Sword - 300 G
Gold: 420
[1] Buy [2] Sell [0] Exit
Inventory UI 스케치
=== Inventory ===
0) Small Potion
1) One Hand Sword
[1] Use [2] Equip [3] Unequip [0] Back
👉 실제 구매/판매/장착/사용 로직은
아래의 “clone 기반 구매 구조”를 확정한 뒤 적용할 계획이다.
6️⃣ Shop 연동 아이디어 – clone 기반 구매 (핵심)
Shop과 Inventory가 Item*를 공유하면
댕글링 포인터 / 중복 delete 문제가 발생하기 쉽다.
해결 전략
Shop은 진열용 프로토타입만 보관
구매 시에는 항상 clone()으로 새 Item 생성
Inventory가 소유권을 가짐(delete 책임)
7️⃣ Item에 clone() 추가 (최소 수정)
class Item {
public:
virtual ~Item() = default;
virtual std::string getName() = 0;
virtual Item* clone() const = 0;
};
Item* HealItem::clone() const {
return new HealItem(*this);
}
new Derived(*this) 방식으로 간단하게 복제
현재 구조에서는 값 타입 위주라 안전
8️⃣ Shop 최소 수정 – buyItem에서 clone 반환
Item* Shop::buyItem(int index) {
if (index < 0 || index >= (int)Items.size()) return nullptr;
return Items[index]->clone(); // 핵심
}
구매 흐름:
Item* bought = shop->buyItem(idx);
if (bought) inventory.add(bought);
Shop: 프로토타입 유지
Inventory: 실제 아이템 소유
9️⃣ 다음 적용 예정 방향
SimpleDataLoader::loadShop(path, shop, artRepo) 형태로 확장
Shop은 .dat 기반으로 진열품 구성
Inventory에는 구매 시 clone으로만 유입
이후 UI 입력/출력과 실제 구매·판매 로직 적용