최종 갱신 (KST)

← 일감 목록
T-075

캐릭터·장비 판매 — 판매가 확정(기획) · 개체 축 판매 패킷(서버) · 인벤토리 배선(클라)

태웅 Closed 우선순위 보통 목표 261010 · 보관됨

배경

지금 팔 수 있는 것은 자원뿐이다. 인벤토리 캐릭터·장비 탭에서 우클릭하면 "판매는 자원 탭만 지원합니다"로 끝난다(StorageGridPresenter·SellCartPresenter).

막고 있는 것은 클라가 아니라 판매 패킷의 축이다.

// 지금 — 아이템 TID 축. 종류 + 개수만 나른다
C_ItemSellRequest { List<ItemInfo> Items }      // ItemInfo = { ItemTID, Count }

캐릭터·장비는 개체다(CharacterId · EquipId — long PK). 같은 TID의 개체가 여러 개이고 레벨·경험치·장착 상태가 개체마다 다르므로 "TID를 N개 판다"로는 어느 개체인지 지정할 수 없다.

가격 쪽 상태도 갈린다.

축 가격 컬럼 상태
아이템 ItemTable.BasePrice ✅ 162행 전부 채워져 있다
장비 EquipTable.BasePrice ✅ 69행 전부 채워져 있다(70~360 등) — 컬럼은 이미 있다
캐릭터 CharacterTable.BasePrice ✅ 2026-09-30 추가 — 등급별 고정값 Common 50 · Uncommon 100 · Rare 250 · Epic 600 · Legendary 1500 · Mythic 4000 (뽑기 1회 1000 G의 기대 판매가 약 216 G)

즉시 판매가 = BasePrice의 100%는 거래 3.1에서 확정됐다. 장비는 이 규칙을 그대로 쓰면 되고, 캐릭터만 새로 정해야 한다.

할 일

기획

  • [x] 캐릭터를 팔 수 있게 할지 정한다. → 판다(2026-10-01 · #47). 경매장에도 올린다 캐릭터는 character 1.1에서 "중복은 개체를 늘린다 · 조각·강화로 바꾸지 않는다"로 확정돼 중복 개체의 출구가 하나도 없다. 판매를 열거나, 다른 출구(T-076)를 열거나 둘 중 하나는 필요하다
  • [x] 캐릭터 판매가 — 등급별 고정값으로 CharacterTable.BasePrice에 넣었다(2026-09-30 태웅). 레벨 반영은 하지 않는다(필요하면 다시 연다)
  • [x] 레벨이 오른 캐릭터·장착 중인 장비를 팔 수 있는지 → 레벨은 막지 않는다. 착용 중인 장비 · 배치됐거나 장비를 낀 캐릭터 · 마지막 남은 캐릭터는 막는다 (2026-10-01)
  • [x] 장비 판매가는 EquipTable.BasePrice × SellRatePermille(100%) — 아이템과 같은 비율

서버

  • [x] 개체 축 판매 패킷을 판다. (2026-10-01) 요청 이슈: #47 ③ 기존 C_ItemSellRequest를 고치지 않고 따로 둔다 — 아이템은 TID·수량 축이 맞고, 개체는 PK 목록 축이 맞다

    ```csharp
    C_EntitySellRequest { List<long> CharacterIds, List<long> EquipIds }
    S_EntitySellResponse { EResultCode Result, long GainedGold }
    ```
    
  • [x] 거절 코드 — InvalidSellRequest(빈 요청·중복) · EquipNotOwned·CharacterNotOwned · SellEquipWorn(304) · SellCharacterBusy(305) · SellLastCharacter(306)

  • [x] 전부 성공하거나 전부 실패한다 — S_ItemSellResponse와 같은 원칙

  • [x] 판매 후 S_CurrencyResponse로 잔액을 내린다. 개체 삭제는 Ok면 클라가 요청한 개체를 지운다(삭제용 싱크 패킷이 따로 없다)

  • [x] 테스트(UserEntitySellTest · SellEntitiesRepositoryTest) · 프로토콜 미러

클라

  • [x] 담기를 캐릭터·장비 탭으로 넓혔다 — 칸 우클릭으로 하나씩 (2026-10-03). 일괄 담기는 자원만
  • [x] 요청이 두 패킷으로 나뉜다 — 섞이면 둘 다 보내고 응답이 다 와야 성공·실패를 말한다(SellCartPresenter)
  • [x] 장착 중·배치 중·장비 낀 캐릭터·마지막 캐릭터는 담기 단계에서 막고 사유를 알린다(EntityBlockText) · 담아 둔 뒤 그렇게 되면 카트가 뺀다
  • [x] ResultMessages에 새 거절 코드 문구 · Ok면 판 개체를 지운다(PlayerDataModel)
  • [x] 실측 — 장비·캐릭터를 담아 즉시 판매 → 골드 증가 · 인벤토리에서 사라짐 · 막힌 개체는 담기지 않음 (2026-10-03 사용자 실측)

완료 조건

인벤토리 장비 탭에서 장비를 우클릭해 담고 판매하면 EquipTable.BasePrice만큼 골드가 들어오고 인벤토리에서 사라진다. 장착 중인 장비는 담기지 않는다. 캐릭터도 기획이 판매를 열기로 했다면 같게 동작한다.

막고 있는 것 / 선행 일감

  • 기획·서버 완료(2026-10-01) · 클라 배선 완료(2026-10-03) — 남은 것은 실측이다 (담당을 클라로 넘김)
  • T-076와 같은 결정을 공유한다 — 중복 개체의 출구를 판매로 둘지 재료로 둘지
  • 인벤토리 장비 탭 T-043 · 장착 UI T-074

관련 일감

  • T-086 — 배치·장착 개체는 인벤토리 목록에 없다 2026-09-25 철회. 배치·장착 중인 개체도 인벤토리에 그대로 보인다(딤 + '배' 마크). 그래서 아래 "담기 단계에서 막는다"는 여전히 필요하다 — 눌리는 칸이 화면에 있다. StorageSlotSource.IsAway가 판정을 갖고 있으니 담기 쪽에서도 그것을 쓴다. ⚠️ 그래도 서버 거절 코드는 그대로 둔다 — 클라를 못 믿는 자리다.

관련 커밋

  • 33f5a5a 캐릭터 경매 · 캐릭터·장비 즉시 판매 (2026-10-03)
  • 이 커밋 — 실측 완료 · 보관

참고