Adding FakeMon project
This commit is contained in:
parent
009e82c21f
commit
83e6bb7a3c
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
*/bin/*
|
||||||
|
*.metadata
|
||||||
|
*.cache
|
||||||
|
*.cache-main
|
||||||
|
*.classpath
|
||||||
|
*.project
|
||||||
|
*/.settings/*
|
32
FakeMon/src/mon/Game.scala
Normal file
32
FakeMon/src/mon/Game.scala
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
14
FakeMon/src/mon/battle/Action.scala
Normal file
14
FakeMon/src/mon/battle/Action.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
FakeMon/src/mon/battle/BattleEngine.scala
Normal file
95
FakeMon/src/mon/battle/BattleEngine.scala
Normal 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!")
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
12
FakeMon/src/mon/battle/package.scala
Normal file
12
FakeMon/src/mon/battle/package.scala
Normal 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)
|
||||||
|
}
|
23
FakeMon/src/mon/stat/Element.scala
Normal file
23
FakeMon/src/mon/stat/Element.scala
Normal 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)
|
||||||
|
}
|
62
FakeMon/src/mon/stat/Form.scala
Normal file
62
FakeMon/src/mon/stat/Form.scala
Normal 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
|
||||||
|
}
|
5
FakeMon/src/mon/stat/Gender.scala
Normal file
5
FakeMon/src/mon/stat/Gender.scala
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package mon.stat
|
||||||
|
|
||||||
|
object Gender extends Enumeration {
|
||||||
|
val Male, Female, Fluid, Neuter = Value
|
||||||
|
}
|
16
FakeMon/src/mon/stat/Gene.scala
Normal file
16
FakeMon/src/mon/stat/Gene.scala
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
62
FakeMon/src/mon/stat/Monster.scala
Normal file
62
FakeMon/src/mon/stat/Monster.scala
Normal 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) = {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
116
FakeMon/src/mon/stat/Move.scala
Normal file
116
FakeMon/src/mon/stat/Move.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
17
FakeMon/src/mon/stat/Party.scala
Normal file
17
FakeMon/src/mon/stat/Party.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
FakeMon/src/mon/stat/Secondary.scala
Normal file
31
FakeMon/src/mon/stat/Secondary.scala
Normal 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 {
|
||||||
|
|
||||||
|
}
|
11
FakeMon/src/mon/stat/Species.scala
Normal file
11
FakeMon/src/mon/stat/Species.scala
Normal 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
|
||||||
|
}
|
29
FakeMon/src/mon/stat/Statistic.scala
Normal file
29
FakeMon/src/mon/stat/Statistic.scala
Normal 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)
|
||||||
|
}
|
96
FakeMon/src/mon/stat/Status.scala
Normal file
96
FakeMon/src/mon/stat/Status.scala
Normal 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
FakeMon/src/mon/stat/StorageMon.scala
Normal file
5
FakeMon/src/mon/stat/StorageMon.scala
Normal 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]) {
|
||||||
|
|
||||||
|
}
|
5
FakeMon/src/mon/stat/TrainerID.scala
Normal file
5
FakeMon/src/mon/stat/TrainerID.scala
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package mon.stat
|
||||||
|
|
||||||
|
case class TrainerID(name : String, gender : Gender, id : Long) {
|
||||||
|
|
||||||
|
}
|
131
FakeMon/src/mon/stat/data/elements.yaml
Normal file
131
FakeMon/src/mon/stat/data/elements.yaml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
24
FakeMon/src/mon/stat/data/forms.yaml
Normal file
24
FakeMon/src/mon/stat/data/forms.yaml
Normal 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
|
213
FakeMon/src/mon/stat/data/moves.yaml
Normal file
213
FakeMon/src/mon/stat/data/moves.yaml
Normal 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
|
140
FakeMon/src/mon/stat/data/statuses.yaml
Normal file
140
FakeMon/src/mon/stat/data/statuses.yaml
Normal 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);
|
13
FakeMon/src/mon/stat/package.scala
Normal file
13
FakeMon/src/mon/stat/package.scala
Normal 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)
|
||||||
|
}
|
11
FakeMon/src/mon/util/Dice.scala
Normal file
11
FakeMon/src/mon/util/Dice.scala
Normal 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
|
||||||
|
}
|
11
FakeMon/src/mon/util/Fraction.scala
Normal file
11
FakeMon/src/mon/util/Fraction.scala
Normal 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)
|
||||||
|
}
|
52
FakeMon/src/mon/util/JsonHelper.scala
Normal file
52
FakeMon/src/mon/util/JsonHelper.scala
Normal 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]])
|
||||||
|
}
|
||||||
|
}
|
45
FakeMon/src/mon/util/YamlHelper.scala
Normal file
45
FakeMon/src/mon/util/YamlHelper.scala
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
44
FakeMon/src/mon/util/package.scala
Normal file
44
FakeMon/src/mon/util/package.scala
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user