저번 회차에서는 Character 부모클래스제작과, 그 안에 플레이어 캐릭터와 몬스터 캐릭터를 각각 상속받아서 간단히 턴 주고받고 데미지를 주고받게 하였다.
이번에는 뭘 할 것이냐.. 바로 인벤토리를 추가 할 것이다.
바로 가봅시다잉.
우선 인벤토리를 채우려면 아이템 이 필요하니까 그 아이템의 정보를 저장할 클래스를 만들어주자.
package com.practice;
public class Item {
private String name;
private int count;
public Item(String name, int count) {
this.name = name;
this.count = count;
}
public String getName() {
return name;
}
public int getCount() {
return count;
}
public void setName(String name) {
this.name = name;
}
public void setCount(int count) {
this.count = count;
}
}
이정도면 될 것 같다. 참고로 인벤토리에서 아이템을 Map으로 저장할 예정이다.
key = item.getName(), value = item.getCount() 가 되겠지.
이렇게 짜는 이유는
인벤토리는 “중복아이템이 없을 시” 10칸까지 보관이 가능하게 할 것이다.
중복되면 아이템 개수, item.setCount 를 통해 올리겠지.
package com.practice;
public class InventoryFullException extends RuntimeException {
public InventoryFullException(String message) {
super(message);
}
}
물론 커스텀으로 exception해야 작업이 편할태니 만들어줬다.
package com.practice;
import java.util.HashMap;
import java.util.Map;
public class Inventory {
private Map<String, Integer> items = new HashMap<>();
int inventorySize = 10;
void addItem(Item item) {
// 새 아이템인데, size가 10이 넘는다? -> exception
if (items.size() >= 10 && !items.containsKey(item.getName())) {
throw new InventoryFullException("아이템이 가득차서 더 이상 들고갈 수 없다!");
}
if (items.containsKey(item.getName())) { // items에 있는 아이템인가? -> value 증가
items.put(item.getName(), items.get(item.getName()) + item.getCount());
} else { // items에 없는 아이템인가? -> 새로 추가
items.put(item.getName(), item.getCount());
}
}
void showInfo() {
System.out.println("==========인벤토리==========");
int count = 1;
for (Map.Entry<String, Integer> entry : items.entrySet()) {
System.out.printf("%d. %s / %d개\n", count, entry.getKey(), entry.getValue());
count++;
}
System.out.println("==========================");
}
}
자 인벤토리 구현까지는 끝났다. 그러면 이제 아이템 수급처가 필요하겠지.
몬스터한태 아이템 드랍을 시킬거다….만!
지금 클래스 상속 상태가 어떻게 되어있지??
그렇다..Character 클래스에 플레이어 캐릭터, 몬스터 캐릭터 둘 다 상속되어있다.
무지성으로 Character 클래스에 DropItem 이런거 넣으면 플레이어가 죽어도 템드랍을 하는 이상현상이 생길 것이기에…
┌ Warrior
Character 클래스 |
└ Ghoul
기존이 이런 형식이였으니,
┌ PlayerCharacter - Warrior
Character 클래스 |
└ Monster - Ghoul
이렇게 바꿔주면 상속이 더 편해지겠지.
package com.practice;
public class Character {
protected String name;
protected int hp;
protected int fullHp;
protected int atk;
protected int def;
protected Weapon weapon;
void attack(Character character) {
if (weapon == null){
System.out.println(name + "의 맨손공격!");
System.out.println("enemy 에게 " + atk + "만큼의 데미지를 주었다.");
}else {
System.out.println(name + "의 공격!");
character.damaged(weapon.additionalDamage(atk));
}
}
void damaged(int damage) {
int totalDamage = damage - def;
if(hp <= totalDamage){
died();
return;
}
if (totalDamage <= 0) {
System.out.println(name + "은 공격을 받았으나, 데미지를 입지 않았다!");
} else {
hp -= damage - def;
System.out.println(name + "은(는) " + (damage-def) + "만큼의 데미지를 입었다!");
System.out.println("남은 채력 : " + hp + "/" + fullHp);
}
}
void died(){
System.out.println(name + "은(는) 쓰러졌다!");
}
}
died 메서드를 추가해, 체력이 0이 됐을 때 이벤트를 분리했다.
package com.practice;
public class PlayerCharacter extends Character {
private Inventory inventory;
public PlayerCharacter() {
inventory = new Inventory();
}
void equipWeapon(Weapon weapon) {
this.weapon = weapon;
System.out.println(name + "은 " + weapon.getWeapon() + "을 장착했습니다.");
}
void pickUpItem(Item item) {
System.out.println("전사는 " + item.getName() + " " + item.getCount() + "개를 인벤토리에 넣었다.");
inventory.addItem(item);
inventory.showInfo();
}
}
public class Warrior extends PlayerCharacter
워리어 클래스는 상속을 바꿔주었다.
package com.practice;
import java.util.Random;
public class Monster extends Character{
private Random random = new Random();
private Item droppedItem;
public Item getDroppedItem() {
return droppedItem;
}
public Item dropItem(){
return new Item("회복 포션", 2);
}
@Override
void died() {
super.died();
int randomDrop = random.nextInt(10) + 1;
if(randomDrop > 3){
System.out.println(name + "은(는) 아이템을 떨궜다.");
droppedItem = dropItem();
}
}
}
public class Ghoul extends Monster
구울도 마찬가지.
드롭률은 랜덤으로 간단하게 정했는데, 다이스 처럼 주사위 1~10중 3 초과로 뜨면 드롭된다. (약 70%)
package com.practice;
public class MainGameTest {
public static void main(String[] args) {
Warrior warrior = new Warrior("전사");
Ghoul ghoul = new Ghoul();
Weapon weapon = new Weapon("검", 5);
warrior.equipWeapon(weapon);
warrior.attack(ghoul);
warrior.attack(ghoul);
warrior.attack(ghoul);
warrior.attack(ghoul);
Item item = ghoul.getDroppedItem();
if(item != null){
warrior.pickUpItem(item);
}
}
}
이제 테스트를 해보자.
전사은 검을 장착했습니다.
전사의 공격!
구울은(는) 13만큼의 데미지를 입었다!
남은 채력 : 37/50
전사의 공격!
구울은(는) 13만큼의 데미지를 입었다!
남은 채력 : 24/50
전사의 공격!
구울은(는) 13만큼의 데미지를 입었다!
남은 채력 : 11/50
전사의 공격!
구울은(는) 쓰러졌다!
구울은(는) 아이템을 떨궜다.
전사는 회복 포션 2개를 인벤토리에 넣었다.
==========인벤토리==========
1. 회복 포션 / 2개
==========================
잘 작동 되는 듯.
예외 테스트도 해봐야겠다.
inventory의 기본 사이즈를 0으로 바꾸고 실행했다.
전사는 회복 포션 2개를 인벤토리에 넣었다.
Exception in thread "main" com.practice.InventoryFullException: 아이템이 가득차서 더 이상 들고갈 수 없다!
at com.practice.Inventory.addItem(Inventory.java:13)
at com.practice.PlayerCharacter.pickUpItem(PlayerCharacter.java:18)
at com.practice.MainGameTest.main(MainGameTest.java:18)
만족스럽다.