diff --git a/FakeMon/src/fmon/Config.scala b/FakeMon/src/fmon/Config.scala new file mode 100644 index 0000000..9d9238f --- /dev/null +++ b/FakeMon/src/fmon/Config.scala @@ -0,0 +1,21 @@ +package fmon + +import fmon.util.YamlHelper + +case class Config( + val title: String, + val crit: Double, + val stab: Double, + val maxBoost: Int + ) { + +} + +object Config { + val config = YamlHelper.extract[Config](getClass.getResourceAsStream("config.yaml")) + + def title = config.title + def crit = config.crit + def stab = config.stab + def maxBoost = config.maxBoost +} \ No newline at end of file diff --git a/FakeMon/src/fmon/Game.scala b/FakeMon/src/fmon/Game.scala index e940abc..7f25f75 100644 --- a/FakeMon/src/fmon/Game.scala +++ b/FakeMon/src/fmon/Game.scala @@ -14,23 +14,6 @@ case class Prop(url : Seq[String]) object Game { def main(args : Array[String]): Unit = { - /* - implicit val rng = new scala.util.Random() - val form1 = Form("Diabolo") - val form2 = Form("Chanilla") - val movepool1 = IndexedSeq(Move("eruption")) - val movepool2 = IndexedSeq(Move("flail")) - val p1 = TrainerID("Jaeda", Gender.Female, 0) - val p2 = TrainerID("Wild Monster", Gender.Male, 0) - val party1 = new Party(p1, new MonsterPtr(Monster.generate("Allied Mon", p1, 500, form1, movepool1)), IndexedSeq()) - val party2 = new Party(p2, new MonsterPtr(Monster.generate("Wild Mon", p2, 500, form2, movepool2)), IndexedSeq( - Monster.generate("Sideboard Mon", p2, 500, form2, movepool2) - )) - println(form1.xpCurve) - val engine = new BattleEngine(party1, party2) - engine.play() - println(party1.lead.elements) - * */ Application.launch(classOf[Game], args: _*) } } @@ -46,8 +29,8 @@ class Game extends Application { implicit val rng = new scala.util.Random() val form1 = Form("Diabolo") val form2 = Form("Chanilla") - val movepool1 = IndexedSeq(Move("eruption"), Move("willowisp"), Move("thunderbolt"), Move("thunderwave")) - val movepool2 = IndexedSeq(Move("flail")) + val movepool1 = IndexedSeq(Move("eruption"), Move("willowisp"), Move("thunderbolt"), Move("thunderwave"), Move("blastburn")) + val movepool2 = IndexedSeq(Move("blastburn")) val p1 = TrainerID("Jaeda", Gender.Female, 0) val p2 = TrainerID("Wild Monster", Gender.Male, 0) val party1 = new Party(p1, new MonsterPtr(Monster.generate("Allied Mon", p1, 500, form1, movepool1)), IndexedSeq()) @@ -57,7 +40,7 @@ class Game extends Application { val engine = new BattleEngine(party1, party2) controller.setEngine(engine) - primaryStage.setTitle("FakeMon Battle Simulator") + primaryStage.setTitle(Config.title) primaryStage.setScene(scene) primaryStage.show() } diff --git a/FakeMon/src/fmon/battle/BattleEngine.scala b/FakeMon/src/fmon/battle/BattleEngine.scala index 454249b..e0a8b4e 100644 --- a/FakeMon/src/fmon/battle/BattleEngine.scala +++ b/FakeMon/src/fmon/battle/BattleEngine.scala @@ -18,10 +18,17 @@ class BattleEngine(val player: Party, val enemy: Party)(implicit val reader: Sig def playTurn(): Unit = { val playerAction = player.pollAction(enemy) - playTurn(playerAction) + playTurnImp(playerAction) } def playTurn(playerAction: Action): Unit = { + playTurnImp(playerAction) + while (player.lead.isLocked) { + playTurn() + } + } + + def playTurnImp(playerAction: Action): Unit = { val enemyAction = enemy.pollAction(player) val actions = Seq(playerAction, enemyAction) diff --git a/FakeMon/src/fmon/battle/BattleUI.scala b/FakeMon/src/fmon/battle/BattleUI.scala index 2db932f..485dd33 100644 --- a/FakeMon/src/fmon/battle/BattleUI.scala +++ b/FakeMon/src/fmon/battle/BattleUI.scala @@ -52,7 +52,9 @@ class BattleUI extends SignalConsumer { } def onMove(move: Move): Unit = { - val action = Action(engine.player.lead, move, engine.enemy.lead) + implicit val rng = engine.rng + implicit val reader = engine + val action = Action(engine.player.lead, move, engine.player.pollTarget(move, engine.player.lead, engine.enemy)) engine.playTurn(action) updateUI() } @@ -68,7 +70,6 @@ class BattleUI extends SignalConsumer { val r = 25 val color = Color.AliceBlue val center = findCenter(target) - print(center) val circle = new Circle { radius = r diff --git a/FakeMon/src/fmon/battle/InfoWidget.scala b/FakeMon/src/fmon/battle/InfoWidget.scala index 1e46c9b..bf692da 100644 --- a/FakeMon/src/fmon/battle/InfoWidget.scala +++ b/FakeMon/src/fmon/battle/InfoWidget.scala @@ -6,6 +6,7 @@ import javafx.scene.{control => jfxsc} import scalafx.Includes._ import scalafx.scene.control._ +import fmon._ import fmon.stat.Monster import fmon.stat._ diff --git a/FakeMon/src/fmon/battle/package.scala b/FakeMon/src/fmon/battle/package.scala index a5081b2..7cdbe7a 100644 --- a/FakeMon/src/fmon/battle/package.scala +++ b/FakeMon/src/fmon/battle/package.scala @@ -1,12 +1,5 @@ package fmon -import scala.language.implicitConversions -import scala.util.Random - -import fmon.util._ - - package object battle { - implicit def rngDice(rng : Random) = new Dice(rng) - implicit def int2Helper(x : Int) = new IntFraction(x) + } \ No newline at end of file diff --git a/FakeMon/src/fmon/config.yaml b/FakeMon/src/fmon/config.yaml new file mode 100644 index 0000000..5a6ee69 --- /dev/null +++ b/FakeMon/src/fmon/config.yaml @@ -0,0 +1,5 @@ +title: FakeMon Engine Demo +crit: 1.5 +stab: 1.5 +maxBoost: 6 +newday: 02:00 diff --git a/FakeMon/src/fmon/package.scala b/FakeMon/src/fmon/package.scala new file mode 100644 index 0000000..319fa74 --- /dev/null +++ b/FakeMon/src/fmon/package.scala @@ -0,0 +1,13 @@ + +import scala.language.implicitConversions +import scala.util.Random + +import fmon.stat._ +import fmon.util.{Dice, IntFraction} + +package object fmon { + + implicit def rngDice(rng : Random) = new Dice(rng) + implicit def int2Helper(x : Int) = new IntFraction(x) + implicit def ptrToMonster(ptr : MonsterPtr) : Monster = ptr.mon +} \ No newline at end of file diff --git a/FakeMon/src/fmon/stat/Ability.scala b/FakeMon/src/fmon/stat/Ability.scala index 98b40bf..45793b6 100644 --- a/FakeMon/src/fmon/stat/Ability.scala +++ b/FakeMon/src/fmon/stat/Ability.scala @@ -53,10 +53,10 @@ object Ability { private val header = """ |import scala.util.Random |import fmon.battle.msg._ + |import fmon._ |import fmon.stat._ |import fmon.stat.MoveType._ |import fmon.stat.Statistic._ - |import fmon.util._ """ private val helper = """ implicit val gen = rng diff --git a/FakeMon/src/fmon/stat/Monster.scala b/FakeMon/src/fmon/stat/Monster.scala index 6564aeb..676625e 100644 --- a/FakeMon/src/fmon/stat/Monster.scala +++ b/FakeMon/src/fmon/stat/Monster.scala @@ -2,6 +2,7 @@ package fmon.stat import scala.util._ +import fmon._ import fmon.battle.msg._ import fmon.util._ @@ -69,7 +70,7 @@ class Monster(val base : StorageMon) { def applyBoost(s : Stat, boost : Int, effect: EffectSource)(implicit reader: SignalConsumer, rng: Random) { val modified = boosts.getOrElse(s, 0) + boost - boosts = boosts.updated(s, Math.min(MaxBoost, Math.max(-MaxBoost, modified))) + boosts = boosts.updated(s, Math.min(Config.maxBoost, Math.max(-Config.maxBoost, modified))) if (boost > 0) { reader ! Message(s"$this's $s rose!") } else if (boost < 0) { @@ -81,6 +82,10 @@ class Monster(val base : StorageMon) { elements.foldLeft(1.0)((m, e) => m * (element --> e)) } + def isLocked(implicit reader: SignalConsumer, rng: Random): Boolean = { + statuses.exists(_.onLockMove(this).isDefined) + } + private def computeStat(s : Stat) : Int = { val num = 2 * base.form.baseStats(s) + base.gene.ivs(s) + base.evs(s) / 4 val frac = num * level / 100 @@ -93,8 +98,6 @@ class Monster(val base : StorageMon) { } object Monster { - final val MaxBoost = 6 - def generate(nickname: String, ot: TrainerID, xp: Int, form: Form, moves: IndexedSeq[Move])(implicit rng: Random) = { new Monster(StorageMon.generate(nickname, ot, xp, form, moves)) } diff --git a/FakeMon/src/fmon/stat/Move.scala b/FakeMon/src/fmon/stat/Move.scala index bbd8e6a..5657e7a 100644 --- a/FakeMon/src/fmon/stat/Move.scala +++ b/FakeMon/src/fmon/stat/Move.scala @@ -10,6 +10,8 @@ import scala.util.Random import Statistic._ import fmon.battle.msg._ +import fmon._ +import fmon.battle.msg.{Message, SignalConsumer} import fmon.util._ object MoveType extends Enumeration { @@ -53,7 +55,8 @@ abstract class Move extends MoveTurn { val desc: String val category: MoveType val pow: Int - val powCallback: (Monster, Move, Monster) => Int + val powCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int + val damageCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int val prior: Int val accuracy: Int val pp: Int @@ -76,7 +79,7 @@ abstract class Move extends MoveTurn { override def useMove(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = { reader ! Message(s"$user used $name.") if (attackRoll(user, target)) { - if (pow > 0 || powCallback != null) { + if (pow > 0 || powCallback != null || damageCallback != null) { val dmg = damageRoll(user, target) val actualDmg = target.takeDamage(dmg) if (dmg == actualDmg) { @@ -136,21 +139,24 @@ abstract class Move extends MoveTurn { } def damageRoll(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = { - val atkStat = if (category == MoveType.Physical) PAtk else MAtk - val defStat = if (category == MoveType.Physical) PDef else MDef - // TODO : Fixed damage - val actualPow = if (powCallback != null) powCallback(user, this, target) else pow - val baseDmg = (2 * user.level / 5 + 2) * actualPow * user(atkStat) / (target(defStat) * 50) + 2 - val effectiveness = target.effectiveness(element) - val multiplier = effectiveness * critMultiplier(user, target) - if (effectiveness > 1.0) { - reader ! Message("It's super effective!") - } else if (effectiveness < 1.0) { - reader ! Message("It's not very effective.") + if (damageCallback != null) { + damageCallback(user, this, target, reader, rng) + } else { + val atkStat = if (category == MoveType.Physical) PAtk else MAtk + val defStat = if (category == MoveType.Physical) PDef else MDef + val actualPow = if (powCallback != null) powCallback(user, this, target, reader, rng) else pow + val baseDmg = (2 * user.level / 5 + 2) * actualPow * user(atkStat) / (target(defStat) * 50) + 2 + val effectiveness = target.effectiveness(element) + val multiplier = effectiveness * critMultiplier(user, target) * stabMultiplier(user) + if (effectiveness > 1.0) { + reader ! Message("It's super effective!") + } else if (effectiveness < 1.0) { + reader ! Message("It's not very effective.") + } + val maxDmg = (baseDmg * multiplier).toInt + val minDmg = (17 \\ 20) * maxDmg + rng.nextInt(minDmg, maxDmg) } - val maxDmg = (baseDmg * multiplier).toInt - val minDmg = (17 \\ 20) * maxDmg - rng.nextInt(minDmg, maxDmg) } def critMultiplier(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = { @@ -159,7 +165,15 @@ abstract class Move extends MoveTurn { val chance = (1 << stage) \\ 16 // Doubles per stage if (rng.chance(chance)) { reader ! Message("A critical hit!") - 1.5 + Config.crit + } else { + 1.0 + } + } + + def stabMultiplier(user: Monster) = { + if (user.elements.contains(element)) { + Config.stab } else { 1.0 } @@ -198,6 +212,7 @@ case class MoveToken( @JsonScalaEnumeration(classOf[MoveTypeType]) val category: MoveType, val basePower: Option[Int], val basePowerCallback: String, + val damage: String, val priority: Int, val accuracy: Option[Int], val pp: Int, @@ -222,6 +237,7 @@ case class MoveToken( val category = token.category val pow = token.basePower.getOrElse(0) val powCallback = Move.compilePowCallback(token.basePowerCallback) + val damageCallback = Move.compileDamageCallback(token.damage) val prior = token.priority val pp = token.pp val element = Element(token.`type`) @@ -250,14 +266,17 @@ object Move { moves(name) } - def compilePowCallback(code: String): (Monster, Move, Monster) => Int = { + def compilePowCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = { if (code != null) { val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() val tree = tb.parse( s""" + |import scala.util.Random + |import fmon._ + |import fmon.battle.msg._ |import fmon.stat._ |import fmon.stat.Statistic._ - |def callback(user : Monster, move: Move, target : Monster): Int = { + |def callback(user : Monster, move: Move, target : Monster, reader: SignalConsumer, rng: Random): Int = { | $code |} |callback _ @@ -265,10 +284,14 @@ object Move { val f = tb.compile(tree) val wrapper = f() - wrapper.asInstanceOf[(Monster, Move, Monster) => Int] + wrapper.asInstanceOf[(Monster, Move, Monster, SignalConsumer, Random) => Int] } else { null } } + + def compileDamageCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = { + compilePowCallback(code) + } } \ No newline at end of file diff --git a/FakeMon/src/fmon/stat/Nature.scala b/FakeMon/src/fmon/stat/Nature.scala index 0c12690..f172626 100644 --- a/FakeMon/src/fmon/stat/Nature.scala +++ b/FakeMon/src/fmon/stat/Nature.scala @@ -2,6 +2,8 @@ package fmon.stat import scala.util.Random +import fmon._ + import Statistic._ object Nature extends Enumeration { diff --git a/FakeMon/src/fmon/stat/Party.scala b/FakeMon/src/fmon/stat/Party.scala index 6bd1911..2c1eed3 100644 --- a/FakeMon/src/fmon/stat/Party.scala +++ b/FakeMon/src/fmon/stat/Party.scala @@ -2,16 +2,35 @@ package fmon.stat import scala.util.Random +import fmon._ import fmon.battle.Action +import fmon.battle.msg.SignalConsumer import fmon.stat.Target._ class Party(val trainer: TrainerID, val lead: MonsterPtr, var sideboard: IndexedSeq[Monster]) { - def pollAction(them: Party)(implicit rng: Random): Action = { - if (hasBackup && shouldSwitch(them)) { + def pollAction(them: Party)(implicit reader: SignalConsumer, rng: Random): Action = { + if (lead.isLocked) { + val moveNames = lead.statuses.flatMap(s => s.onLockMove(lead)).distinct + moveNames match { + case Seq(moveName) => { + // Use locked move + val move = Move(moveName) + // TODO : remember target + val target = pollTarget(move, lead, them) + Action(lead, move, target) + } + case _ => { + // Struggle + val move = Move("struggle") + val target = pollTarget(move, lead, them) + Action(lead, move, target) + } + } + } else if (hasBackup && shouldSwitch(them)) { val sub = pollReplacement(them: Party) val move = new SwitchOut(this, sub) Action(lead, move, lead) - } else { + } else{ val move = pollMove val target = pollTarget(move, lead, them) Action(lead, move, target) diff --git a/FakeMon/src/fmon/stat/Status.scala b/FakeMon/src/fmon/stat/Status.scala index a29251d..1d89166 100644 --- a/FakeMon/src/fmon/stat/Status.scala +++ b/FakeMon/src/fmon/stat/Status.scala @@ -7,6 +7,7 @@ import scala.util.Random import scala.io.Source +import fmon._ import fmon.battle.msg._ import fmon.util._ import EffectType.Volatile @@ -58,6 +59,7 @@ class Status(template: StatusTemplate) extends Effect { def onResidualOrder = template.onResidualOrder def onResidual(mon: Monster)(implicit reader: SignalConsumer, rng: Random) = template.onResidual(this, mon, reader, rng) // val onSwitchIn + def onLockMove(mon: Monster)(implicit reader: SignalConsumer, rng: Random) = template.onLockMove(this, mon, reader, rng) val intData: MutMap[String, Int] = MutMap[String, Int]() val stringData = MutMap[String, String]() @@ -79,6 +81,7 @@ abstract class StatusTemplate { val onResidualOrder: Int val onResidual: (Status, Monster, SignalConsumer, Random) => Unit // val onSwitchIn + val onLockMove: (Status, Monster, SignalConsumer, Random) => Option[String] def build = new Status(this) @@ -93,7 +96,8 @@ case class StatusToken( val onBeforeMove: String, val onModifyStat: String, val onResidualOrder: Int, - val onResidual: String) { + val onResidual: String, + val onLockMove: String) { def instantiate() = { val self = this new StatusTemplate { @@ -105,6 +109,7 @@ case class StatusToken( val onModifyStat = Status.compileOnModifyStat(self.onModifyStat) val onResidualOrder = self.onResidualOrder val onResidual = Status.compileOnResidual(self.onResidual) + val onLockMove = Status.compileOnLockMove(self.onLockMove) } } } @@ -124,12 +129,15 @@ object Status { private val header = """ import scala.util.Random import fmon.battle.msg._ + import fmon._ import fmon.stat._ import fmon.stat.MoveType._ import fmon.stat.Statistic._ - import fmon.util._ + import fmon.util.Fraction """ private val helpers = """ + implicit val dice = rng + implicit val consumer = reader def msg(text: String): Unit = { reader ! Message(text) } @@ -230,4 +238,25 @@ object Status { wrapper.asInstanceOf[(Status, Monster, SignalConsumer, Random) => Unit] } } + + def compileOnLockMove(code: String): (Status, Monster, SignalConsumer, Random) => Option[String] = { + if (code == null) { + (_, _, _, _) => None + } else { + val tree = tb.parse( + s""" + |$header + |def onLockMove(self: Status, mon: Monster, reader: SignalConsumer, rng: Random) = { + | $helpers + | val result = {$code} + | Some(result) + |} + |onLockMove _ + """.stripMargin) + val f = tb.compile(tree) + val wrapper = f() + + wrapper.asInstanceOf[(Status, Monster, SignalConsumer, Random) => Option[String]] + } + } } \ No newline at end of file diff --git a/FakeMon/src/fmon/stat/StorageMon.scala b/FakeMon/src/fmon/stat/StorageMon.scala index 3397b5e..5553b80 100644 --- a/FakeMon/src/fmon/stat/StorageMon.scala +++ b/FakeMon/src/fmon/stat/StorageMon.scala @@ -2,7 +2,7 @@ package fmon.stat import scala.util.Random -import fmon.util._ +import fmon._ class StorageMon(val nickname : String, val xp: Int, val gene : Gene, val form : Form, val evs : Map[Stat, Int], val moves : IndexedSeq[Move], val ability: Ability) { def level = form.xpCurve(xp) diff --git a/FakeMon/src/fmon/stat/data/moves.yaml b/FakeMon/src/fmon/stat/data/moves.yaml index 085a8c2..8b1f6b7 100644 --- a/FakeMon/src/fmon/stat/data/moves.yaml +++ b/FakeMon/src/fmon/stat/data/moves.yaml @@ -462,6 +462,29 @@ bite: target: Normal type: Dark zMovePower: 120 +blastburn: + accuracy: 90 + basePower: 150 + category: Special + contestType: Beautiful + desc: If this move is successful, the user must recharge on the following turn and + cannot make a move. + flags: + mirror: 1 + protect: 1 + recharge: 1 + id: blastburn + name: Blast Burn + num: 307 + pp: 5 + priority: 0 + secondary: null + self: + volatileStatus: mustrecharge + shortDesc: User cannot move next turn. + target: Normal + type: Fire + zMovePower: 200 blazekick: accuracy: 90 basePower: 85 @@ -1621,6 +1644,25 @@ dragonpulse: target: Any type: Dragon zMovePower: 160 +dragonrage: + accuracy: 100 + basePower: 0 + category: Special + contestType: Cool + damage: 40 + flags: + mirror: 1 + protect: 1 + id: dragonrage + name: Dragon Rage + num: 82 + pp: 10 + priority: 0 + secondary: null + shortDesc: Deals 40 HP of damage to the target. + target: Normal + type: Dragon + zMovePower: 100 dragonrush: accuracy: 75 basePower: 100 @@ -2021,6 +2063,34 @@ extremespeed: target: Normal type: Normal zMovePower: 160 +facade: + accuracy: 100 + basePower: 70 + category: Physical + contestType: Cute + desc: Power doubles if the user is burned, paralyzed, or poisoned. The physical + damage halving effect from the user's burn is ignored. + flags: + contact: 1 + mirror: 1 + protect: 1 + id: facade + isViable: true + name: Facade + num: 263 + onBasePower: |- + if (user.status != null && user.status != Status("slp")) { + move.pow * 2 + } else { + move.pow + } + pp: 20 + priority: 0 + secondary: null + shortDesc: Power doubles if user is burn/poison/paralyzed. + target: Normal + type: Normal + zMovePower: 140 fairywind: accuracy: 100 basePower: 40 @@ -2481,6 +2551,30 @@ forcepalm: target: Normal type: Fighting zMovePower: 120 +frenzyplant: + accuracy: 90 + basePower: 150 + category: Special + contestType: Cool + desc: If this move is successful, the user must recharge on the following turn and + cannot select a move. + flags: + mirror: 1 + nonsky: 1 + protect: 1 + recharge: 1 + id: frenzyplant + name: Frenzy Plant + num: 338 + pp: 5 + priority: 0 + secondary: null + self: + volatileStatus: mustrecharge + shortDesc: User cannot move next turn. + target: Normal + type: Grass + zMovePower: 200 frostbreath: accuracy: 90 basePower: 60 @@ -2527,6 +2621,30 @@ gigadrain: target: Normal type: Grass zMovePower: 140 +gigaimpact: + accuracy: 90 + basePower: 150 + category: Physical + contestType: Tough + desc: If this move is successful, the user must recharge on the following turn and + cannot select a move. + flags: + contact: 1 + mirror: 1 + protect: 1 + recharge: 1 + id: gigaimpact + name: Giga Impact + num: 416 + pp: 5 + priority: 0 + secondary: null + self: + volatileStatus: mustrecharge + shortDesc: User cannot move next turn. + target: Normal + type: Normal + zMovePower: 200 glaciate: accuracy: 95 basePower: 65 @@ -2953,6 +3071,29 @@ howl: type: Normal zMoveBoost: PAtk: 1 +hydrocannon: + accuracy: 90 + basePower: 150 + category: Special + contestType: Beautiful + desc: If this move is successful, the user must recharge on the following turn and + cannot select a move. + flags: + mirror: 1 + protect: 1 + recharge: 1 + id: hydrocannon + name: Hydro Cannon + num: 308 + pp: 5 + priority: 0 + secondary: null + self: + volatileStatus: mustrecharge + shortDesc: User cannot move next turn. + target: Normal + type: Water + zMovePower: 200 hydropump: accuracy: 80 basePower: 110 @@ -2972,6 +3113,29 @@ hydropump: target: Normal type: Water zMovePower: 185 +hyperbeam: + accuracy: 90 + basePower: 150 + category: Special + contestType: Cool + desc: If this move is successful, the user must recharge on the following turn and + cannot select a move. + flags: + mirror: 1 + protect: 1 + recharge: 1 + id: hyperbeam + name: Hyper Beam + num: 63 + pp: 5 + priority: 0 + secondary: null + self: + volatileStatus: mustrecharge + shortDesc: User cannot move next turn. + target: Normal + type: Normal + zMovePower: 200 hyperfang: accuracy: 90 basePower: 80 @@ -4081,6 +4245,28 @@ nastyplot: target: Self type: Dark zMoveEffect: clearnegativeboost +naturesmadness: + accuracy: 90 + basePower: 0 + category: Special + contestType: Tough + damage: Math.max(target.hp / 2, 1); + desc: Deals damage to the target equal to half of its current HP, rounded down, + but not less than 1 HP. + flags: + mirror: 1 + protect: 1 + id: naturesmadness + isViable: true + name: Nature's Madness + num: 717 + pp: 10 + priority: 0 + secondary: null + shortDesc: Does damage equal to 1/2 target's current HP. + target: Normal + type: Fairy + zMovePower: 100 needlearm: accuracy: 100 basePower: 60 @@ -4125,6 +4311,27 @@ nightdaze: target: Normal type: Dark zMovePower: 160 +nightshade: + accuracy: 100 + basePower: 0 + category: Special + contestType: Clever + damage: user.level + desc: Deals damage to the target equal to the user's level. + flags: + mirror: 1 + protect: 1 + id: nightshade + isViable: true + name: Night Shade + num: 101 + pp: 15 + priority: 0 + secondary: null + shortDesc: Does damage equal to the user's level. + target: Normal + type: Ghost + zMovePower: 100 nightslash: accuracy: 100 basePower: 70 @@ -4711,6 +4918,29 @@ precipiceblades: target: AllAdjacentFoes type: Ground zMovePower: 190 +prismaticlaser: + accuracy: 100 + basePower: 160 + category: Special + contestType: Cool + desc: If this move is successful, the user must recharge on the following turn and + cannot select a move. + flags: + mirror: 1 + protect: 1 + recharge: 1 + id: prismaticlaser + name: Prismatic Laser + num: 711 + pp: 10 + priority: 0 + secondary: null + self: + volatileStatus: mustrecharge + shortDesc: User cannot move next turn. + target: Normal + type: Psychic + zMovePower: 200 psybeam: accuracy: 100 basePower: 65 @@ -4799,7 +5029,27 @@ psychocut: target: Normal type: Psychic zMovePower: 140 - +psywave: + accuracy: 100 + basePower: 0 + category: Special + contestType: Clever + damage: rng.nextInt(50, 151) * user.level / 100 + desc: Deals damage to the target equal to (user's level) * (X + 50) / 100, where + X is a random number from 0 to 100, rounded down, but not less than 1 HP. + flags: + mirror: 1 + protect: 1 + id: psywave + name: Psywave + num: 149 + pp: 15 + priority: 0 + secondary: null + shortDesc: Random damage equal to 0.5x-1.5x user's level. + target: Normal + type: Psychic + zMovePower: 100 quickattack: accuracy: 100 basePower: 40 @@ -4889,6 +5139,48 @@ razorshell: target: Normal type: Water zMovePower: 140 +recharge: + accuracy: 100 + basePower: 0 + category: Support + contestType: Tough + flags: + contact: 1 + mirror: 1 + protect: 1 + id: recharge + name: Recharge + num: 10 + pp: 40 + priority: 0 + secondary: null + shortDesc: Placeholder move for when recharging. + target: Normal + type: Normal + zMovePower: 100 +roaroftime: + accuracy: 90 + basePower: 150 + category: Special + contestType: Beautiful + desc: If this move is successful, the user must recharge on the following turn and + cannot select a move. + flags: + mirror: 1 + protect: 1 + recharge: 1 + id: roaroftime + name: Roar of Time + num: 459 + pp: 5 + priority: 0 + secondary: null + self: + volatileStatus: mustrecharge + shortDesc: User cannot move next turn. + target: Normal + type: Dragon + zMovePower: 200 rockclimb: accuracy: 85 basePower: 90 @@ -5017,6 +5309,30 @@ rocktomb: target: Normal type: Rock zMovePower: 120 +rockwrecker: + accuracy: 90 + basePower: 150 + category: Physical + contestType: Tough + desc: If this move is successful, the user must recharge on the following turn and + cannot select a move. + flags: + bullet: 1 + mirror: 1 + protect: 1 + recharge: 1 + id: rockwrecker + name: Rock Wrecker + num: 439 + pp: 5 + priority: 0 + secondary: null + self: + volatileStatus: mustrecharge + shortDesc: User cannot move next turn. + target: Normal + type: Rock + zMovePower: 200 rollingkick: accuracy: 85 basePower: 60 @@ -5244,6 +5560,29 @@ seedflare: target: Normal type: Grass zMovePower: 190 +seismictoss: + accuracy: 100 + basePower: 0 + category: Physical + contestType: Tough + damage: user.level + desc: Deals damage to the target equal to the user's level. + flags: + contact: 1 + mirror: 1 + nonsky: 1 + protect: 1 + id: seismictoss + isViable: true + name: Seismic Toss + num: 69 + pp: 20 + priority: 0 + secondary: null + shortDesc: Does damage equal to the user's level. + target: Normal + type: Fighting + zMovePower: 100 shadowball: accuracy: 100 basePower: 80 @@ -5803,6 +6142,26 @@ snarl: target: AllAdjacentFoes type: Dark zMovePower: 100 +sonicboom: + accuracy: 90 + basePower: 0 + category: Special + contestType: Cool + damage: 20 + desc: Deals 20 HP of damage to the target. + flags: + mirror: 1 + protect: 1 + id: sonicboom + name: Sonic Boom + num: 49 + pp: 20 + priority: 0 + secondary: null + shortDesc: Always does 20 HP of damage. + target: Normal + type: Normal + zMovePower: 100 spacialrend: accuracy: 95 basePower: 100 @@ -6140,6 +6499,30 @@ submission: target: Normal type: Fighting zMovePower: 160 +superfang: + accuracy: 90 + basePower: 0 + category: Physical + contestType: Tough + damage: Math.max(target.hp / 2, 1); + desc: Deals damage to the target equal to half of its current HP, rounded down, + but not less than 1 HP. + flags: + contact: 1 + mirror: 1 + protect: 1 + id: superfang + isViable: true + name: Super Fang + num: 162 + pp: 10 + priority: 0 + secondary: null + shortDesc: Does damage equal to 1/2 target's current HP. + target: Normal + type: Normal + zMovePower: 100 + superpower: accuracy: 100 basePower: 120 @@ -6763,6 +7146,34 @@ vitalthrow: target: Normal type: Fighting zMovePower: 140 +volttackle: + accuracy: 100 + basePower: 120 + category: Physical + contestType: Cool + desc: Has a 10% chance to paralyze the target. If the target lost HP, the user takes + recoil damage equal to 33% the HP lost by the target, rounded half up, but not + less than 1 HP. + flags: + contact: 1 + mirror: 1 + protect: 1 + id: volttackle + isViable: true + name: Volt Tackle + num: 344 + pp: 15 + priority: 0 + recoil: + denom: 100 + num: 33 + secondary: + chance: 10 + status: par + shortDesc: Has 33% recoil. 10% chance to paralyze target. + target: Normal + type: Electric + zMovePower: 190 waterfall: accuracy: 100 basePower: 80 @@ -6985,6 +7396,26 @@ workup: type: Normal zMoveBoost: PAtk: 1 +xscissor: + accuracy: 100 + basePower: 80 + category: Physical + contestType: Cool + flags: + contact: 1 + mirror: 1 + protect: 1 + id: xscissor + isViable: true + name: X-Scissor + num: 404 + pp: 15 + priority: 0 + secondary: null + shortDesc: No additional effect. + target: Normal + type: Bug + zMovePower: 160 zapcannon: accuracy: 50 basePower: 120 diff --git a/FakeMon/src/fmon/stat/data/statuses.yaml b/FakeMon/src/fmon/stat/data/statuses.yaml index 346e28c..e888a39 100644 --- a/FakeMon/src/fmon/stat/data/statuses.yaml +++ b/FakeMon/src/fmon/stat/data/statuses.yaml @@ -41,16 +41,6 @@ confusion: 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: msg(s"${mon} snapped out of its confusion.") onStart: |- @@ -95,11 +85,6 @@ slp: msg(s"${mon} woke up!") onBeforeMovePriority: 10 onBeforeMove: | - /* - if (mon.hasAbility('earlybird')) { - this.intData.time--; - } - */ self.intData("time") -= 1; if (self.intData("time") <= 0) { mon.cureStatus(); @@ -148,6 +133,21 @@ frz: if (move.flags("defrost")) { mon.cureStatus() } + +mustrecharge: + duration: 2 + id: mustrecharge + name: mustrecharge + num: 0 + onBeforeMove: |- + msg(s"${mon} must recharge!") + mon -= self + //mon.removeVolatile('mustrecharge'); + //mon.removeVolatile('truant'); + false + onBeforeMovePriority: 11 + onLockMove: | + "recharge" psn: name: 'psn' diff --git a/FakeMon/src/fmon/stat/package.scala b/FakeMon/src/fmon/stat/package.scala index be5b307..12d82b1 100644 --- a/FakeMon/src/fmon/stat/package.scala +++ b/FakeMon/src/fmon/stat/package.scala @@ -15,7 +15,6 @@ package object stat { type Target = Target.Value type XpCurve = XpCurve.Val - implicit def rngDice(rng : Random) = new Dice(rng) - implicit def ptrToMonster(ptr : MonsterPtr) : Monster = ptr.mon implicit def templateToStatus(status: StatusTemplate): Status = status.build + } \ No newline at end of file diff --git a/FakeMon/src/fmon/util/package.scala b/FakeMon/src/fmon/util/package.scala index ddf6cd9..c50503e 100644 --- a/FakeMon/src/fmon/util/package.scala +++ b/FakeMon/src/fmon/util/package.scala @@ -42,5 +42,4 @@ package object util { new BufferedInputStream(new FileInputStream(filename)) } - implicit def int2Helper(x : Int) = new IntFraction(x) } \ No newline at end of file