Added the first charge-move, razorwind. Future moves incoming. Note that since there are no items or weather, insta-charge is not yet implemented

This commit is contained in:
dalyjame 2019-06-18 16:48:54 -04:00
parent c244d36bd6
commit e008892c13
6 changed files with 147 additions and 47 deletions

View File

@ -30,7 +30,7 @@ class Game extends Application {
val form1 = Form("Diabolo") val form1 = Form("Diabolo")
val form2 = Form("Chanilla") val form2 = Form("Chanilla")
val movepool1 = IndexedSeq(Move("eruption"), Move("willowisp"), Move("thunderbolt"), Move("thunderwave"), Move("blastburn")) 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 p1 = TrainerID("Jaeda", Gender.Female, 0)
val p2 = TrainerID("Wild Monster", Gender.Male, 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 party1 = new Party(p1, new MonsterPtr(Monster.generate("Allied Mon", p1, 500, form1, movepool1)), IndexedSeq())

View File

@ -24,6 +24,7 @@ abstract class Ability extends Effect {
class AbilityToken( class AbilityToken(
val name: String, val name: String,
val id: String,
val shortDesc: String, val shortDesc: String,
val onAfterDamage: String val onAfterDamage: String
) { ) {
@ -32,6 +33,7 @@ class AbilityToken(
val self = this val self = this
new Ability { new Ability {
val name = self.name val name = self.name
val id = self.id
val desc = self.shortDesc val desc = self.shortDesc
val onAfterDamageImp = Ability.compileOnAfterDamage(self.onAfterDamage) val onAfterDamageImp = Ability.compileOnAfterDamage(self.onAfterDamage)
} }

View File

@ -36,6 +36,7 @@ trait MoveTurn extends Effect {
class SwitchOut(val party: Party, val replacement: Monster) extends MoveTurn { class SwitchOut(val party: Party, val replacement: Monster) extends MoveTurn {
def name = "Swap" def name = "Swap"
def id = "swap"
def flags = Set() def flags = Set()
def prior = +6 def prior = +6
def target = Target.Self def target = Target.Self
@ -52,11 +53,13 @@ class SwitchOut(val party: Party, val replacement: Monster) extends MoveTurn {
abstract class Move extends MoveTurn { abstract class Move extends MoveTurn {
val name: String val name: String
val id: String
val desc: String val desc: String
val category: MoveType val category: MoveType
val pow: Int val pow: Int
val powCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int val powCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int
val damageCallback: (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 prior: Int
val accuracy: Int val accuracy: Int
val pp: 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) = { override def useMove(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = {
reader ! Message(s"$user used $name.") reader ! Message(s"$user used $name.")
if (attackRoll(user, target)) { if (onTryMove(user, this, target, reader, rng)) {
if (pow > 0 || powCallback != null || damageCallback != null) { if (attackRoll(user, target)) {
val dmg = damageRoll(user, target) if (pow > 0 || powCallback != null || damageCallback != null) {
val actualDmg = target.takeDamage(dmg) val dmg = damageRoll(user, target)
if (dmg == actualDmg) { val actualDmg = target.takeDamage(dmg)
reader ! Message(s"$target takes $actualDmg damage!") if (dmg == actualDmg) {
reader ! DamageMsg(dmg, target, element) reader ! Message(s"$target takes $actualDmg damage!")
} else { reader ! DamageMsg(dmg, target, element)
reader ! Message(s"$target takes $actualDmg (+${dmg - actualDmg} overkill) damage!") } else {
reader ! DamageMsg(dmg, target, element) 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) { applyBoosts(target, boosts, user)
val healing = Math.max(drain * actualDmg, 1) applyStatus(target, user, status)
user.recoverDamage(healing) if (effect != null) {
reader ! Message(s"$user recovered $healing damage.") applyEffect(target, user, effect)
} else if (drain < 0.frac) { }
val recoil = Math.max(-drain * actualDmg, 1) if (selfEffect != null) {
user.takeDamage(recoil) applyEffect(user, user, selfEffect)
reader ! Message(s"$user took $recoil damage from recoil!") }
reader ! DamageMsg(actualDmg, target, Element("None")) if (secondary != null && rng.chance(secondary.chance.%%)) {
applyEffect(target, user, secondary)
} }
target.base.ability.onAfterDamage(user, this, target, actualDmg) // TODO : Multiparty
} else {
if (!user.isAlive) { reader ! Message("Missed!")
reader ! Message(s"$user fainted!")
}
if (!target.isAlive) {
reader ! Message(s"$target fainted!")
}
} }
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) = { def attackRoll(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = {
@ -208,10 +214,12 @@ abstract class Move extends MoveTurn {
case class MoveToken( case class MoveToken(
val name: String, val name: String,
val id: String,
val shortDesc: String, val shortDesc: String,
@JsonScalaEnumeration(classOf[MoveTypeType]) val category: MoveType, @JsonScalaEnumeration(classOf[MoveTypeType]) val category: MoveType,
val basePower: Option[Int], val basePower: Option[Int],
val basePowerCallback: String, val basePowerCallback: String,
val onTryMove: String,
val damage: String, val damage: String,
val priority: Int, val priority: Int,
val accuracy: Option[Int], val accuracy: Option[Int],
@ -233,11 +241,13 @@ case class MoveToken(
val effectToken = SecondaryToken(100, Map(), null, token.volatileStatus, null) val effectToken = SecondaryToken(100, Map(), null, token.volatileStatus, null)
new Move { new Move {
val name = token.name val name = token.name
val id = token.id
val desc = token.shortDesc val desc = token.shortDesc
val category = token.category val category = token.category
val pow = token.basePower.getOrElse(0) val pow = token.basePower.getOrElse(0)
val powCallback = Move.compilePowCallback(token.basePowerCallback) val powCallback = Move.compilePowCallback(token.basePowerCallback)
val damageCallback = Move.compileDamageCallback(token.damage) val damageCallback = Move.compileDamageCallback(token.damage)
val onTryMove = Move.compileOnTryMove(token.onTryMove)
val prior = token.priority val prior = token.priority
val pp = token.pp val pp = token.pp
val element = Element(token.`type`) val element = Element(token.`type`)
@ -266,6 +276,14 @@ object Move {
moves(name) 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 = { def compilePowCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = {
if (code != null) { if (code != null) {
val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
@ -294,4 +312,29 @@ object Move {
def compileDamageCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = { def compileDamageCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = {
compilePowCallback(code) 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]
}
}
} }

View File

@ -29,6 +29,7 @@ object EffectType extends Enumeration {
trait Effect { trait Effect {
def name: String def name: String
def id: String
def effectType: EffectType def effectType: EffectType
def isItem = effectType == EffectType.ItemEffect def isItem = effectType == EffectType.ItemEffect
@ -37,6 +38,9 @@ trait Effect {
} }
case class EffectSource(mon: Monster, effect: Effect) { case class EffectSource(mon: Monster, effect: Effect) {
def name = effect.name
def id = effect.id
def isItem = effect.isItem def isItem = effect.isItem
def isMove = effect.isMove def isMove = effect.isMove
def isAbility = effect.isAbility def isAbility = effect.isAbility
@ -47,7 +51,7 @@ case class EffectSource(mon: Monster, effect: Effect) {
class Status(template: StatusTemplate) extends Effect { class Status(template: StatusTemplate) extends Effect {
def name = template.name def name = template.name
// val id def id = template.id
def effectType = template.effectType def effectType = template.effectType
def onStart(mon: Monster, source: EffectSource)(implicit reader: SignalConsumer, rng: Random) = template.onStart(this, mon, source, reader, rng) 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) 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) def onLockMove(mon: Monster)(implicit reader: SignalConsumer, rng: Random) = template.onLockMove(this, mon, reader, rng)
val intData: MutMap[String, Int] = MutMap[String, Int]() val intData: MutMap[String, Int] = MutMap[String, Int]()
val stringData = MutMap[String, String]() val strData = MutMap[String, String]()
override def toString = name override def toString = name
} }
abstract class StatusTemplate { abstract class StatusTemplate {
val name: String val name: String
// val id val id: String
val effectType: EffectType val effectType: EffectType
val onStart: (Status, Monster, EffectSource, SignalConsumer, Random) => Unit val onStart: (Status, Monster, EffectSource, SignalConsumer, Random) => Unit
val onEnd: (Status, Monster, SignalConsumer, Random) => Unit val onEnd: (Status, Monster, SignalConsumer, Random) => Unit
@ -90,6 +94,7 @@ abstract class StatusTemplate {
case class StatusToken( case class StatusToken(
val name: String, val name: String,
val id: String,
val effectType: String, val effectType: String,
val onStart: String, val onStart: String,
val onEnd: String, val onEnd: String,
@ -102,6 +107,7 @@ case class StatusToken(
val self = this val self = this
new StatusTemplate { new StatusTemplate {
val name = self.name val name = self.name
val id = self.id
val effectType = EffectType.parse(self.effectType, Volatile) val effectType = EffectType.parse(self.effectType, Volatile)
val onStart = Status.compileOnStart(self.onStart) val onStart = Status.compileOnStart(self.onStart)
val onEnd = Status.compileOnEnd(self.onEnd) val onEnd = Status.compileOnEnd(self.onEnd)

View File

@ -5139,6 +5139,42 @@ razorshell:
target: Normal target: Normal
type: Water type: Water
zMovePower: 140 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: recharge:
accuracy: 100 accuracy: 100
basePower: 0 basePower: 0

View File

@ -180,3 +180,16 @@ tox:
} }
mon.takeDamage(self.intData("stage") \\ 16 * mon(Hp)); mon.takeDamage(self.intData("stage") \\ 16 * mon(Hp));
msg(s"${mon} was damaged by poison!") 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