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