180 lines
5.5 KiB
Kotlin
180 lines
5.5 KiB
Kotlin
|
||
import java.util.*
|
||
import kotlin.math.abs
|
||
import kotlin.properties.Delegates
|
||
|
||
abstract class Character {
|
||
//basic info
|
||
abstract val name: String
|
||
abstract val id: Int
|
||
abstract val deck: Deck
|
||
|
||
var life: Int by Delegates.observable(20) {_, old, new ->
|
||
val diff = abs(old-new)
|
||
if (new < old) {
|
||
println("${this.name} 受到 $diff 點傷害")
|
||
} //heal description is provided in HealCard
|
||
}
|
||
var poisoned: Boolean by Delegates.observable(false) {_, _, new ->
|
||
if(new) {
|
||
println("${this.name} 中毒了")
|
||
} else {
|
||
println("${this.name} 毒性解除")
|
||
}
|
||
}
|
||
var hand: Cards = emptyList<Card>().toMutableList()
|
||
lateinit var enemy: Character //This will be set in GameLoop.init
|
||
lateinit var endGameAsLoser: () -> Unit //This will be set in GameLoop.init
|
||
var turn = 0
|
||
|
||
var effectBuffer: Array<Effect> 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++
|
||
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
|
||
}
|
||
}
|
||
}
|
||
}
|
||
override fun toString() = this.name
|
||
}
|
||
|
||
fun <T> Array<T>.contentNotEquals(array: Array<out T>) = !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()
|
||
}
|
||
class EmptyDeckException: Exception()
|
||
|
||
typealias Cards = MutableList<Card>
|
||
|
||
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<Effect>
|
||
|
||
fun startSkill()
|
||
}
|
||
|
||
abstract class Effect {
|
||
abstract val subjects: Array<out Character> //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
|
||
} |