diff --git a/lib/src/core/engine/arena_service.dart b/lib/src/core/engine/arena_service.dart index ee99e22..edb5515 100644 --- a/lib/src/core/engine/arena_service.dart +++ b/lib/src/core/engine/arena_service.dart @@ -1,5 +1,6 @@ import 'package:asciineverdie/data/skill_data.dart'; import 'package:asciineverdie/src/core/engine/combat_calculator.dart'; +import 'package:asciineverdie/src/core/engine/item_service.dart'; import 'package:asciineverdie/src/core/engine/skill_service.dart'; import 'package:asciineverdie/src/core/model/arena_match.dart'; import 'package:asciineverdie/src/core/model/equipment_item.dart'; @@ -668,18 +669,58 @@ class ArenaService { } } // ============================================================================ - // 장비 교환 + // AI 베팅 슬롯 선택 // ============================================================================ - /// 장비 교환 (같은 슬롯끼리) + /// AI가 도전자에게서 약탈할 슬롯 자동 선택 /// - /// 승자가 선택한 슬롯의 장비를 서로 교환 + /// 도전자의 가장 좋은 장비 슬롯 선택 (무기 제외) + EquipmentSlot selectOpponentBettingSlot(HallOfFameEntry challenger) { + final equipment = challenger.finalEquipment ?? []; + if (equipment.isEmpty) { + // 장비가 없으면 기본 슬롯 (투구) + return EquipmentSlot.helm; + } + + // 무기를 제외한 장비 중 가장 높은 점수의 슬롯 선택 + EquipmentSlot? bestSlot; + int bestScore = -1; + + for (final item in equipment) { + // 무기는 약탈 불가 + if (item.slot == EquipmentSlot.weapon) continue; + if (item.isEmpty) continue; + + final score = ItemService.calculateEquipmentScore(item); + if (score > bestScore) { + bestScore = score; + bestSlot = item.slot; + } + } + + // 유효한 슬롯이 없으면 투구 선택 + return bestSlot ?? EquipmentSlot.helm; + } + + /// 베팅 가능한 슬롯 목록 반환 (무기 제외) + List getBettableSlots() { + return EquipmentSlot.values + .where((slot) => slot != EquipmentSlot.weapon) + .toList(); + } + + // ============================================================================ + // 장비 약탈 + // ============================================================================ + + /// 장비 약탈 (승자가 패자의 베팅 슬롯 장비 획득) + /// + /// - 승자: 자신이 선택한 슬롯의 패자 장비 획득 + /// - 패자: 해당 슬롯 장비 손실 → 기본 장비로 대체 (HallOfFameEntry, HallOfFameEntry) _exchangeEquipment({ required ArenaMatch match, required bool isVictory, }) { - final slot = match.bettingSlot; - // 도전자 장비 목록 복사 final challengerEquipment = List.from(match.challenger.finalEquipment ?? []); @@ -688,13 +729,29 @@ class ArenaService { final opponentEquipment = List.from(match.opponent.finalEquipment ?? []); - // 해당 슬롯의 장비 찾기 - final challengerItem = _findItemBySlot(challengerEquipment, slot); - final opponentItem = _findItemBySlot(opponentEquipment, slot); + if (isVictory) { + // 도전자 승리: 도전자가 선택한 슬롯의 상대 장비 획득 + final winnerSlot = match.challengerBettingSlot; + final lootedItem = _findItemBySlot(opponentEquipment, winnerSlot); - // 장비 교환 - _replaceItemInList(challengerEquipment, slot, opponentItem); - _replaceItemInList(opponentEquipment, slot, challengerItem); + // 도전자: 약탈한 장비로 교체 + _replaceItemInList(challengerEquipment, winnerSlot, lootedItem); + + // 상대: 해당 슬롯 기본 장비로 대체 + final defaultItem = _createDefaultEquipment(winnerSlot); + _replaceItemInList(opponentEquipment, winnerSlot, defaultItem); + } else { + // 상대 승리: 상대가 선택한 슬롯의 도전자 장비 획득 + final winnerSlot = match.opponentBettingSlot; + final lootedItem = _findItemBySlot(challengerEquipment, winnerSlot); + + // 상대: 약탈한 장비로 교체 + _replaceItemInList(opponentEquipment, winnerSlot, lootedItem); + + // 도전자: 해당 슬롯 기본 장비로 대체 + final defaultItem = _createDefaultEquipment(winnerSlot); + _replaceItemInList(challengerEquipment, winnerSlot, defaultItem); + } // 업데이트된 엔트리 생성 final updatedChallenger = match.challenger.copyWith( @@ -731,4 +788,11 @@ class ArenaService { // 슬롯이 없으면 추가 equipment.add(newItem); } + + /// 기본 장비 생성 (Common 등급) + /// + /// 패자가 장비를 잃었을 때 빈 슬롯 방지용 + EquipmentItem _createDefaultEquipment(EquipmentSlot slot) { + return ItemService.createDefaultEquipmentForSlot(slot); + } } diff --git a/lib/src/core/engine/item_service.dart b/lib/src/core/engine/item_service.dart index bc40d4c..d18a7b5 100644 --- a/lib/src/core/engine/item_service.dart +++ b/lib/src/core/engine/item_service.dart @@ -279,6 +279,51 @@ class ItemService { return score; } + // ============================================================================ + // 기본 장비 생성 + // ============================================================================ + + /// 슬롯별 기본 장비 생성 (Common 등급, 레벨 1) + /// + /// 아레나에서 패배하여 장비를 잃었을 때 빈 슬롯 방지용 + static EquipmentItem createDefaultEquipmentForSlot(EquipmentSlot slot) { + final name = _getDefaultItemName(slot); + const rarity = ItemRarity.common; + + // 기본 스탯 (레벨 1 기준) + final stats = switch (slot) { + EquipmentSlot.weapon => const ItemStats(atk: 2, attackSpeed: 1000), + EquipmentSlot.shield => const ItemStats(def: 1, blockRate: 0.05), + _ => const ItemStats(def: 1), + }; + + return EquipmentItem( + name: name, + slot: slot, + level: 1, + weight: 1, + stats: stats, + rarity: rarity, + ); + } + + /// 슬롯별 기본 장비 이름 + static String _getDefaultItemName(EquipmentSlot slot) { + return switch (slot) { + EquipmentSlot.weapon => 'Wooden Stick', + EquipmentSlot.shield => 'Wooden Shield', + EquipmentSlot.helm => 'Cloth Cap', + EquipmentSlot.hauberk => 'Torn Shirt', + EquipmentSlot.brassairts => 'Cloth Wraps', + EquipmentSlot.vambraces => 'Worn Bracers', + EquipmentSlot.gauntlets => 'Tattered Gloves', + EquipmentSlot.gambeson => 'Ragged Tunic', + EquipmentSlot.cuisses => 'Worn Pants', + EquipmentSlot.greaves => 'Cloth Leggings', + EquipmentSlot.sollerets => 'Worn Sandals', + }; + } + // ============================================================================ // 자동 장착 // ============================================================================ diff --git a/lib/src/core/model/arena_match.dart b/lib/src/core/model/arena_match.dart index d8e5c0b..d65afea 100644 --- a/lib/src/core/model/arena_match.dart +++ b/lib/src/core/model/arena_match.dart @@ -3,12 +3,13 @@ import 'package:asciineverdie/src/core/model/hall_of_fame.dart'; /// 아레나 대전 정보 /// -/// 도전자와 상대의 정보, 베팅 슬롯을 포함 +/// 도전자와 상대의 정보, 양방향 베팅 슬롯을 포함 class ArenaMatch { const ArenaMatch({ required this.challenger, required this.opponent, - required this.bettingSlot, + required this.challengerBettingSlot, + required this.opponentBettingSlot, }); /// 도전자 (내 캐릭터) @@ -17,14 +18,21 @@ class ArenaMatch { /// 상대 캐릭터 final HallOfFameEntry opponent; - /// 베팅 슬롯 (같은 슬롯 교환) - final EquipmentSlot bettingSlot; + /// 도전자 베팅 슬롯 (승리 시 상대에게서 빼앗을 슬롯) + final EquipmentSlot challengerBettingSlot; + + /// 상대 베팅 슬롯 (상대 승리 시 도전자에게서 빼앗을 슬롯) + final EquipmentSlot opponentBettingSlot; /// 도전자 순위 int get challengerRank => 0; // ArenaService에서 계산 /// 상대 순위 int get opponentRank => 0; // ArenaService에서 계산 + + /// 기존 bettingSlot 호환용 (deprecated) + @Deprecated('Use challengerBettingSlot instead') + EquipmentSlot get bettingSlot => challengerBettingSlot; } /// 아레나 대전 결과