diff --git a/1st-spec.md b/1st-spec.md index 4811a98..ae3e5a4 100644 --- a/1st-spec.md +++ b/1st-spec.md @@ -58,7 +58,7 @@ 2. 방어(1 ~ 10) 3. 두번베기(2 ~ 20) - 2MP - 0턴 4. 3번베기(3 ~ 30) - 3MP - 0턴 - 5. 쎼게 때리기(0 ~ 5) - 5MP - 2턴 + 5. 쎼게 때리기(5 ~ 50) - 5MP - 2턴 ``` * 체력이 0이하시, 게임 종료를 출력한다. ``` diff --git a/TODO.md b/TODO.md index 2dc257d..a729d69 100644 --- a/TODO.md +++ b/TODO.md @@ -1 +1,95 @@ # 턴제 게임 + +### 객체 설계 +1. 캐릭터 (Character) +2. 행동 (Action) + +~~~mermaid +classDiagram + class Character { + -String name + -int healthPoint + -int manaPoint + -int defense + +takeDamage(int) void + +setDefense(int) void + +useMana(int) void + +isAlive() boolean + } + + class Action { + <> + +execute(Character, Character) void + } + + class AttackAction { + -int MIN_DAMAGE + -int MAX_DAMAGE + -Random random + +execute(Character, Character) void + } + + class DefenseAction { + -int MIN_DEFENSE + -int MAX_DEFENSE + -Random random + +execute(Character, Character) void + } + + class SkillAction { + <> + #int manaCost + #int coolTime + #int remainingCooltime + #Random random + +SkillAction(manaCost, coolTime) + +reduceCoolTime() void + +execute(Character, Character) void + } + + class CutTwice { + -int MIN_DAMAGE + -int MAX_DAMAGE + -int MANA_COST + -int COOL_TIME + +CutTwice() + +execute(Character, Character) void + } + + class CutThreeTimes { + -int MIN_DAMAGE + -int MAX_DAMAGE + -int MANA_COST + -int COOL_TIME + +CutThreeTimes() + +execute(Character, Character) void + } + + class PowerStrike { + -int MIN_DAMAGE + -int MAX_DAMAGE + -int MANA_COST + -int COOL_TIME + +PowerStrike() + +execute(Character, Character) void + } + + Action <|.. AttackAction : implements + Action <|.. DefenseAction : implements + Action <|.. SkillAction : implements + SkillAction <|-- CutTwice : extends + SkillAction <|-- CutThreeTimes : extends + SkillAction <|-- PowerStrike : extends +~~~ +### 기능 목록 +| 객체 | 기능 | +| ------- | ----------------------------------------------------- | +| **캐릭터** | **행동에 따른 체력, 마나, 데미지 감소를 받는다** | +| | - (상대방이) 공격 수행시, 받은 데미지 만큼 체력이 감소한다 | +| | - (현재캐릭터가) 방어 수행시, 랜덤한 방어력을 설정해주고 상대방에게 받는 데미지를 감소시킨다 | +| | - 스킬 사용시, 각 스킬에 따른 마나를 소모한다 | +| **행동** |**각 행동(공격, 방어, 스킬)을 수행한다** | +| | - 공격 : 상대방에게 랜덤한 데미지를 입힌다 | +| | - 방어 : 현재 캐릭터에게 랜덤한 방어력을 설정한다 | +| | - 스킬 : 상대방에게 각 스킬(2번 베기, 3번 베기, 세게때리기)에 따른 데미지를 입힌다 | + diff --git a/build.gradle b/build.gradle index b540f64..096a811 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,7 @@ plugins { id 'java' + id 'org.springframework.boot' version '3.4.0' + id 'io.spring.dependency-management' version '1.1.6' } group = 'com.gdsc' @@ -7,7 +9,7 @@ version = '0.0.1-SNAPSHOT' java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(17) } } @@ -16,8 +18,16 @@ repositories { } dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + runtimeOnly 'com.h2database:h2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + } tasks.named('test') { diff --git a/src/main/java/com/gdsc/game/Application.java b/src/main/java/com/gdsc/game/Application.java deleted file mode 100644 index 0eef710..0000000 --- a/src/main/java/com/gdsc/game/Application.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gdsc.game; - -public class Application { - - public static void main(String[] args) { - System.out.println("hello world"); - } - -} diff --git a/src/main/java/com/gdsc/game/CutThreeTimes.java b/src/main/java/com/gdsc/game/CutThreeTimes.java new file mode 100644 index 0000000..576b1b0 --- /dev/null +++ b/src/main/java/com/gdsc/game/CutThreeTimes.java @@ -0,0 +1,22 @@ +package com.gdsc.game; + +public class CutThreeTimes extends SkillAction{ + private static final int MIN_DAMAGE = 5; + private static final int MAX_DAMAGE = 50; + + private static final int MANA_COST = 3; + private static final int COOL_TIME = 0; + + public CutThreeTimes() { + super(MANA_COST,COOL_TIME); + } + + @Override + public void execute(Character current, Character target) { + current.useMana(manaCost); + int damage = random.nextInt(MAX_DAMAGE - MIN_DAMAGE + 1) + MIN_DAMAGE; + int actualDamage = manaCost * damage; + target.takeDamage(actualDamage); + remainingCooltime = coolTime; + } +} diff --git a/src/main/java/com/gdsc/game/CutTwice.java b/src/main/java/com/gdsc/game/CutTwice.java new file mode 100644 index 0000000..f835b0d --- /dev/null +++ b/src/main/java/com/gdsc/game/CutTwice.java @@ -0,0 +1,22 @@ +package com.gdsc.game; + +public class CutTwice extends SkillAction { + private static final int MIN_DAMAGE = 5; + private static final int MAX_DAMAGE = 50; + + private static final int MANA_COST = 2; + private static final int COOL_TIME = 0; + + public CutTwice() { + super(MANA_COST,COOL_TIME); + } + + @Override + public void execute(Character current, Character target) { + current.useMana(manaCost); + int damage = random.nextInt(MAX_DAMAGE - MIN_DAMAGE + 1) + MIN_DAMAGE; + int actualDamage = manaCost * damage; + target.takeDamage(actualDamage); + remainingCooltime = coolTime; + } +} diff --git a/src/main/java/com/gdsc/game/Game.java b/src/main/java/com/gdsc/game/Game.java new file mode 100644 index 0000000..585fc76 --- /dev/null +++ b/src/main/java/com/gdsc/game/Game.java @@ -0,0 +1,4 @@ +package com.gdsc.game; + +public class Game { +} diff --git a/src/main/java/com/gdsc/game/PowerStrike.java b/src/main/java/com/gdsc/game/PowerStrike.java new file mode 100644 index 0000000..6791217 --- /dev/null +++ b/src/main/java/com/gdsc/game/PowerStrike.java @@ -0,0 +1,22 @@ +package com.gdsc.game; + +public class PowerStrike extends SkillAction{ + private static final int MIN_DAMAGE = 5; + private static final int MAX_DAMAGE = 50; + + private static final int MANA_COST = 5; + private static final int COOL_TIME = 2; + + public PowerStrike() { + super(MANA_COST,COOL_TIME); + } + + @Override + public void execute(Character current, Character target) { + current.useMana(manaCost); + int damage = random.nextInt(MAX_DAMAGE - MIN_DAMAGE + 1) + MIN_DAMAGE; + int actualDamage = manaCost * damage; + target.takeDamage(actualDamage); + remainingCooltime = coolTime; + } +} diff --git a/src/main/java/com/gdsc/game/SkillAction.java b/src/main/java/com/gdsc/game/SkillAction.java new file mode 100644 index 0000000..3bab95c --- /dev/null +++ b/src/main/java/com/gdsc/game/SkillAction.java @@ -0,0 +1,23 @@ +package com.gdsc.game; + +import java.util.Random; + +public abstract class SkillAction implements Action { + protected final int manaCost; + protected final int coolTime; + protected int remainingCooltime; + + protected final Random random = new Random(); + + public SkillAction(int manaCost, int coolTime) { + this.manaCost = manaCost; + this.coolTime = coolTime; + remainingCooltime = 0; + } + + public void reduceCoolTime() { + if (remainingCooltime > 0) { + remainingCooltime--; + } + } +} diff --git a/src/main/java/com/gdsc/game/controller/GameController.java b/src/main/java/com/gdsc/game/controller/GameController.java new file mode 100644 index 0000000..2883cdb --- /dev/null +++ b/src/main/java/com/gdsc/game/controller/GameController.java @@ -0,0 +1,35 @@ +package com.gdsc.game.controller; + +import com.gdsc.game.dto.ActionResponse; +import com.gdsc.game.dto.CharacterStatusResponse; +import com.gdsc.game.service.GameService; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class GameController { + + private final GameService gameService; + + public GameController(GameService gameService) { + this.gameService = gameService; + } + + @GetMapping("/character/{name}") + public CharacterStatusResponse getStatus(@PathVariable String name){ + return gameService.getStatus(name); + } + + @GetMapping("/characters/{name}/actions") + public List showActions( + @PathVariable String name, @RequestParam String actionName + ) { + return gameService.showActions(name, actionName); + } +} diff --git a/src/main/java/com/gdsc/game/domain/Action.java b/src/main/java/com/gdsc/game/domain/Action.java new file mode 100644 index 0000000..40ed12b --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/Action.java @@ -0,0 +1,7 @@ +package com.gdsc.game.domain; + + +interface Action { + void execute(Character current, Character target); +} + diff --git a/src/main/java/com/gdsc/game/domain/AttackAction.java b/src/main/java/com/gdsc/game/domain/AttackAction.java new file mode 100644 index 0000000..f6c52aa --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/AttackAction.java @@ -0,0 +1,16 @@ +package com.gdsc.game.domain; + +import java.util.Random; +// 공격 +public class AttackAction implements Action { + private static final int MIN_DAMAGE = 1; + private static final int MAX_DAMAGE = 10; + private final Random random = new Random(); + + @Override + public void execute(Character current, Character target) { + int demage = random.nextInt(MAX_DAMAGE - MIN_DAMAGE + 1) + MIN_DAMAGE; + target.takeDamage(demage); + } + +} \ No newline at end of file diff --git a/src/main/java/com/gdsc/game/domain/Character.java b/src/main/java/com/gdsc/game/domain/Character.java new file mode 100644 index 0000000..ae08231 --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/Character.java @@ -0,0 +1,80 @@ +package com.gdsc.game.domain; + +import jakarta.persistence.*; + +@Entity +public class Character { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private int healthPoint; + private int manaPoint; + private int defense; + private int level; + + @Enumerated(EnumType.STRING) + private Job job; + + protected Character() {} + + public Character(String name, Job job, int level) { + this.name = name; + this.job = job; + this.level = level; + this.healthPoint = job.calculateHealth(level); + this.manaPoint = job.calculateMana(level); + } + + + public void takeDamage(int damage) { + int actualDamage = Math.max(0, damage - defense); + this.healthPoint = Math.max(0, healthPoint - damage); + this.defense = 0; + } + + public void setDefense(int defense) { + this.defense = defense; + } + + public void useMana(int amount) { + if (amount > manaPoint) { + throw new IllegalStateException("마나부족"); + } + this.manaPoint -= amount; + } + + public boolean isAlive() { + return healthPoint > 0; + } + + public Character(String name, Job job) { + this.name = name; + this.job = job; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getHealthPoint() { + return healthPoint; + } + + public int getManaPoint() { + return manaPoint; + } + + public int getDefense() { + return defense; + } + + public Job getJob() { + return job; + } +} diff --git a/src/main/java/com/gdsc/game/domain/CutThreeTimes.java b/src/main/java/com/gdsc/game/domain/CutThreeTimes.java new file mode 100644 index 0000000..614a0b2 --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/CutThreeTimes.java @@ -0,0 +1,22 @@ +package com.gdsc.game.domain; + +public class CutThreeTimes extends SkillAction{ + private static final int MIN_DAMAGE = 5; + private static final int MAX_DAMAGE = 50; + + private static final int MANA_COST = 3; + private static final int COOL_TIME = 0; + + public CutThreeTimes() { + super(MANA_COST,COOL_TIME); + } + + @Override + public void execute(Character current, Character target) { + current.useMana(manaCost); + int damage = random.nextInt(MAX_DAMAGE - MIN_DAMAGE + 1) + MIN_DAMAGE; + int actualDamage = manaCost * damage; + target.takeDamage(actualDamage); + remainingCooltime = coolTime; + } +} diff --git a/src/main/java/com/gdsc/game/domain/CutTwice.java b/src/main/java/com/gdsc/game/domain/CutTwice.java new file mode 100644 index 0000000..8e2c053 --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/CutTwice.java @@ -0,0 +1,22 @@ +package com.gdsc.game.domain; + +public class CutTwice extends SkillAction { + private static final int MIN_DAMAGE = 5; + private static final int MAX_DAMAGE = 50; + + private static final int MANA_COST = 2; + private static final int COOL_TIME = 0; + + public CutTwice() { + super(MANA_COST,COOL_TIME); + } + + @Override + public void execute(Character current, Character target) { + current.useMana(manaCost); + int damage = random.nextInt(MAX_DAMAGE - MIN_DAMAGE + 1) + MIN_DAMAGE; + int actualDamage = manaCost * damage; + target.takeDamage(actualDamage); + remainingCooltime = coolTime; + } +} diff --git a/src/main/java/com/gdsc/game/domain/DefenseAction.java b/src/main/java/com/gdsc/game/domain/DefenseAction.java new file mode 100644 index 0000000..d12bd03 --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/DefenseAction.java @@ -0,0 +1,18 @@ +package com.gdsc.game.domain; + +import java.util.Random; + +public class DefenseAction implements Action { + private static final int MIN_DEFENSE = 1; + private static final int MAX_DEFENSE = 10; + private final Random random = new Random(); + + + + @Override + public void execute(Character current, Character target) { + int defense = random.nextInt(MAX_DEFENSE - MIN_DEFENSE + 1) + MIN_DEFENSE; + current.setDefense(defense); + } + +} \ No newline at end of file diff --git a/src/main/java/com/gdsc/game/domain/Job.java b/src/main/java/com/gdsc/game/domain/Job.java new file mode 100644 index 0000000..efe93ce --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/Job.java @@ -0,0 +1,55 @@ +package com.gdsc.game.domain; + +import com.gdsc.game.exception.GameException; + +import java.util.Arrays; +import java.util.List; + +import static com.gdsc.game.exception.ErrorCode.JOB_NOT_FOUND; + +public enum Job { + KNIGHT(100, 50, Arrays.asList("CutTwice", "CutThreeTimes", "PowerStrike")), + WIZARD(90, 100, Arrays.asList("Recovery", "AttackSpeed", "Stun")), + ARCHER(80, 80, Arrays.asList("FireShot", "MultipleArrow", "SlowShot")); + + private final int health; + private final int mana; + private final List skills; + + Job(int health, int mana, List skills) { + this.health = health; + this.mana = mana; + this.skills = skills; + } + + public int calculateHealth(int level) { + return health + (level * 10); + } + + public int calculateMana(int level) { + return mana + (level * 5); + } + + + public int getHealth() { + return health; + } + + public int getMana() { + return mana; + } + + public List getSkills() { + return skills; + } + + public static Job fromString(String value) { + try { + return Job.valueOf(value.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new GameException(JOB_NOT_FOUND, "존재하지 않는 직업입니링: " + value); + } + } + + +} diff --git a/src/main/java/com/gdsc/game/domain/PowerStrike.java b/src/main/java/com/gdsc/game/domain/PowerStrike.java new file mode 100644 index 0000000..b149949 --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/PowerStrike.java @@ -0,0 +1,22 @@ +package com.gdsc.game.domain; + +public class PowerStrike extends SkillAction{ + private static final int MIN_DAMAGE = 5; + private static final int MAX_DAMAGE = 50; + + private static final int MANA_COST = 5; + private static final int COOL_TIME = 2; + + public PowerStrike() { + super(MANA_COST,COOL_TIME); + } + + @Override + public void execute(Character current, Character target) { + current.useMana(manaCost); + int damage = random.nextInt(MAX_DAMAGE - MIN_DAMAGE + 1) + MIN_DAMAGE; + int actualDamage = manaCost * damage; + target.takeDamage(actualDamage); + remainingCooltime = coolTime; + } +} diff --git a/src/main/java/com/gdsc/game/domain/SkillAction.java b/src/main/java/com/gdsc/game/domain/SkillAction.java new file mode 100644 index 0000000..51ec77a --- /dev/null +++ b/src/main/java/com/gdsc/game/domain/SkillAction.java @@ -0,0 +1,23 @@ +package com.gdsc.game.domain; + +import java.util.Random; + +public abstract class SkillAction implements Action { + protected final int manaCost; + protected final int coolTime; + protected int remainingCooltime; + + protected final Random random = new Random(); + + public SkillAction(int manaCost, int coolTime) { + this.manaCost = manaCost; + this.coolTime = coolTime; + remainingCooltime = 0; + } + + public void reduceCoolTime() { + if (remainingCooltime > 0) { + remainingCooltime--; + } + } +} diff --git a/src/main/java/com/gdsc/game/dto/ActionResponse.java b/src/main/java/com/gdsc/game/dto/ActionResponse.java new file mode 100644 index 0000000..f033471 --- /dev/null +++ b/src/main/java/com/gdsc/game/dto/ActionResponse.java @@ -0,0 +1,38 @@ +package com.gdsc.game.dto; + +public class ActionResponse { + private String actionName; + private String description; + private int manaCost; + private int remainingCooltime; + private int remainingTurns; + + public ActionResponse(String actionName, String description, int manaCost, int remainingCooltime, int remainingTurns) { + this.actionName = actionName; + this.description = description; + this.manaCost = manaCost; + this.remainingCooltime = remainingCooltime; + this.remainingTurns = remainingTurns; + } + + public String getActionName() { + return actionName; + } + + public String getDescription() { + return description; + } + + public int getManaCost() { + return manaCost; + } + + public int getRemainingCooltime() { + return remainingCooltime; + } + + public int getRemainingTurns() { + return remainingTurns; + } +} + diff --git a/src/main/java/com/gdsc/game/dto/CharacterStatusResponse.java b/src/main/java/com/gdsc/game/dto/CharacterStatusResponse.java new file mode 100644 index 0000000..12bc13a --- /dev/null +++ b/src/main/java/com/gdsc/game/dto/CharacterStatusResponse.java @@ -0,0 +1,25 @@ +package com.gdsc.game.dto; + +public class CharacterStatusResponse { + private String name; + private int healthPoint; + private int manaPoint; + + public CharacterStatusResponse(String name, int healthPoint, int manaPoint) { + this.name = name; + this.healthPoint = healthPoint; + this.manaPoint = manaPoint; + } + + public String getName() { + return name; + } + + public int getHealthPoint() { + return healthPoint; + } + + public int getManaPoint() { + return manaPoint; + } +} diff --git a/src/main/java/com/gdsc/game/dto/ErrorResponse.java b/src/main/java/com/gdsc/game/dto/ErrorResponse.java new file mode 100644 index 0000000..0dca287 --- /dev/null +++ b/src/main/java/com/gdsc/game/dto/ErrorResponse.java @@ -0,0 +1,30 @@ +package com.gdsc.game.dto; + +import com.gdsc.game.exception.ErrorCode; + +public class ErrorResponse { + private final String code; + private final String message; + private final String status; + private final String detail; + + private ErrorResponse(String code, String message, String status, String detail) { + this.code = code; + this.message = message; + this.status = status; + this.detail = detail; + } + + public static ErrorResponse of(ErrorCode errorCode, String detail) { + return new ErrorResponse( + errorCode.getCode(), + errorCode.getMessage(), + errorCode.getStatus().toString(), + detail + ); + } + + public String getCode() { return code; } + public String getMessage() { return message; } + public String getStatus() { return status; } +} diff --git a/src/main/java/com/gdsc/game/exception/ErrorCode.java b/src/main/java/com/gdsc/game/exception/ErrorCode.java new file mode 100644 index 0000000..0582bb2 --- /dev/null +++ b/src/main/java/com/gdsc/game/exception/ErrorCode.java @@ -0,0 +1,34 @@ +package com.gdsc.game.exception; + +import org.springframework.http.HttpStatus; + +public enum ErrorCode { + INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "400", "잘못된 입력값입니다"), + INSUFFICIENT_MANA(HttpStatus.BAD_REQUEST, "400", "마나가 부족합니다"), + + JOB_NOT_FOUND(HttpStatus.NOT_FOUND, "404", "존재하지 않는 직업입니다"); + + private final HttpStatus status; + private final String code; + private final String message; + + ErrorCode(HttpStatus status, String code, String message) { + this.status = status; + this.code = code; + this.message = message; + } + + public HttpStatus getStatus() { + return status; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } +} + + diff --git a/src/main/java/com/gdsc/game/exception/GameException.java b/src/main/java/com/gdsc/game/exception/GameException.java new file mode 100644 index 0000000..1f218da --- /dev/null +++ b/src/main/java/com/gdsc/game/exception/GameException.java @@ -0,0 +1,20 @@ +package com.gdsc.game.exception; + +public class GameException extends RuntimeException { + + private final ErrorCode errorCode; + + public GameException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } + + public GameException(ErrorCode errorCode, String detail) { + super(detail); + this.errorCode = errorCode; + } + + public ErrorCode getErrorCode() { + return errorCode; + } +} diff --git a/src/main/java/com/gdsc/game/exception/GlobalExceptionHandler.java b/src/main/java/com/gdsc/game/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..173cfe9 --- /dev/null +++ b/src/main/java/com/gdsc/game/exception/GlobalExceptionHandler.java @@ -0,0 +1,19 @@ +package com.gdsc.game.exception; + +import com.gdsc.game.dto.ErrorResponse; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +@RestControllerAdvice +public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(GameException.class) + protected ResponseEntity handleGameException(GameException e) { + ErrorCode errorCode = e.getErrorCode(); + ErrorResponse response = ErrorResponse.of(errorCode, e.getMessage()); + return new ResponseEntity<>(response, errorCode.getStatus()); + } + +} diff --git a/src/main/java/com/gdsc/game/service/GameService.java b/src/main/java/com/gdsc/game/service/GameService.java new file mode 100644 index 0000000..5443f4c --- /dev/null +++ b/src/main/java/com/gdsc/game/service/GameService.java @@ -0,0 +1,143 @@ +package com.gdsc.game.service; + +import com.gdsc.game.domain.*; +import com.gdsc.game.domain.Character; +import com.gdsc.game.dto.ActionResponse; +import com.gdsc.game.dto.CharacterStatusResponse; +import com.gdsc.game.exception.GameException; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +import static com.gdsc.game.exception.ErrorCode.INSUFFICIENT_MANA; + +@Service +public class GameService { + private final Scanner scanner; + private Character character1; + private Character character2; + private int turns; + + public GameService() { + scanner = new Scanner(System.in); + } + + public CharacterStatusResponse getStatus(String name) { + Character character = findCharacterByName(name); + return new CharacterStatusResponse( + character.getName(), + character.getHealthPoint(), + character.getManaPoint() + ); + } + + private Character findCharacterByName(String name) { + if (character1.getName().equals(name)) return character1; + if (character2.getName().equals(name)) return character2; + throw new IllegalArgumentException("캐릭터를 찾지 못하였습니다"); + } + + public List showActions(String characterName, String actionName) { + Character character = findCharacterByName(characterName); + List actions = new ArrayList<>(); + + actions.add(new ActionResponse("공격", "데미지(1-10)", 0, 0, turns)); + actions.add(new ActionResponse("방어", "방어(1-10)", 0, 0, turns)); + actions.add(new ActionResponse("두번베기", "데미지(2-20)", 2, 0, turns)); + actions.add(new ActionResponse("3번베기", "데미지(3-30)", 3, 0, turns)); + actions.add(new ActionResponse("쎄게때리기", "데미지(5-50)", 5, 2, turns)); + + if (actionName.equals("name")) { + actions.sort((a1, a2) -> a1.getActionName().compareTo(a2.getActionName())); + } else if (actionName.equals("cooltime")) { + actions.sort((a1, a2) -> Integer.compare(a1.getRemainingCooltime(), a2.getRemainingCooltime())); + } + + return actions; + } + + public void startGame() { + System.out.println("===============GAME START================="); + System.out.println("캐릭터 2명의 이름을 입력해주세요 (쉼표로 구분해주세요):"); + String inputNames[] = scanner.nextLine().split(","); + System.out.println("직업을 입력해주세요 (쉼표로 구분해주세요, KNIGHT/WIZARD/ARCHER):"); + String[] inputJobs = scanner.nextLine().split(","); + System.out.println("레벨을 입력해주세요:"); + int level = Integer.parseInt(scanner.nextLine().trim()); + System.out.println("턴 수을 입력해주세요:"); + this.turns = scanner.nextInt(); + + character1 = new Character(inputNames[0].trim(), Job.fromString(inputJobs[0].trim()), level); + character2 = new Character(inputNames[1].trim(), Job.fromString(inputJobs[1].trim()), level); + + Character current = character1; + Character target = character2; + + + while (turns > 0) { + System.out.println("\n" + current.getName() + "의 차례입니다."); + System.out.print("행동을 선택하세요: "); + int choice = scanner.nextInt(); + + switch (choice) { + case 1: + AttackAction attack = new AttackAction(); + attack.execute(current, target); + break; + + case 2: + DefenseAction defense = new DefenseAction(); + defense.execute(current, target); + break; + + case 3: + if (current.getManaPoint() >= 2) { + SkillAction skill = new CutTwice(); + skill.execute(current, target); + } else { + throw new GameException(INSUFFICIENT_MANA); + } + break; + + case 4: + if (current.getManaPoint() >= 3) { + SkillAction skill = new CutThreeTimes(); + skill.execute(current, target); + } else { + throw new GameException(INSUFFICIENT_MANA); + } + break; + + default: + if (current.getManaPoint() >= 5) { + SkillAction skill = new PowerStrike(); + skill.execute(current, target); + } else { + throw new GameException(INSUFFICIENT_MANA); + } + break; + } + + Character temp = current; + current = target; + target = temp; + + if (current == character1) { + turns--; + } + + if (turns <= 0 || !character1.isAlive() || !character2.isAlive()) { + if (!character1.isAlive()) { + System.out.println(character2.getName() + "가 이겼습니다!"); + } else if (!character2.isAlive()) { + System.out.println(character1.getName() + "가 이겼습니다!"); + } + break; + } + } + } +} + +