Merged some conflicts with the new damage animations

This commit is contained in:
James Daly 2019-06-17 22:23:22 -04:00
commit c244d36bd6
19 changed files with 609 additions and 80 deletions

View File

@ -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
}

View File

@ -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()
}

View File

@ -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)

View File

@ -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

View File

@ -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._

View File

@ -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)
}

View File

@ -0,0 +1,5 @@
title: FakeMon Engine Demo
crit: 1.5
stab: 1.5
maxBoost: 6
newday: 02:00

View File

@ -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
}

View File

@ -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

View File

@ -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))
}

View File

@ -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)
}
}

View File

@ -2,6 +2,8 @@ package fmon.stat
import scala.util.Random
import fmon._
import Statistic._
object Nature extends Enumeration {

View File

@ -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)

View File

@ -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]]
}
}
}

View File

@ -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)

View File

@ -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

View File

@ -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'

View File

@ -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
}

View File

@ -42,5 +42,4 @@ package object util {
new BufferedInputStream(new FileInputStream(filename))
}
implicit def int2Helper(x : Int) = new IntFraction(x)
}