diff --git a/FakeMon/src/fmon/Game.scala b/FakeMon/src/fmon/Game.scala index 7f25f75..3b72fbd 100644 --- a/FakeMon/src/fmon/Game.scala +++ b/FakeMon/src/fmon/Game.scala @@ -30,7 +30,7 @@ class Game extends Application { val form1 = Form("Diabolo") val form2 = Form("Chanilla") val movepool1 = IndexedSeq(Move("eruption"), Move("willowisp"), Move("thunderbolt"), Move("thunderwave"), Move("blastburn")) - val movepool2 = IndexedSeq(Move("blastburn")) + val movepool2 = IndexedSeq(Move("razorwind")) 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()) diff --git a/FakeMon/src/fmon/stat/Ability.scala b/FakeMon/src/fmon/stat/Ability.scala index 45793b6..3a1867f 100644 --- a/FakeMon/src/fmon/stat/Ability.scala +++ b/FakeMon/src/fmon/stat/Ability.scala @@ -24,6 +24,7 @@ abstract class Ability extends Effect { class AbilityToken( val name: String, + val id: String, val shortDesc: String, val onAfterDamage: String ) { @@ -32,6 +33,7 @@ class AbilityToken( val self = this new Ability { val name = self.name + val id = self.id val desc = self.shortDesc val onAfterDamageImp = Ability.compileOnAfterDamage(self.onAfterDamage) } diff --git a/FakeMon/src/fmon/stat/Move.scala b/FakeMon/src/fmon/stat/Move.scala index 5657e7a..efaea41 100644 --- a/FakeMon/src/fmon/stat/Move.scala +++ b/FakeMon/src/fmon/stat/Move.scala @@ -36,6 +36,7 @@ trait MoveTurn extends Effect { class SwitchOut(val party: Party, val replacement: Monster) extends MoveTurn { def name = "Swap" + def id = "swap" def flags = Set() def prior = +6 def target = Target.Self @@ -52,11 +53,13 @@ class SwitchOut(val party: Party, val replacement: Monster) extends MoveTurn { abstract class Move extends MoveTurn { val name: String + val id: String val desc: String val category: MoveType val pow: Int val powCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int val damageCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int + val onTryMove: (Monster, Move, Monster, SignalConsumer, Random) => Boolean val prior: Int val accuracy: Int val pp: Int @@ -78,53 +81,56 @@ 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 || damageCallback != null) { - val dmg = damageRoll(user, target) - val actualDmg = target.takeDamage(dmg) - if (dmg == actualDmg) { - reader ! Message(s"$target takes $actualDmg damage!") - reader ! DamageMsg(dmg, target, element) - } else { - reader ! Message(s"$target takes $actualDmg (+${dmg - actualDmg} overkill) damage!") - reader ! DamageMsg(dmg, target, element) + if (onTryMove(user, this, target, reader, rng)) { + if (attackRoll(user, target)) { + if (pow > 0 || powCallback != null || damageCallback != null) { + val dmg = damageRoll(user, target) + val actualDmg = target.takeDamage(dmg) + if (dmg == actualDmg) { + reader ! Message(s"$target takes $actualDmg damage!") + reader ! DamageMsg(dmg, target, element) + } else { + reader ! Message(s"$target takes $actualDmg (+${dmg - actualDmg} overkill) damage!") + reader ! DamageMsg(dmg, target, element) + } + if (drain > 0.frac) { + val healing = Math.max(drain * actualDmg, 1) + user.recoverDamage(healing) + reader ! Message(s"$user recovered $healing damage.") + } else if (drain < 0.frac) { + val recoil = Math.max(-drain * actualDmg, 1) + user.takeDamage(recoil) + reader ! Message(s"$user took $recoil damage from recoil!") + reader ! DamageMsg(actualDmg, target, Element("None")) + } + + target.base.ability.onAfterDamage(user, this, target, actualDmg) + + if (!user.isAlive) { + reader ! Message(s"$user fainted!") + } + if (!target.isAlive) { + reader ! Message(s"$target fainted!") + } } - if (drain > 0.frac) { - val healing = Math.max(drain * actualDmg, 1) - user.recoverDamage(healing) - reader ! Message(s"$user recovered $healing damage.") - } else if (drain < 0.frac) { - val recoil = Math.max(-drain * actualDmg, 1) - user.takeDamage(recoil) - reader ! Message(s"$user took $recoil damage from recoil!") - reader ! DamageMsg(actualDmg, target, Element("None")) + applyBoosts(target, boosts, user) + applyStatus(target, user, status) + if (effect != null) { + applyEffect(target, user, effect) } - - target.base.ability.onAfterDamage(user, this, target, actualDmg) - - if (!user.isAlive) { - reader ! Message(s"$user fainted!") + if (selfEffect != null) { + applyEffect(user, user, selfEffect) } - if (!target.isAlive) { - reader ! Message(s"$target fainted!") + if (secondary != null && rng.chance(secondary.chance.%%)) { + applyEffect(target, user, secondary) } + + // TODO : Multiparty + } else { + reader ! Message("Missed!") } - applyBoosts(target, boosts, user) - applyStatus(target, user, status) - if (effect != null) { - applyEffect(target, user, effect) - } - if (selfEffect != null) { - applyEffect(user, user, selfEffect) - } - if (secondary != null && rng.chance(secondary.chance.%%)) { - applyEffect(target, user, secondary) - } - - // TODO : Multiparty - } else { - reader ! Message("Missed!") } + } def attackRoll(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = { @@ -208,10 +214,12 @@ abstract class Move extends MoveTurn { case class MoveToken( val name: String, + val id: String, val shortDesc: String, @JsonScalaEnumeration(classOf[MoveTypeType]) val category: MoveType, val basePower: Option[Int], val basePowerCallback: String, + val onTryMove: String, val damage: String, val priority: Int, val accuracy: Option[Int], @@ -233,11 +241,13 @@ case class MoveToken( val effectToken = SecondaryToken(100, Map(), null, token.volatileStatus, null) new Move { val name = token.name + val id = token.id val desc = token.shortDesc val category = token.category val pow = token.basePower.getOrElse(0) val powCallback = Move.compilePowCallback(token.basePowerCallback) val damageCallback = Move.compileDamageCallback(token.damage) + val onTryMove = Move.compileOnTryMove(token.onTryMove) val prior = token.priority val pp = token.pp val element = Element(token.`type`) @@ -265,7 +275,15 @@ object Move { } moves(name) } - + + private val helpers = """ + implicit val dice = rng + implicit val consumer = reader + def msg(text: String): Unit = { + reader ! Message(text) + } + """ + def compilePowCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = { if (code != null) { val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() @@ -294,4 +312,29 @@ object Move { def compileDamageCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = { compilePowCallback(code) } + + def compileOnTryMove(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Boolean = { + if (code == null) { + (_, _, _, _, _) => true + } else { + 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, reader: SignalConsumer, rng: Random): Boolean = { + | $helpers + | $code + |} + |callback _ + """.stripMargin) + val f = tb.compile(tree) + val wrapper = f() + + wrapper.asInstanceOf[(Monster, Move, Monster, SignalConsumer, Random) => Boolean] + } + } } \ No newline at end of file diff --git a/FakeMon/src/fmon/stat/Status.scala b/FakeMon/src/fmon/stat/Status.scala index 1d89166..e1c190d 100644 --- a/FakeMon/src/fmon/stat/Status.scala +++ b/FakeMon/src/fmon/stat/Status.scala @@ -29,6 +29,7 @@ object EffectType extends Enumeration { trait Effect { def name: String + def id: String def effectType: EffectType def isItem = effectType == EffectType.ItemEffect @@ -37,6 +38,9 @@ trait Effect { } case class EffectSource(mon: Monster, effect: Effect) { + def name = effect.name + def id = effect.id + def isItem = effect.isItem def isMove = effect.isMove def isAbility = effect.isAbility @@ -47,7 +51,7 @@ case class EffectSource(mon: Monster, effect: Effect) { class Status(template: StatusTemplate) extends Effect { def name = template.name - // val id + def id = template.id def effectType = template.effectType def onStart(mon: Monster, source: EffectSource)(implicit reader: SignalConsumer, rng: Random) = template.onStart(this, mon, source, reader, rng) def onEnd(mon: Monster)(implicit reader: SignalConsumer, rng: Random) = template.onEnd(this, mon, reader, rng) @@ -62,14 +66,14 @@ class Status(template: StatusTemplate) extends Effect { 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]() + val strData = MutMap[String, String]() override def toString = name } abstract class StatusTemplate { val name: String - // val id + val id: String val effectType: EffectType val onStart: (Status, Monster, EffectSource, SignalConsumer, Random) => Unit val onEnd: (Status, Monster, SignalConsumer, Random) => Unit @@ -90,6 +94,7 @@ abstract class StatusTemplate { case class StatusToken( val name: String, + val id: String, val effectType: String, val onStart: String, val onEnd: String, @@ -102,6 +107,7 @@ case class StatusToken( val self = this new StatusTemplate { val name = self.name + val id = self.id val effectType = EffectType.parse(self.effectType, Volatile) val onStart = Status.compileOnStart(self.onStart) val onEnd = Status.compileOnEnd(self.onEnd) diff --git a/FakeMon/src/fmon/stat/data/moves.yaml b/FakeMon/src/fmon/stat/data/moves.yaml index 8b1f6b7..6af1f60 100644 --- a/FakeMon/src/fmon/stat/data/moves.yaml +++ b/FakeMon/src/fmon/stat/data/moves.yaml @@ -5139,6 +5139,42 @@ razorshell: target: Normal type: Water zMovePower: 140 + +razorwind: + accuracy: 100 + basePower: 80 + category: Special + contestType: Cool + critRatio: 2 + desc: Has a higher chance for a critical hit. This attack charges on the first turn + and executes on the second. If the user is holding a Power Herb, the move completes + in one turn. + flags: + charge: 1 + mirror: 1 + protect: 1 + id: razorwind + name: Razor Wind + num: 13 + onTryMove: |- + if (user.statuses.exists(_.id == "twoturnmove")) { + user -= Status("twoturnmove") + true + } else { + msg(s"${user} drew in power!") + /*if (!this.runEvent('ChargeMove', attacker, defender, move)) { + return; + }*/ + user.addStatus(Status("twoturnmove"), EffectSource(user, move)) + false + } + pp: 10 + priority: 0 + secondary: null + shortDesc: Charges, then hits foe(s) turn 2. High crit ratio. + target: AllAdjacentFoes + type: Normal + zMovePower: 160 recharge: accuracy: 100 basePower: 0 diff --git a/FakeMon/src/fmon/stat/data/statuses.yaml b/FakeMon/src/fmon/stat/data/statuses.yaml index e888a39..fad90c3 100644 --- a/FakeMon/src/fmon/stat/data/statuses.yaml +++ b/FakeMon/src/fmon/stat/data/statuses.yaml @@ -162,7 +162,7 @@ psn: onResidual: | mon.takeDamage(mon(Hp) / 8); msg(s"${mon} was damaged by poison!") - + tox: name: 'tox' id: 'tox' @@ -180,3 +180,16 @@ tox: } mon.takeDamage(self.intData("stage") \\ 16 * mon(Hp)); msg(s"${mon} was damaged by poison!") + +twoturnmove: + duration: 2 + id: twoturnmove + name: twoturnmove + num: 0 + onLockMove: self.strData("move") + onStart: |- + self.strData("move") = source.id + //target.addVolatile(effect.id, source) + //this.attrLastMove("[still]") + onMoveAborted: |- + mon -= self \ No newline at end of file