213 lines
6.5 KiB
Kotlin
213 lines
6.5 KiB
Kotlin
|
||
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<Card>().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<NextTurnListener> = emptyList<NextTurnListener>().toMutableList()
|
||
|
||
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++
|
||
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 <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()
|
||
}
|
||
|
||
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
|
||
} |