diff --git a/FakeMon/src/fmon/Game.scala b/FakeMon/src/fmon/Game.scala index 3064738..f2cac0c 100644 --- a/FakeMon/src/fmon/Game.scala +++ b/FakeMon/src/fmon/Game.scala @@ -18,8 +18,8 @@ object Game { implicit val rng = new scala.util.Random() val form1 = Form("Diabolo") val form2 = Form("Chanilla") - val movepool1 = IndexedSeq(Move("Aqua Jet")) - val movepool2 = IndexedSeq(Move("Thunder Wave")) + val movepool1 = IndexedSeq(Move("Confuse Ray")) + val movepool2 = IndexedSeq(Move("Headbutt")) val p1 = TrainerID("Jaeda", Gender.Female, 0) val p2 = TrainerID("Wild Monster", Gender.Male, 0) val party1 = new Party(p1, new MonsterPtr(new Monster(new StorageMon("Allied Mon", 500, Gene.randomGene(null, form1), form1, Statistic.emptyEvs, movepool1))), IndexedSeq()) diff --git a/FakeMon/src/fmon/battle/BattleEngine.scala b/FakeMon/src/fmon/battle/BattleEngine.scala index 0ae5985..7bd4966 100644 --- a/FakeMon/src/fmon/battle/BattleEngine.scala +++ b/FakeMon/src/fmon/battle/BattleEngine.scala @@ -23,8 +23,8 @@ class BattleEngine(val player: Party, val enemy: Party)(implicit val rng: Random val queue = rng.shuffle(actions).sorted // Shuffle to randomize in the event of a tie queue.foreach(useMove) val eotQueue = Seq(player.lead, enemy.lead).sortBy(_(Speed)) - eotQueue.foreach(mon => mon.status.map(_.onResidual(mon))) - + eotQueue.foreach(mon => mon.statuses.map(_.onResidual(mon))) + if (!player.lead.isAlive) { if (player.canFight) { val replace = player.pollReplacement(enemy) @@ -51,7 +51,7 @@ class BattleEngine(val player: Party, val enemy: Party)(implicit val rng: Random val move = action.move val target = action.target if (user.isAlive) { - if (user.status.forall(_.onBeforeMove(user, move, target))) { + if (user.statuses.forall(_.onBeforeMove(user, move, target))) { move.useMove(user, target) } } diff --git a/FakeMon/src/fmon/stat/Element.scala b/FakeMon/src/fmon/stat/Element.scala index edbf909..1a65dff 100644 --- a/FakeMon/src/fmon/stat/Element.scala +++ b/FakeMon/src/fmon/stat/Element.scala @@ -16,8 +16,7 @@ case class Element(val name : String, val effect : Map[String, Double]) { } object Element { - private val elements = YamlHelper.extractSeq[Element](Element.getClass.getResourceAsStream("data/elements.yaml")) - private val fromName = elements.map(e => (e.name, e)).toMap + private val elements = YamlHelper.extractMap[Element](Element.getClass.getResourceAsStream("data/elements.yaml")) - def apply(name : String) = fromName(name) + def apply(name : String) = elements(name) } \ No newline at end of file diff --git a/FakeMon/src/fmon/stat/Monster.scala b/FakeMon/src/fmon/stat/Monster.scala index 089a8cd..494f45b 100644 --- a/FakeMon/src/fmon/stat/Monster.scala +++ b/FakeMon/src/fmon/stat/Monster.scala @@ -14,6 +14,7 @@ class Monster(val base : StorageMon) { var hp = stats(Hp) var status : Option[Status] = None + var volatile = Seq[Status]() def isAlive = hp > 0 @@ -36,11 +37,34 @@ class Monster(val base : StorageMon) { hp = Math.min(stats(Hp), hp + healing) } + def statuses = volatile ++ status + def cureStatus() { status.map(_.onEnd(this)) status = None } + def +=(s : Status)(implicit rng: Random) = { + if (s.effectType == EffectType.Volatile && !volatile.exists(_.name == s.name)) { + volatile +:= s + s.onStart(this) + } else if (s.effectType == EffectType.NonVolatile && status == None) { + status = Some(s) + s.onStart(this) + } else { + println("But it failed!") + } + } + + def -=(s : Status) = { + if (s.effectType == EffectType.Volatile) { + s.onEnd(this) + volatile = volatile.filterNot(_.name == s.name) + } else { + cureStatus() + } + } + def applyBoost(s : Stat, boost : Int) { val modified = boosts.getOrElse(s, 0) + boost boosts = boosts.updated(s, Math.min(MaxBoost, Math.max(-MaxBoost, modified))) diff --git a/FakeMon/src/fmon/stat/Move.scala b/FakeMon/src/fmon/stat/Move.scala index c4a312f..7bc1da7 100644 --- a/FakeMon/src/fmon/stat/Move.scala +++ b/FakeMon/src/fmon/stat/Move.scala @@ -60,6 +60,7 @@ abstract class Move extends MoveTurn { val crit: Int val status: StatusTemplate val drain: Fraction + val effect: Secondary val selfEffect: Secondary val secondary: Secondary // boosts @@ -97,6 +98,9 @@ abstract class Move extends MoveTurn { } applyBoosts(target, boosts) applyStatus(target, status) + if (effect != null) { + applyEffect(target, effect) + } if (selfEffect != null) { applyEffect(user, selfEffect) } @@ -104,7 +108,6 @@ abstract class Move extends MoveTurn { applyEffect(target, secondary) } - // TODO : Support moves // TODO : Multiparty } else { println("Missed!") @@ -155,18 +158,12 @@ abstract class Move extends MoveTurn { def applyEffect(target: Monster, effect: Secondary)(implicit rng: Random) { applyBoosts(target, effect.boosts) applyStatus(target, effect.status) + applyStatus(target, effect.volatile) } def applyStatus(target: Monster, status: StatusTemplate)(implicit rng: Random) { if (target.isAlive && status != null) { - if (target.status == None) { - // apply status - val s = status.build - target.status = Some(s) - s.onStart(target) - } else { - println("But it failed!") - } + target += status.build } } @@ -204,6 +201,7 @@ case class MoveToken( val status: String, val self: SecondaryToken, val secondary: SecondaryToken, + val volatileStatus: String, val drain: Fraction, val recoil: Fraction, @JsonScalaEnumeration(classOf[TargetType]) val target: Target = Target.Normal, @@ -212,6 +210,7 @@ case class MoveToken( def instantiate() = { val token = this + val effectToken = SecondaryToken(100, Map(), null, token.volatileStatus, null) new Move { val name = token.name val desc = token.shortDesc @@ -228,6 +227,7 @@ case class MoveToken( val crit = token.critRatio val status = if (token.status != null) Status(token.status) else null val drain = if (token.drain != null) token.drain else if (token.recoil != null) -token.recoil else 0.frac + val effect = effectToken.instantiate() val selfEffect = if (token.self != null) token.self.instantiate() else null val secondary = if (token.secondary != null) token.secondary.instantiate() else null } diff --git a/FakeMon/src/fmon/stat/Secondary.scala b/FakeMon/src/fmon/stat/Secondary.scala index 150f969..1d9f34b 100644 --- a/FakeMon/src/fmon/stat/Secondary.scala +++ b/FakeMon/src/fmon/stat/Secondary.scala @@ -4,7 +4,7 @@ abstract class Secondary { val chance : Int val boosts : Map[Stat, Int] val status : StatusTemplate - // val volatileStatus + val volatile: StatusTemplate //val onHit : // val self } @@ -13,7 +13,7 @@ case class SecondaryToken( val chance : Int, val boosts : Map[String, Int], val status : String, - //val volatileStatus + val volatileStatus: String, val onHit : String ) { def instantiate() : Secondary = { @@ -22,6 +22,7 @@ case class SecondaryToken( val chance = self.chance val boosts = if (self.boosts != null) self.boosts.map{case (s, i) => (Statistic(s), i)} else Map() val status = if (self.status != null) Status(self.status) else null + val volatile = if (self.volatileStatus != null) Status(self.volatileStatus) else null } } } diff --git a/FakeMon/src/fmon/stat/Status.scala b/FakeMon/src/fmon/stat/Status.scala index 213175e..ffcd61d 100644 --- a/FakeMon/src/fmon/stat/Status.scala +++ b/FakeMon/src/fmon/stat/Status.scala @@ -8,11 +8,24 @@ import scala.util.Random import scala.io.Source import fmon.util._ +import EffectType.Volatile + +object EffectType extends Enumeration { + + val NonVolatile, Volatile, Field, Weather = Value + + def parse(s : String, default : EffectType) = s match { + case "status" => NonVolatile + case "volatile" => Volatile + case "weather" => Weather + case _ => default + } +} class Status(template: StatusTemplate) { def name = template.name // val id - // val effectType + def effectType = template.effectType def onStart(mon: Monster)(implicit rng: Random) = template.onStart(this, mon, rng) def onEnd(mon: Monster) = template.onEnd(this, mon) def onModifyStat(mon: Monster, stat: Stat) = template.onModifyStat(this, mon, stat) @@ -33,7 +46,7 @@ class Status(template: StatusTemplate) { abstract class StatusTemplate { val name: String // val id - // val effectType + val effectType: EffectType val onStart: (Status, Monster, Random) => Unit val onEnd: (Status, Monster) => Unit val onModifyStat: (Status, Monster, Stat) => Fraction @@ -52,6 +65,7 @@ abstract class StatusTemplate { case class StatusToken( val name: String, + val effectType: String, val onStart: String, val onEnd: String, val onBeforeMove: String, @@ -62,6 +76,7 @@ case class StatusToken( val self = this new StatusTemplate { val name = self.name + val effectType = EffectType.parse(self.effectType, Volatile) val onStart = Status.compileOnStart(self.onStart) val onEnd = Status.compileOnEnd(self.onEnd) val onBeforeMove = Status.compileOnBeforeMove(self.onBeforeMove) @@ -74,7 +89,7 @@ case class StatusToken( object Status { private var statuses = Map[String, StatusTemplate]() - val tokens = YamlHelper.extractSeq[StatusToken](Source.fromInputStream(Move.getClass.getResourceAsStream("data/statuses.yaml"))).map(t => (t.name, t)).toMap + val tokens = YamlHelper.extractMap[StatusToken](Source.fromInputStream(Status.getClass.getResourceAsStream("data/statuses.yaml"))) def apply(name: String) = { if (!statuses.contains(name)) { @@ -135,6 +150,7 @@ object Status { import scala.util.Random import fmon.stat._ import fmon.stat.Statistic._ + import fmon.util._ def onBeforeMove(self: Status, mon: Monster, move: MoveTurn, target: Monster, rng: Random): Boolean = { $code } diff --git a/FakeMon/src/fmon/stat/data/elements.yaml b/FakeMon/src/fmon/stat/data/elements.yaml index 30ea6c2..7769681 100644 --- a/FakeMon/src/fmon/stat/data/elements.yaml +++ b/FakeMon/src/fmon/stat/data/elements.yaml @@ -1,131 +1,174 @@ -[ -{ - name: Normal, - effect: { - Rock: 0.5, - Steel: 0.5, - Ghost: 0 - } -}, -{ - name: Fire, - effect: { - Fire: 0.5, - Water: 0.5, - Rock: 0.5, - Dragon: 0.5, - Bug: 2.0, - Grass: 2.0, - Ice: 2.0, - Steel: 2 - } -}, -{ - name: Grass, - effect: { - Bug: 0.5, - Dragon: 0.5, - Fire: 0.5, - Flying: 0.5, - Water: 2.0, - Ground: 2.0, - Rock: 2.0, - Grass: 0.5, +Bug: + effect: + Dark: 2.0 + Fairy: 0.5 + Fighting: 0.5 + Fire: 0.5 + Flying: 0.5 + Ghost: 0.5 + Grass: 2.0 Poison: 0.5 - } -}, -{ - name: Water, - effect: { - Fire: 2.0, - Ground: 2.0, - Rock: 2.0, - Water: 0.5, - Grass: 0.5, + Psychic: 2.0 + Steel: 0.5 + name: Bug +Dark: + effect: + Dark: 0.5 + Fairy: 0.5 + Fighting: 0.5 + Ghost: 2.0 + Psychic: 2.0 + name: Dark +Dragon: + effect: + Dragon: 2.0 + Fairy: 0.0 + Steel: 0.5 + name: Dragon +Electric: + effect: Dragon: 0.5 - } -}, -{ - name: Electric, - effect: { - Flying: 2.0, - Water: 2.0, - Dragon: 0.5, - Electric: 0.5, - Grass: 0.5, - Ground: 0 - } -}, -{ - name: Ice, - effect: { - Dragon: 2.0, - Flying: 2.0, - Grass: 2.0, - Ground: 2.0, - Fire: 0.5, - Ice: 0.5, - Water: 0.5, + Electric: 0.5 + Flying: 2.0 + Grass: 0.5 + Ground: 0.0 + Water: 2.0 + name: Electric +Fairy: + effect: + Dark: 2.0 + Dragon: 2.0 + Fighting: 2.0 + Fire: 0.5 + Poison: 0.5 Steel: 0.5 - } -}, -{ - name: Psychic, - effect: { - Fighting: 2.0, - Poison: 2.0, - Psychic: 0.5, - Steel: 0.5, - Dark: 0 - } -}, -{ - name: Dark, - effect: { - Ghost: 2.0, - Psychic: 2.0, - Dark: 0.5, - Fairy: 0.5, - Fight: 0.5 - } -}, -{ - name: Fairy, - effect: { - Dark: 2.0, - Dragon: 2.0, - Fighting: 2.0, - Fire: 0.5, - Poison: 0.5, - Steel: 0.5 - } -}, -{ - name: Fighting, - effect: { - Dark: 2.0, - Ice: 2.0, - Normal: 2.0, - Rock: 2.0, - Steel: 2.0, - Bug: 0.5, - Fairy: 0.5, - Flying: 0.5, - Poison: 0.5, - Psychic: 0.5, + name: Fairy +Fighting: + effect: + Bug: 0.5 + Dark: 2.0 + Fairy: 0.5 + Flying: 0.5 Ghost: 0.0 - } -}, -{ - name: Poison, - effect: { - Fairy: 2.0, - Grass: 2.0, - Poison: 0.5, - Ground: 0.5, - Rock: 0.5, - Ghost: 0.5, + Ice: 2.0 + Normal: 2.0 + Poison: 0.5 + Psychic: 0.5 + Rock: 2.0 + Steel: 2.0 + name: Fighting +Fire: + effect: + Bug: 2.0 + Dragon: 0.5 + Fire: 0.5 + Grass: 2.0 + Ice: 2.0 + Rock: 0.5 + Steel: 2.0 + Water: 0.5 + name: Fire +Flying: + effect: + Bug: 2.0 + Electric: 0.5 + Fighting: 2.0 + Grass: 2.0 + Rock: 0.5 + Steel: 0.5 + name: Flying +Ghost: + effect: + Dark: 0.5 + Ghost: 2.0 + Normal: 0.0 + Psychic: 2.0 + name: Ghost +Grass: + effect: + Bug: 0.5 + Dragon: 0.5 + Fire: 0.5 + Flying: 0.5 + Grass: 0.5 + Ground: 2.0 + Poison: 0.5 + Rock: 2.0 + Steel: 0.5 + Water: 2.0 + name: Grass +Ground: + effect: + Bug: 0.5 + Electric: 2.0 + Fire: 2.0 + Flying: 0.0 + Grass: 0.5 + Poison: 2.0 + Rock: 2.0 + Steel: 2.0 + name: Ground +Ice: + effect: + Dragon: 2.0 + Fire: 0.5 + Flying: 2.0 + Grass: 2.0 + Ground: 2.0 + Ice: 0.5 + Steel: 0.5 + Water: 0.5 + name: Ice +Normal: + effect: + Ghost: 0.0 + Rock: 0.5 + Steel: 0.5 + name: Normal +Poison: + effect: + Fairy: 2.0 + Ghost: 0.5 + Grass: 2.0 + Ground: 0.5 + Poison: 0.5 + Rock: 0.5 Steel: 0.0 - } -} -] \ No newline at end of file + name: Poison +Psychic: + effect: + Dark: 0.0 + Fighting: 2.0 + Poison: 2.0 + Psychic: 0.5 + Steel: 0.5 + name: Psychic +Rock: + effect: + Bug: 2.0 + Fighting: 0.5 + Fire: 2.0 + Flying: 2.0 + Ground: 0.5 + Ice: 2.0 + Steel: 0.5 + name: Rock +Steel: + effect: + Electric: 0.5 + Fairy: 2.0 + Fire: 0.5 + Ice: 2.0 + Rock: 2.0 + Steel: 0.5 + Water: 0.5 + name: Steel +Water: + effect: + Dragon: 0.5 + Fire: 2.0 + Grass: 0.5 + Ground: 2.0 + Rock: 2.0 + Water: 0.5 + name: Water diff --git a/FakeMon/src/fmon/stat/data/moves.yaml b/FakeMon/src/fmon/stat/data/moves.yaml index 3dea64e..df2361d 100644 --- a/FakeMon/src/fmon/stat/data/moves.yaml +++ b/FakeMon/src/fmon/stat/data/moves.yaml @@ -112,6 +112,28 @@ type: "Fighting" zMovePower: 190 contestType: "Tough" + +- name: Confuse Ray + accuracy: 100 + basePower: 0 + category: Status + contestType: Clever + desc: Causes the target to become confused. + flags: + mirror: 1 + protect: 1 + reflectable: 1 + id: confuseray + num: 109 + pp: 10 + priority: 0 + secondary: null + shortDesc: Confuses the target. + target: Normal + type: Ghost + volatileStatus: confusion + zMoveBoost: + MAtk: 1 - name: Electro Ball num: 486 @@ -165,6 +187,28 @@ zMovePower: 120 contestType: "Beautiful" +- name: Headbutt + accuracy: 100 + basePower: 70 + category: Physical + contestType: Tough + desc: Has a 30% chance to flinch the target. + flags: + contact: 1 + mirror: 1 + protect: 1 + id: headbutt + num: 29 + pp: 15 + priority: 0 + secondary: + chance: 30 + volatileStatus: flinch + shortDesc: 30% chance to flinch the target. + target: Normal + type: Normal + zMovePower: 140 + - name: Ice Beam accuracy: 100 basePower: 90 diff --git a/FakeMon/src/fmon/stat/data/statuses.yaml b/FakeMon/src/fmon/stat/data/statuses.yaml index a320d98..ed1854a 100644 --- a/FakeMon/src/fmon/stat/data/statuses.yaml +++ b/FakeMon/src/fmon/stat/data/statuses.yaml @@ -1,4 +1,5 @@ -- name: 'brn' +brn: + name: 'brn' id: 'brn' num: 0 effectType: 'Status' @@ -24,8 +25,54 @@ onResidual: | mon.takeDamage(mon(Hp) / 16); println(s"${mon} was hurt by its burn!") - -- name: 'par' + +confusion: + id: confusion + name: confusion + num: 0 + onBeforeMove: |- + self.intData("time") -= 1 + if (self.intData("time") <= 0) { + mon -= self + true + } else { + println(s"${mon} is confused!") + if (rng.chance(1, 3)) { + // confusion damage + val maxDmg = (2 * mon.level / 5 + 2) * 40 * mon(PAtk) / (mon(PDef) * 50) + 2 + val minDmg = (17 \\ 20) * maxDmg + val dmg = rng.nextInt(minDmg, maxDmg) + println(s"${mon} hurt itself in its confusion.") + mon.takeDamage(dmg) + false + } else { + true + } + } + /* + this.activeTarget = mon; + let damage = this.getDamage(mon, mon, 40); + if (typeof damage !== 'number') throw new Error('Confusion damage not dealt'); + mon.takeDamage(damage, mon, mon, /** @type {ActiveMove} */ ({ + 'id': 'confused', + 'effectType': 'Move', + 'type': '???' + })); + */ + onBeforeMovePriority: 3 + onEnd: println(s"${mon} snapped out of its confusion.") + onStart: |- + println(s"${mon} was confused!") + /* + if (sourceEffect && sourceEffect.id === 'lockedmove') { + this.add('-start', target, 'confusion', '[fatigue]'); + } else { + this.add('-start', target, 'confusion'); + }*/ + self.intData("time") = rng.nextInt(2, 6); + +par: + name: 'par' id: 'par' num: 0 effectType: 'Status' @@ -55,7 +102,8 @@ true } -- name: 'slp' +slp: + name: 'slp' id: 'slp' num: 0 effectType: 'Status' @@ -91,7 +139,21 @@ !move.flags("sleepUsable") } -- name: 'frz' +flinch: + duration: 1 + id: flinch + name: flinch + num: 0 + onBeforeMove: |- + println(s"${mon} flinched!") + false + onBeforeMovePriority: 8 + onResidualOrder: 13 + onResidual: | + mon -= self + +frz: + name: 'frz' effectType: Status id: frz num: 0 @@ -127,7 +189,8 @@ mon.cureStatus() } -- name: 'psn' +psn: + name: 'psn' id: 'psn' num: 0 effectType: 'Status' @@ -147,7 +210,8 @@ mon.takeDamage(mon(Hp) / 8); println(s"${mon} was damaged by poison!") -- name: 'tox' +tox: + name: 'tox' id: 'tox' num: 0 effectType: 'Status' diff --git a/FakeMon/src/fmon/stat/package.scala b/FakeMon/src/fmon/stat/package.scala index 20c40aa..03900c6 100644 --- a/FakeMon/src/fmon/stat/package.scala +++ b/FakeMon/src/fmon/stat/package.scala @@ -11,6 +11,7 @@ import fmon.util.Dice package object stat { type Stat = Statistic.Value + type EffectType = EffectType.Value type MoveType = MoveType.Value type Gender = Gender.Value type Nature = Nature.Val diff --git a/FakeMon/src/fmon/util/YamlHelper.scala b/FakeMon/src/fmon/util/YamlHelper.scala index 4cee02c..2fc081e 100644 --- a/FakeMon/src/fmon/util/YamlHelper.scala +++ b/FakeMon/src/fmon/util/YamlHelper.scala @@ -42,4 +42,12 @@ object YamlHelper { def extractSeq[T](source : InputStream)(implicit m : Manifest[T]) : IndexedSeq[T] = { mapper.readValue[IndexedSeq[T]](source) } + + def extractMap[T](source : Source)(implicit mf : Manifest[T]) : Map[String, T] = { + extract[Map[String, T]](source) + } + + def extractMap[T](source : InputStream)(implicit m: Manifest[T]) : Map[String, T] = { + mapper.readValue[Map[String, T]](source) + } } \ No newline at end of file