From cb4ce44ea3248b4f9dc7e6c1166d44d1189e06c7 Mon Sep 17 00:00:00 2001 From: secminhr Date: Sun, 26 Aug 2018 13:43:22 +0000 Subject: [PATCH] Kotlin core finished Finish basic function Update card's effect --- Cards.kt | 127 ++++++++++++++++++++++++++++++ Character.kt | 211 +++++++++++++++++++++++++++++++++++++++++++++++++ Effect.kt | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++ GameLoop.kt | 34 ++++++++ Interfaces.kt | 180 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 765 insertions(+) create mode 100644 Cards.kt create mode 100644 Character.kt create mode 100644 Effect.kt create mode 100644 GameLoop.kt create mode 100644 Interfaces.kt diff --git a/Cards.kt b/Cards.kt new file mode 100644 index 0000000..41f1526 --- /dev/null +++ b/Cards.kt @@ -0,0 +1,127 @@ +import java.util.* + +abstract class AggressiveCard: Card { + override fun startSkill() { + println(activeString) + owner.enemy.effectBuffer = this.effects + owner.enemy.attackingCard = this + owner.enemy.effectBuffer.forEach { it.apply() } + owner.enemy.effectBuffer = emptyArray() + owner.enemy.attackingCard = null + } + + override fun toString(): String { + return "${this.id}. ${this.name}" + } +} + +abstract class DefensiveCard: Card { + class NotInPassiveContextException: Exception() + override fun startSkill() { + if(owner.effectBuffer.isEmpty()) { + throw NotInPassiveContextException() + } + println(activeString) + this.effects.forEach { it.apply() } + } + + override fun toString(): String { + return "${this.id}. ${this.name}" + } +} + +abstract class GeneralCard: Card { + override fun startSkill() { + println(activeString) + this.effects.forEach { it.apply() } + } + + override fun toString(): String { + return "${this.id}. ${this.name}" + } +} + +class AttackCard(override val owner: Character): AggressiveCard() { + override val name = "攻擊" + override val id = 1 + override val effects: Array = arrayOf(Damage(owner.enemy, decreaseLife = 2)) + override val activeString = "$owner 攻擊 ${owner.enemy}" + +} +class DefendCard(override val owner: Character): DefensiveCard() { + override val name = "防禦" + override val id = 2 + override val effects: Array = arrayOf(Defense(owner)) + override val activeString = "$owner 防禦成功" +} +class HealCard(override val owner: Character): GeneralCard() { + override val name = "治癒" + override val id = 3 + override val effects: Array = arrayOf(Heal(owner, increaseLife = 2)) + override val activeString = "$owner 回復2點生命" +} +class SupplyCard(override val owner: Character): GeneralCard() { + override val name = "補給" + override val id = 4 + override val effects: Array = arrayOf(DrawFromDeck(owner, numberOfCards = 2)) + override val activeString = "$owner 增加2張手牌" +} +class RobbingCard(override val owner: Character): AggressiveCard() { + override val name = "強奪" + override val id = 5 + override val effects: Array = arrayOf(ChooseFromCharacterHand(owner, owner.enemy)) + override val activeString = "$owner 正在對 ${owner.enemy} 行搶" +} +class SurpriceCard(override val owner: Character): AggressiveCard() { + override val name = "奇襲" + override val id = 6 + override val effects: Array = arrayOf(Damage(owner.enemy, decreaseLife = 2), + LoseCard(owner.enemy)) + override val activeString = "$owner 發動奇襲" +} +class AwareCard(override val owner: Character): DefensiveCard() { + override val name = "洞悉" + override val id = 7 + override val effects: Array = arrayOf(Defense(owner), DrawFromDeck(owner, numberOfCards = 2)) + override val activeString = "$owner 洞悉了 ${owner.enemy} 的 ${owner.attackingCard},並抽取2張手牌" +} +class PlanCard(override val owner: Character): GeneralCard() { + override val name = "妙策" + override val id = 8 + override val effects: Array = arrayOf(ChooseFromDeck(owner)) + override val activeString = "$owner 有個妙策" +} +class SweepCard(override val owner: Character): AggressiveCard() { + override val name = "掃射" + override val id = 9 + private val damage = Random().nextInt(6) + override val effects: Array + get() = arrayOf(Damage(owner.enemy, decreaseLife = damage)) + override val activeString = "$owner 對 ${owner.enemy} 進行掃射,威力是 $damage" +} +class BlessCard(override val owner: Character): GeneralCard() { + override val name = "加護" + override val id = 10 + override val effects: Array = arrayOf(Heal(owner, increaseLife = 3), SolvePoison(owner)) + override val activeString = "$owner 獲得加護,身上的毒素一掃而空,並回復3點生命" +} +class PoisonCard(override val owner: Character): GeneralCard() { + override val name = "劇毒" + override val id = 11 + override val effects: Array = arrayOf(Poison(owner.enemy)) + override val activeString = "$owner 在食物下毒,${owner.enemy} 中毒了" +} +class ChaosCard(override val owner: Character): AggressiveCard() { + override val name = "狂亂" + override val id = 12 + override val effects: Array = arrayOf(Heal(owner, increaseLife = 3), + Damage(owner.enemy, decreaseLife = 3)) + override val activeString = "$owner 進入狂亂模式,回復三點生命,並對 ${owner.enemy} 造成三點傷害" +} +class ReverseCard(override val owner: Character): DefensiveCard() { + override val name = "反制" + override val id = 13 + override val effects: Array = arrayOf(Reverse(owner), Defense(owner)) + override val activeString = "$owner 反制了 ${owner.enemy} 的攻擊,反彈了其效果" +} + diff --git a/Character.kt b/Character.kt new file mode 100644 index 0000000..9de1ef8 --- /dev/null +++ b/Character.kt @@ -0,0 +1,211 @@ +class Ann: Character() { + override val name = "安" + override val id = 1 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} + +class Guo: Character() { + override val name = "圭月" + override val id = 2 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} +class May: Character() { + override val name = "梅" + override val id = 3 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} +class Rabbit: Character() { + override val name = "小兔" + override val id = 4 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} +class Silver: Character() { + override val name = "銀" + override val id = 5 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} +class Tadashisaku: Character() { + override val name = "正作" + override val id = 6 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} +class W: Character() { + override val name = "W" + override val id = 7 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} +class Thunder: Character() { + override val name = "桑德" + override val id = 8 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} +class Haier: Character() { + override val name = "海爾" + override val id = 9 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} +class Yukimura: Character() { + override val name = "雪村" + override val id = 10 + override val deck: Deck by lazy { + val deck = Deck(mutableListOf(AttackCard(this), + DefendCard(this), + HealCard(this), + SupplyCard(this), + RobbingCard(this), + SurpriceCard(this), + AwareCard(this), + PlanCard(this), + SweepCard(this), + BlessCard(this), + PoisonCard(this), + ChaosCard(this), + ReverseCard(this))) + deck.shuffle() + deck + } +} \ No newline at end of file diff --git a/Effect.kt b/Effect.kt new file mode 100644 index 0000000..98b9f87 --- /dev/null +++ b/Effect.kt @@ -0,0 +1,213 @@ +import java.util.* + +class WrongParameterException: Exception() + +//This class take subjects[0] as the one to be damaged +class Damage(override vararg val subjects: Character, + private val decreaseLife: Int) : Effect() { + + init { + if (subjects.isEmpty() || decreaseLife <= 0) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = Damage(subject.enemy, decreaseLife = decreaseLife) + + override val id = 1 + + override fun apply() { + subject.life -= decreaseLife + } +} + +//This class take subjects[0] as the one the be healed +class Heal(override vararg val subjects: Character, private val increaseLife: Int) : Effect() { + + init { + if (subjects.isEmpty() || increaseLife <= 0) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = Heal(subject.enemy, increaseLife = increaseLife) + override val id = 2 + + override fun apply() { + subject.life += increaseLife + } +} + +//This class take subjects[0] as the one who draws from the deck +class DrawFromDeck(override vararg val subjects: Character, + private val numberOfCards: Int): Effect() { + + init { + if (subjects.isEmpty() || numberOfCards <= 0) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = DrawFromDeck(subject.enemy, numberOfCards = numberOfCards) + override val id = 3 + + override fun apply() { + subject.drawFromDeck(numberOfCards) + } +} + +//This class take subjects[0] as the one who draws +//subjects[1] as the one whose cards is taken +class ChooseFromCharacterHand(override vararg val subjects: Character): Effect() { + + init { + if (subjects.size < 2) { + throw WrongParameterException() + } + } + + private val taker = subjects[0] + private val giver = subjects[1] + override val reverseEffect: Effect + get() = ChooseFromCharacterHand(giver, taker) + override val id = 4 + + override fun apply() { + taker.chooseFromCards(giver.hand) + } +} + +//This class take subjects[0] as the one who loses one card randomly +class LoseCard(override vararg val subjects: Character): Effect() { + + init { + if (subjects.isEmpty()) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = LoseCard(subject.enemy) + override val id = 5 + + override fun apply() { + val cards = subject.hand + val index = Random().nextInt(cards.size) + cards.toMutableList().removeAt(index) + } +} + +//This class take subject[0] as the one who chooses the card +class ChooseFromDeck(override vararg val subjects: Character): Effect() { + init { + if (subjects.isEmpty()) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = ChooseFromDeck(subject.enemy) + override val id = 6 + + override fun apply() { + val random = Random() + val first = random.nextInt(subject.deck.size) + var second = random.nextInt(subject.deck.size) + while(second == first) { + second = random.nextInt(subject.deck.size) + } + var third = random.nextInt(subject.deck.size) + while(third == first || third == second) { + third = random.nextInt(subject.deck.size) + } + val cards = subject.deck.cards + subject.chooseFromCards(mutableListOf(cards[first], cards[second], cards[third])) + } +} + +//This class take subject[0] as the one who was poisoned +class SolvePoison(override vararg val subjects: Character): Effect() { + + init { + if (subjects.isEmpty()) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = SolvePoison(subject.enemy) + override val id = 7 + + override fun apply() { + subject.poisoned = false + } +} + +//The class take subject[0] as the one who's going to be poisoned +class Poison(override vararg val subjects: Character): Effect() { + + init { + if (subjects.isEmpty()) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = Poison(subject.enemy) + override val id = 8 + + override fun apply() { + subject.poisoned = true + } +} + +//This class take subject[0] as the one who needs to defense attack +class Defense(override vararg val subjects: Character): Effect() { + + init { + if (subjects.isEmpty()) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = Defense(subject.enemy) + override val id = 9 + + override fun apply() { + subject.effectBuffer = emptyArray() + subject.attackingCard = null + } +} + +//This class take subject[0] as the one who wants to reflect the effect +class Reverse(override vararg val subjects: Character): Effect() { + + init { + if (subjects.isEmpty()) { + throw WrongParameterException() + } + } + + private val subject = subjects[0] + override val reverseEffect: Effect + get() = Reverse(subject.enemy) + override val id = 10 + + override fun apply() { + subject.effectBuffer = subject.effectBuffer.map { it.reverseEffect }.toTypedArray() + subject.effectBuffer.forEach { it.apply() } + } +} + diff --git a/GameLoop.kt b/GameLoop.kt new file mode 100644 index 0000000..a1b3071 --- /dev/null +++ b/GameLoop.kt @@ -0,0 +1,34 @@ +class GameLoop(private val first: Character, + private val second: Character, + val complition: (Character, Character) -> Unit) { + + fun init() { + //enemy must be initialize first since the Card initiate needs them + first.enemy = second + second.enemy = first + first.endGameAsLoser = { //first's deck is dry + complition(second, first) + } + second.endGameAsLoser = { //second's deck is dry + complition(first, second) + } + first.drawFromDeck(3) + second.drawFromDeck(3) + } + + fun start() { + while(first.life > 0 && second.life > 0) { + first.nextTurn() + second.nextTurn() + } + endGame() + } + + + + private fun endGame() { + val winner = if (first.life > 0) first else second + val loser = winner.enemy + complition(winner, loser) + } +} \ No newline at end of file diff --git a/Interfaces.kt b/Interfaces.kt new file mode 100644 index 0000000..8b006ee --- /dev/null +++ b/Interfaces.kt @@ -0,0 +1,180 @@ + +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().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 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 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() +} +class EmptyDeckException: Exception() + +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 +} \ No newline at end of file