Adding FakeMon project

This commit is contained in:
James Daly 2019-05-23 22:58:31 -04:00
parent 009e82c21f
commit 83e6bb7a3c
28 changed files with 1322 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*/bin/*
*.metadata
*.cache
*.cache-main
*.classpath
*.project
*/.settings/*

View File

@ -0,0 +1,32 @@
package mon
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
import mon.battle.BattleEngine
import mon.stat._
case class Prop(url : Seq[String])
object Game {
def main(args : Array[String]): Unit = {
println(Element("Water").effect)
println(Move.moves)
println(Form.forms)
println(Status("psn"))
implicit val rng = new scala.util.Random()
val form1 = Form("Diabolo")
val form2 = Form("Chanilla")
val movepool1 = IndexedSeq(Move("Poison Sting"))
val movepool2 = IndexedSeq(Move("Poison Sting"))
val party1 = new Party(null, new Monster(new StorageMon("Allied Mon", Gene.randomGene(null, form1), form1, Statistic.emptyEvs, movepool1)), IndexedSeq())
val party2 = new Party(null, new Monster(new StorageMon("Wild Mon", Gene.randomGene(null, form2), form2, Statistic.emptyEvs, movepool2)), IndexedSeq())
val engine = new BattleEngine(party1, party2)
engine.playTurn()
println(party1.lead.elements)
}
}

View File

@ -0,0 +1,14 @@
package mon.battle
import mon.stat._
import mon.stat.Statistic.Speed
case class Action(user : Monster, move : Move, target : Monster) extends Comparable[Action] {
override def compareTo(other : Action) = {
if (move.prior == other.move.prior) {
other.user(Speed) compareTo user(Speed)
} else {
other.move.prior compareTo move.prior
}
}
}

View File

@ -0,0 +1,95 @@
package mon.battle
import scala.util.Random
import mon.stat._
import mon.stat.Statistic._
import mon.util.Fraction
class BattleEngine(val player : Party, val enemy : Party)(implicit val rng : Random) {
def playTurn() = {
val playerMove = player.pollAction
val playerTarget = player.pollTarget(playerMove, player.lead, player, enemy)
val enemyMove = enemy.pollAction
val enemyTarget = enemy.pollTarget(enemyMove, enemy.lead, enemy, player)
val actions = Seq(Action(player.lead, playerMove, playerTarget), Action(enemy.lead, enemyMove, enemyTarget))
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.map(_(mon))))
println(s"${player.lead}(${player.lead.hp}/${player.lead(Hp)})")
println(s"${enemy.lead}(${enemy.lead.hp}/${enemy.lead(Hp)})")
}
def useMove(action : Action) = {
val user = action.user
val move = action.move
val target = action.target
println(s"$user used $move.")
if (attackRoll(user, move, target)) {
if (move.pow > 0 || move.powCallback != null) {
val dmg = damageRoll(user, move, target)
target.takeDamage(dmg)
println(s"$target takes $dmg damage!")
}
applyBoosts(target, move.boosts)
// TODO : Secondary effects
if (move.selfEffect != null) {
applyBoosts(user, move.selfEffect.boosts)
}
if (move.secondary != null && rng.chance(move.secondary.chance.%%)) {
applyBoosts(target, move.secondary.boosts)
if (move.secondary.status != null && target.status == None) {
// apply status
target.status = Some(move.secondary.status)
move.secondary.status.onStart.map(_(target))
}
}
// TODO : Support moves
// TODO : Multiparty
} else {
println("Missed!")
}
}
def attackRoll(user : Monster, move : Move, target : Monster) = {
if (move.accuracy == 0) {
true
} else {
val acc = user.boosts.getOrElse(Accuracy, 0) - target.boosts.getOrElse(Evasion, 0)
val mod = if (acc > 0) Fraction(3 + acc, 3) else Fraction(3, 3 - acc)
val chance = move.accuracy.%% * mod
rng.chance(chance)
}
}
def damageRoll(user : Monster, move : Move, target : Monster) = {
val atkStat = if (move.mvType == MoveType.Physical) PAtk else MAtk
val defStat = if (move.mvType == MoveType.Physical) PDef else MDef
// TODO : Fixed damage
val pow = if (move.powCallback != null) move.powCallback(user, target) else move.pow
val baseDmg = (2 * user.level / 5 + 2) * pow * user(atkStat) / (target(defStat) * 50) + 2
val multiplier = target.effectiveness(move.element)
if (multiplier > 1.0) {
println("It's super effective!")
} else if (multiplier < 1.0) {
println("It's not very effective.")
}
val maxDmg = (baseDmg * multiplier).toInt
val minDmg = (17 \ 20) * maxDmg
rng.nextInt(minDmg, maxDmg)
}
def applyBoosts(target : Monster, boosts : Map[Stat, Int]) {
boosts.foreach{case (s, b) => {
target.applyBoost(s, b)
if (b > 0) {
println(s"$target's $s rose!")
} else if (b < 0) {
println(s"$target's $s fell!")
}
}}
}
}

View File

@ -0,0 +1,12 @@
package mon
import scala.languageFeature.implicitConversions
import scala.util.Random
import mon.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,23 @@
package mon.stat
import scala.io.Source
import mon.util.YamlHelper
case class Element(val name : String, val effect : Map[String, Double]) {
def -->(other : Element) = {
effect.getOrElse(other.name, 1.0)
}
def <--(other : Element) = other --> this
override def toString = name
}
object Element {
private val elements = YamlHelper.extractSeq[Element](Element.getClass.getResourceAsStream("data/elements.yaml"))
private val fromName = elements.map(e => (e.name, e)).toMap
def apply(name : String) = fromName(name)
}

View File

@ -0,0 +1,62 @@
package mon.stat
import scala.io.Source
import Statistic._
import mon.util.YamlHelper
abstract class Form {
val name : String
//val height
//val weight
val desc : String
val elements : IndexedSeq[Element]
val baseStats : Map[Stat, Int]
// val appearance // animation
// moves
// abilities
val catchRate : Int
// val color
override def toString = {
name
}
}
case class FormToken(
val name : String,
val desc : String,
val elements : IndexedSeq[String],
val baseStats : Map[String, Int],
val catchRate : Int = 255
) {
def instantiate() = {
val self = this
new Form {
val name = self.name
val desc = self.desc
val elements = self.elements.map(Element(_))
val baseStats = self.baseStats.map{ case (s, i) => (Statistic(s), i)}
val catchRate = self.catchRate
}
}
}
object Form {
def apply(name : String) = byName(name)
def fromMap(dict : Map[String, Any]) = {
new Form {
val name = dict("name").toString
val desc = dict("desc").toString()
val elements = IndexedSeq()
val baseStats = Statistic.buildMap(_ => 10)
val catchRate = 255
}
}
val forms = YamlHelper.extractSeq[FormToken](Form.getClass.getResourceAsStream("data/forms.yaml")).map(_.instantiate())
val byName = forms.map(f => (f.name, f)).toMap
}

View File

@ -0,0 +1,5 @@
package mon.stat
object Gender extends Enumeration {
val Male, Female, Fluid, Neuter = Value
}

View File

@ -0,0 +1,16 @@
package mon.stat
import scala.util.Random
case class Gene(val gender : Gender, val ivs : Map[Stat, Int], ot : TrainerID) {
}
object Gene {
final val MaxIV = 31
def randomGene(ot : TrainerID, form : Form)(implicit rng : Random) = {
val gender = Gender.Neuter // TODO
val ivs = Statistic.values.map(s => (s, rng.nextInt(MaxIV + 1))).toMap
Gene(gender, ivs, ot)
}
}

View File

@ -0,0 +1,62 @@
package mon.stat
import scala.util._
import mon.util.Fraction
import Monster._
import Statistic._
class Monster(val base : StorageMon) {
val level = 10
val stats = Statistic.buildMap(computeStat)
var boosts = Statistic.buildMap(_ => 0)
var hp = stats(Hp)
var status : Option[Status] = None
def isAlive = hp > 0
def apply(s : Stat) = {
val mod = boosts.getOrElse(s, 0)
val mult = if (mod > 0) Fraction(2 + mod, 2) else Fraction(2, 2 - mod)
mult * stats(s)
}
def elements = base.form.elements
def takeDamage(dmg : Int) {
hp = Math.max(0, hp - dmg)
}
def recoverDamage(healing : Int) {
hp = Math.min(stats(Hp), hp + healing)
}
def applyBoost(s : Stat, boost : Int) {
val modified = boosts.getOrElse(s, 0) + boost
boosts = boosts.updated(s, Math.min(MaxBoost, Math.max(-MaxBoost, modified)))
}
def effectiveness(element : Element) : Double = {
elements.foldLeft(1.0)((m, e) => m * (element --> e))
}
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
val score = if(s == Hp) frac + 10 + level else frac + 5
score
}
override def toString = base.nickname
}
object Monster {
final val MaxBoost = 6
def build(trainer : TrainerID, form : Form)(implicit rng : Random) = {
}
}

View File

@ -0,0 +1,116 @@
package mon.stat
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
import com.fasterxml.jackson.module.scala.JsonScalaEnumeration
import scala.io.Source
import mon.util.{TypeReference, YamlHelper}
object MoveType extends Enumeration {
val Physical, Special, Status = Value
}
class MoveTypeType extends TypeReference[MoveType.type]
object Target extends Enumeration {
val Normal, Self, AllAdjacentFoes = Value
}
class TargetType extends TypeReference[Target.type]
abstract class Move {
val name : String
val desc : String
val mvType : MoveType
val pow : Int
val powCallback : (Monster, Monster) => Int
val prior : Int
val accuracy : Int
val pp : Int
val element : Element
val flags : Set[String]
val target : Target
val boosts : Map[Stat, Int]
val crit : Int
val selfEffect : Secondary
val secondary: Secondary
// boosts
// onHit
// onTryHit
// zPower, zMoveEffect, zMoveBoost
override def toString = {
name
}
}
case class MoveToken(
val name : String,
val shortDesc : String,
@JsonScalaEnumeration(classOf[MoveTypeType]) val category : MoveType,
val basePower : Option[Int],
val basePowerCallback : String,
val priority : Int,
val accuracy : Option[Int],
val pp : Int,
val `type` : String,
val flags : Map[String, Int],
val self : SecondaryToken,
val secondary : SecondaryToken,
@JsonScalaEnumeration(classOf[TargetType]) val target : Target = Target.Normal,
val boosts : Map[String, Int] = Map(),
val crit : Int = 0) {
def instantiate() = {
val token = this
new Move {
val name = token.name
val desc = token.shortDesc
val mvType = category
val pow = token.basePower.getOrElse(0)
val powCallback = Move.compilePowCallback(token.basePowerCallback)
val prior = token.priority
val pp = token.pp
val element = Element(token.`type`)
val accuracy = token.accuracy.getOrElse(100)
val flags = token.flags.keySet
val target = token.target
val boosts = if (token.boosts != null) token.boosts.map{case (s, i) => (Statistic(s), i)} else Map()
val crit = token.crit
val selfEffect = if (token.self != null) token.self.instantiate() else null
val secondary = if (token.secondary != null) token.secondary.instantiate() else null
}
}
}
object Move {
val tokens = YamlHelper.extractSeq[MoveToken](Source.fromInputStream(Move.getClass.getResourceAsStream("data/moves.yaml")))
val moves = tokens.map(_.instantiate())
val byName = moves.map(m => (m.name, m)).toMap
def apply(s : String) = byName(s)
def compilePowCallback(code: String): (Monster, Monster) => Int = {
if (code != null) {
val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
val tree = tb.parse(
s"""
|import mon.stat.Monster
|import mon.stat.Statistic._
|def callback(user : Monster, target : Monster): Int = {
| $code
|}
|callback _
""".stripMargin)
val f = tb.compile(tree)
val wrapper = f()
wrapper.asInstanceOf[(Monster, Monster) => Int]
} else {
null
}
}
}

View File

@ -0,0 +1,17 @@
package mon.stat
import scala.util.Random
import mon.battle.rngDice
import mon.stat.Target._
class Party(val trainer : TrainerID, var lead : Monster, val sideboard : IndexedSeq[Monster]) {
def pollAction(implicit rng : Random) : Move = rng.pick(lead.base.moves)
def pollTarget(move : Move, user : Monster, us : Party, them: Party)(implicit rng : Random) : Monster = {
if (move.target == Self) {
user
} else {
them.lead
}
}
}

View File

@ -0,0 +1,31 @@
package mon.stat
abstract class Secondary {
val chance : Int
val boosts : Map[Stat, Int]
val status : Status
// val volatileStatus
//val onHit :
// val self
}
case class SecondaryToken(
val chance : Int,
val boosts : Map[String, Int],
val status : String,
//val volatileStatus
val onHit : String
) {
def instantiate() : Secondary = {
val self = this
new Secondary {
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
}
}
}
object Secondary {
}

View File

@ -0,0 +1,11 @@
package mon.stat
class Species {
// name
// gender ratio
// forms
// baby forms (hatchable)
// evolution conditions (form change)
// egg groups
// egg steps
}

View File

@ -0,0 +1,29 @@
package mon.stat
object Statistic extends Enumeration {
val Hp = Value("Hp")
val PAtk = Value("P. Atk")
val PDef = Value("P. Def")
val MAtk = Value("M. Atk")
val MDef = Value("M. Def")
val Speed = Value("Speed")
val Accuracy = Value("Accuracy")
val Evasion = Value("Evasion")
def apply(s : String) = s match {
case "hp" => Hp
case "patk" => PAtk
case "pdef" => PDef
case "matk" => MAtk
case "mdef" => MDef
case "spd" => Speed
case "acc" => Accuracy
case "evd" => Evasion
}
def buildMap(f : Stat => Int) : Map[Stat, Int] = {
Map(Hp -> f(Hp), PAtk -> f(PAtk), PDef -> f(PDef), MAtk -> f(MAtk), MDef -> f(MDef), Speed -> f(Speed))
}
def emptyEvs = buildMap(_ => 0)
}

View File

@ -0,0 +1,96 @@
package mon.stat
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
import scala.io.Source
import mon.util.YamlHelper
abstract class Status {
val name : String
// val id
// val effectType
val onStart : Option[Monster => Unit]
// val onModifyStat
// val onBeforeMovePriority : Int
// val onBeforeMove
// val onModifyMove
// val onHit
val onResidualOrder : Int
val onResidual : Option[Monster => Unit]
// val onSwitchIn
override def toString = name
}
case class StatusToken(
val name : String,
val onStart : String,
val onResidualOrder : Int,
val onResidual : String
) {
def instantiate() = {
val self = this
new Status{
val name = self.name
val onStart = Status.compileOnStart(self.onStart)
val onResidualOrder = self.onResidualOrder
val onResidual = Status.compileOnResidual(self.onResidual)
}
}
}
object Status {
private var statuses = Map[String, Status]()
val tokens = YamlHelper.extractSeq[StatusToken](Source.fromInputStream(Move.getClass.getResourceAsStream("data/statuses.yaml"))).map(t => (t.name, t)).toMap
def apply(name : String) = {
if (!statuses.contains(name)) {
statuses = statuses.updated(name, tokens(name).instantiate())
}
statuses(name)
}
private val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
def compileOnStart(code : String) : Option[(Monster /*, source, source effect */) => Unit] = {
if (code == null) {
None
} else {
val tree = tb.parse(
s"""
|import mon.stat.Monster
|import mon.stat.Statistic._
|def onStart(mon : Monster) = {
| $code
|}
|onStart _
""".stripMargin)
val f = tb.compile(tree)
val wrapper = f()
Some(wrapper.asInstanceOf[Monster => Unit])
}
}
def compileOnResidual(code : String) : Option[Monster => Unit] = {
if (code == null) {
None
} else {
val tree = tb.parse(
s"""
|import mon.stat.Monster
|import mon.stat.Statistic._
|def onResidual(mon : Monster) = {
| $code
|}
|onResidual _
""".stripMargin)
val f = tb.compile(tree)
val wrapper = f()
Some(wrapper.asInstanceOf[Monster => Unit])
}
}
}

View File

@ -0,0 +1,5 @@
package mon.stat
class StorageMon(val nickname : String, val gene : Gene, val form : Form, val evs : Map[Stat, Int], val moves : IndexedSeq[Move]) {
}

View File

@ -0,0 +1,5 @@
package mon.stat
case class TrainerID(name : String, gender : Gender, id : Long) {
}

View File

@ -0,0 +1,131 @@
[
{
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,
Poison: 0.5
}
},
{
name: Water,
effect: {
Fire: 2.0,
Ground: 2.0,
Rock: 2.0,
Water: 0.5,
Grass: 0.5,
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,
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,
Ghost: 0.0
}
},
{
name: Poison,
effect: {
Fairy: 2.0,
Grass: 2.0,
Poison: 0.5,
Ground: 0.5,
Rock: 0.5,
Ghost: 0.5,
Steel: 0.0
}
}
]

View File

@ -0,0 +1,24 @@
- name: Chanilla
desc: A vanilla chameleon.
elements:
- Normal
baseStats:
hp: 60
patk: 70
pdef: 70
matk: 70
mdef: 70
spd: 60
- name: Diabolo
desc: This is a ball of lightning from Hell itself.
elements:
- Electric
- Fire
baseStats:
hp: 40
patk: 110
pdef: 30
matk: 130
mdef: 40
spd: 130

View File

@ -0,0 +1,213 @@
- name: Aqua Jet
num: 453
accuracy: 100
basePower: 40
category: Physical
desc: No additional effect.
shortDesc: Usually goes first.
id: aquajet
isViable: true
pp: 20
priority: 1
flags:
contact: 1
protect: 1
mirror: 1
secondary: null
target: Normal
type: Water
type: Water
zMovePower: 100
contestType: Cool
- name: "Bulk Up"
num: 339
accuracy: 0
basePower: 0
category: "Status"
desc: "Raises the user's Attack and Defense by 1 stage."
shortDesc: "Raises the user's Attack and Defense by 1."
id: "bulkup"
isViable: true
pp: 20
priority: 0
flags:
snatch: 1
boosts:
patk: 1
pdef: 1
secondary: null
target: "Self"
type: "Fighting"
zMoveBoost:
patk: 1
contestType: "Cool"
- name: Charm
num: 204
accuracy: 100
basePower: 0
category: Status
desc: Lowers the target's Attack by 2 stages.
shortDesc: Lowers the target's Attack by 2.
id: charm
pp: 20
priority: 0
flags:
protect: 1
reflectable: 1
mirror: 1
mystery: 1
boosts:
patk: -2
secondary: null
target: Normal
type: Fairy
zMoveBoost:
pdef: 1
contestType: Cute
- name: "Close Combat"
num: 370
accuracy: 100
basePower: 120
category: "Physical"
desc: "Lowers the user's Defense and Special Defense by 1 stage."
shortDesc: "Lowers the user's Defense and Sp. Def by 1."
id: "closecombat"
isViable: true
pp: 5
priority: 0
flags:
contact: 1
protect: 1
mirror: 1
self:
boosts:
pdef: -1
spd: -1
secondary: null
target: "Normal"
type: "Fighting"
zMovePower: 190
contestType: "Tough"
- name: Electro Ball
num: 486
accuracy: 100
basePower: 0
basePowerCallback: |
val ratio = user(Speed) / target(Speed)
// this.debug([40, 60, 80, 120, 150][(Math.floor(ratio) > 4 ? 4 : Math.floor(ratio))] + ' bp');
if (ratio >= 4) {
150
} else if (ratio >= 3) {
120
} else if (ratio >= 2) {
80
} else if (ratio >= 1) {
60;
} else {
40;
}
category: Special
desc: "The power of this move depends on (user's current Speed / target's current Speed), rounded down. Power is equal to 150 if the result is 4 or more, 120 if 3, 80 if 2, 60 if 1, 40 if less than 1. If the target's current Speed is 0, this move's power is 40."
shortDesc: "More power the faster the user is than the target."
id: electroball
pp: 10
priority: 0
flags:
bullet: 1
protect: 1
mirror: 1
secondary: null
target: "Normal"
type: Electric
zMovePower: 160
contestType: "Cool"
- name: "Poison Gas"
num: 139
accuracy: 90
basePower: 0
category: "Status"
desc: "Poisons the target."
shortDesc: "Poisons the foe(s)."
id: "poisongas"
pp: 40
priority: 0
flags:
protect: 1
reflectable: 1
mirror: 1
status: 'psn'
secondary: null
target: "AllAdjacentFoes"
type: "Poison"
zMoveBoost:
def: 1
contestType: "Clever"
- name: "Poison Sting"
num: 40
accuracy: 100
basePower: 15
category: "Physical"
desc: "Has a 30% chance to poison the target."
shortDesc: "30% chance to poison the target."
id: "poisonsting"
pp: 35
priority: 0
flags:
protect: 1
mirror: 1
secondary:
chance: 30
status: 'psn'
target: "Normal"
type: "Poison"
zMovePower: 100
contestType: "Clever"
- name: "Snarl"
num: 555
accuracy: 95
basePower: 55
category: "Special"
desc: "Has a 100% chance to lower the target's Special Attack by 1 stage."
shortDesc: "100% chance to lower the foe(s) Sp. Atk by 1."
id: "snarl"
pp: 15
priority: 0
flags:
protect: 1
mirror: 1
sound: 1
authentic: 1
secondary:
chance: 100
boosts:
matk: -1
target: "AllAdjacentFoes"
type: "Dark"
zMovePower: 100
contestType: "Tough"
- name: Tackle
num: 33
accuracy: 100
basePower: 40
category: Physical
shortDesc: No additional effect.
id: tackle
pp: 35
priority: 0
flags:
contact: 1
protect: 1
mirror: 1
secondary: null
target: Normal
type: Normal
zMovePower: 100
contestType: Tough

View File

@ -0,0 +1,140 @@
- name: 'brn'
id: 'brn'
num: 0
effectType: 'Status'
onStart: |
if (sourceEffect && sourceEffect.id === 'flameorb') {
this.add('-status', target, 'brn', '[from] item: Flame Orb');
} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'brn', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'brn');
}
// Damage reduction is handled directly in the sim/battle.js damage function
onResidualOrder: 9
onResidual: |
this.damage(pokemon.maxhp / 16);
- name: 'par'
id: 'par'
num: 0
effectType: 'Status'
onStart: |
if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'par', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'par');
}
onModifySpe: |
if (!pokemon.hasAbility('quickfeet')) {
return this.chainModify(0.5);
}
onBeforeMovePriority: 1
onBeforeMove: |
if (this.randomChance(1, 4)) {
this.add('cant', pokemon, 'par');
return false;
}
- name: 'slp'
id: 'slp'
num: 0
effectType: 'Status'
onStart: |
if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'slp', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else if (sourceEffect && sourceEffect.effectType === 'Move') {
this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
} else {
this.add('-status', target, 'slp');
}
// 1-3 turns
this.effectData.startTime = this.random(2, 5);
this.effectData.time = this.effectData.startTime;
onBeforeMovePriority: 10
onBeforeMove: |
if (pokemon.hasAbility('earlybird')) {
pokemon.statusData.time--;
}
pokemon.statusData.time--;
if (pokemon.statusData.time <= 0) {
pokemon.cureStatus();
return;
}
this.add('cant', pokemon, 'slp');
if (move.sleepUsable) {
return;
}
return false;
- name: 'frz'
id: 'frz'
num: 0
effectType: 'Status'
onStart: |
if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'frz', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'frz');
}
if (target.template.species === 'Shaymin-Sky' && target.baseTemplate.baseSpecies === 'Shaymin') {
target.formeChange('Shaymin', this.effect, true);
}
onBeforeMovePriority: 10
onBeforeMove: |
if (move.flags['defrost']) return;
if (this.randomChance(1, 5)) {
pokemon.cureStatus();
return;
}
this.add('cant', pokemon, 'frz');
return false;
onModifyMove: |
if (move.flags['defrost']) {
this.add('-curestatus', pokemon, 'frz', '[from] move: ' + move);
pokemon.setStatus('');
}
onHit: |
if (move.thawsTarget || move.type === 'Fire' && move.category !== 'Status') {
target.cureStatus();
}
- name: 'psn'
id: 'psn'
num: 0
effectType: 'Status'
onStart: |
println(s"${mon} was poisoned!")
/*
if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'psn', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'psn');
}
*/
onResidualOrder: 9
onResidual: |
mon.takeDamage(mon(Hp) / 8);
println(s"${mon} was damaged by poison!")
- name: 'tox'
id: 'tox'
num: 0
effectType: 'Status'
onStart: |
this.effectData.stage = 0;
if (sourceEffect && sourceEffect.id === 'toxicorb') {
this.add('-status', target, 'tox', '[from] item: Toxic Orb');
} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
this.add('-status', target, 'tox', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
} else {
this.add('-status', target, 'tox');
}
onSwitchIn: |
this.effectData.stage = 0;
onResidualOrder: 9
onResidual: |
if (this.effectData.stage < 15) {
this.effectData.stage++;
}
this.damage(this.clampIntRange(pokemon.maxhp / 16, 1) * this.effectData.stage);

View File

@ -0,0 +1,13 @@
package mon
import org.json4s.DefaultFormats
import org.json4s.ext.EnumNameSerializer
package object stat {
type Stat = Statistic.Value
type MoveType = MoveType.Value
type Gender = Gender.Value
type Target = Target.Value
implicit val formats = DefaultFormats + new EnumNameSerializer(MoveType) + new EnumNameSerializer(Gender)
}

View File

@ -0,0 +1,11 @@
package mon.util
import scala.util.Random
class Dice(val rng : Random) extends AnyVal {
def nextInt(min : Int, max : Int) = rng.nextInt(max - min + 1) + min
def pick[T](seq : IndexedSeq[T]) : T = seq(rng.nextInt(seq.size))
def chance(frac : Fraction) = rng.nextInt(frac.denom) < frac.num
}

View File

@ -0,0 +1,11 @@
package mon.util
case class Fraction(val num : Int, val denom : Int) {
def *(f : Fraction) = Fraction(num * f.num, denom * f.denom)
def *(x : Int) : Int = x * num / denom
}
class IntFraction(val x : Int) extends AnyVal {
def %% = Fraction(x, 100)
def \ (denom : Int) = Fraction(x, denom)
}

View File

@ -0,0 +1,52 @@
package mon.util
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.ext.EnumNameSerializer
import java.io.InputStream
import scala.io.Source
object JsonHelper {
// Maybe Look at Jackson-YAML & Jackson-Scala
implicit val formats = mon.stat.formats
def extract[T](text : String)(implicit mf : Manifest[T]) : T = {
val json = parse(text)
json.extract[T]
}
def extract[T](source : Source)(implicit mf : Manifest[T]) : T = {
val text = source.getLines().mkString("\n")
extract(text)
}
def extractFromFile[T](filename : String)(implicit mf : Manifest[T]) : T = {
using(filename : InputStream)(reader => extract(Source.fromInputStream(filename)))
}
def toScala(jvalue : JValue): Any = jvalue match {
case JNothing => null
case JNull => null
case JString(s) => s
case JDouble(num) => num.toDouble
case JDecimal(num) => num.toInt
case JInt(num) => num.toInt
case JLong(num) => num.toLong
case JBool(value) => value
case JObject(obj) => obj.map{ case (s, o) => {
(s, toScala(o))
}}.toMap
case JArray(arr) => arr.map(toScala)
case JSet(data) => data.map(toScala)
}
def loadFromSource(source : Source) : List[Map[String, Any]] = {
val text = source.getLines().mkString("\n")
val array = parse(text).asInstanceOf[JArray]
array.arr.map(jo => toScala(jo).asInstanceOf[Map[String, Any]])
}
}

View File

@ -0,0 +1,45 @@
package mon.util
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
import java.io.InputStream
import scala.io.Source
import mon.util._
object YamlHelper {
val mapper = new ObjectMapper(new YAMLFactory()) with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
def extract[T](text : String)(implicit m : Manifest[T]) : T = {
mapper.readValue[T](text)
}
def extract[T](source : Source)(implicit mf : Manifest[T]) : T = {
val text = source.getLines().mkString("\n")
extract(text)
}
def extract[T](source : InputStream)(implicit m : Manifest[T]) : T = {
mapper.readValue[T](source)
}
def extractSeq[T](text : String)(implicit m : Manifest[T]) : IndexedSeq[T] = {
mapper.readValue[IndexedSeq[T]](text)
}
def extractSeq[T](source : Source)(implicit mf : Manifest[T]) : IndexedSeq[T] = {
val text = source.getLines().mkString("\n")
extractSeq(text)
}
def extractSeq[T](source : InputStream)(implicit m : Manifest[T]) : IndexedSeq[T] = {
mapper.readValue[IndexedSeq[T]](source)
}
}

View File

@ -0,0 +1,44 @@
package mon
import java.io._
import scala.language.implicitConversions
package object util {
type TypeReference[A] = com.fasterxml.jackson.core.`type`.TypeReference[A]
def using[T <: Closeable, A](resource : T)(block : T => A) : A = {
try {
block(resource)
} finally {
resource.close()
}
}
def makeParentDirs(filename : String) : Unit = {
val file = new File(filename)
file.getParentFile().mkdirs()
}
def getFilename(file : String) : String = {
file.split("\\.").head
}
implicit def fileName2Writer(filename : String) : PrintWriter = {
makeParentDirs(filename)
new PrintWriter(new BufferedWriter(new FileWriter(filename)))
}
implicit def fileName2Reader(filename : String) : BufferedReader = {
new BufferedReader(new FileReader(filename))
}
implicit def filename2OutputStream(filename : String) : PrintStream = {
makeParentDirs(filename)
new PrintStream(new BufferedOutputStream(new FileOutputStream(filename)))
}
implicit def filename2InputStream(filename : String) : BufferedInputStream = {
new BufferedInputStream(new FileInputStream(filename))
}
}