import java.util.* import kotlin.math.abs import kotlin.properties.Delegates //Character is the one who entered next turn //Returned value indicates whether this listener can be removed typealias NextTurnListener = (Character) -> Boolean abstract class Character { //basic info abstract val name: String abstract val id: Int abstract val deck: Deck var damageAddition: Int = 0 var damageMultiplier: Int = 1 var life: Int by Delegates.observable(0) {_, old, new -> val diff = abs(old-new) if (new < old) { println("${this.name} 受到 $diff 點傷害") } //heal description is provided in HealCard } protected set //This line of code belong's to life property //This describe whether the character can be damaged by non_SP attack var isInvincible = false var poisoned: Boolean by Delegates.observable(false) {_, _, new -> if(new) { println("${this.name} 中毒了") } else { println("${this.name} 毒性解除") } } var hand: Cards = emptyList().toMutableList() lateinit var enemy: Character //This will be set in GameLoop.init lateinit var endGameAsLoser: () -> Unit //This will be set in GameLoop.init //listeners inside this will be called immediately after character enter next turn var nextTurnListeners: MutableList = emptyList().toMutableList() var turn = 0 var effectBuffer: Array by Delegates.observable(emptyArray()) { _, old, new -> if(new.isNotEmpty() /* been clean*/ && old.map { it.reverseEffect }.toTypedArray().contentNotEquals(new) /*tmp effect of reverse*/) { val defensiveInHand = hand.filter { it is DefensiveCard } if (defensiveInHand.isNotEmpty()) { println("請選擇要使用的防禦卡片,或是打0放棄") println(defensiveInHand.filterIndexed { index,_ -> if (index != 0) { defensiveInHand[index].name != defensiveInHand[index-1].name } else { true } }) val scanner = Scanner(System.`in`) var id = scanner.nextInt() while(id != 0) { if(defensiveInHand.indexOfFirst { it.id == id } == -1){ id = scanner.nextInt() continue } else { val index = hand.indexOfFirst { it.id == id } hand[index].startSkill() hand.removeAt(index) break } } } } } var attackingCard: Card? = null fun drawFromDeck(num: Int = 1) { for (i in 1..num) { try { val card = deck.firstCard println("${this.name}抽到了 ${card.id}. ${card.name}") hand.add(card) println("牌組剩餘 ${deck.size} 張牌") } catch (e: NoSuchElementException) { println("牌庫已空,你抽到了死神") life = -9999 endGameAsLoser() } } } fun chooseFromCards(cards: Cards) { println("請選擇一張") cards.forEach { println("${it.id}. ${it.name} ") } val scanner = Scanner(System.`in`) while(true) { val id = scanner.nextInt() val card = try { cards.filter { it.id == id }[0] } catch (e: IndexOutOfBoundsException) { continue } println("${this.name} 選擇了 ${card.name}") hand.add(card) break } } fun nextTurn() { turn++ nextTurnListeners.forEach {listener -> val result = listener(this) if(result) { //listener can be removed nextTurnListeners.remove(listener) } } println("${this.name} 的第 $turn 回合") if (this.poisoned) { println("中毒,扣1滴血") this.life-- } println("生命值: $life") println("請出牌:(或輸入0放棄)") println(this.hand.filter { !(it is DefensiveCard) }) val scanner = Scanner(System.`in`) var id = scanner.nextInt() while (id != 0) { val index = hand.indexOfFirst { it.id == id } if (index == -1) { println("請出牌:(或輸入0放棄)") id = scanner.nextInt() continue } else { if (hand[index] is DefensiveCard) { println("請出牌:(或輸入0放棄)") id = scanner.nextInt() continue } else { hand[index].startSkill() hand.removeAt(index) drawFromDeck() break } } } } fun gotDamage(point: Int, defendable: Boolean = true) { if (defendable && this.isInvincible) { return } val damage = point * damageMultiplier + damageAddition life -= damage } fun heal(point: Int) { life += point } override fun toString() = this.name } fun Array.contentNotEquals(array: Array) = !this.contentEquals(array) class Deck(cards: Cards) { var cards: Cards = cards private set val size: Int get() = cards.size val firstCard: Card get() { val card = cards.first() cards.removeAt(0) return card } fun shuffle() = cards.shuffle() } typealias Cards = MutableList interface Card { val owner: Character val name: String val id: Int val activeString: String //This must be a calculated property instead of a stored value //in order to prevent StackOverflowError val effects: Array fun startSkill() } abstract class Effect { abstract val subjects: Array //The structure of subjects will defined by the concrete classes abstract val reverseEffect: Effect abstract val id: Int abstract fun apply() override fun toString(): String = this.javaClass.name override fun equals(other: Any?): Boolean { return if(other is Effect) { this.id == other.id } else { false } } override fun hashCode() = this.id }