From 34cd67c36f8244448d10aad23d73161671b5eb6c Mon Sep 17 00:00:00 2001
From: James Daly <dalyjame@cse.msu.edu>
Date: Sun, 2 Jun 2019 22:46:32 -0400
Subject: [PATCH] Added the ability for statuses to have data (like duration)
 and the sleep and toxic status effects

---
 FakeMon/src/fmon/Game.scala                |  7 +-
 FakeMon/src/fmon/battle/BattleEngine.scala |  2 +-
 FakeMon/src/fmon/stat/Move.scala           | 11 +--
 FakeMon/src/fmon/stat/Secondary.scala      |  2 +-
 FakeMon/src/fmon/stat/Status.scala         | 97 ++++++++++++++++------
 FakeMon/src/fmon/stat/data/moves.yaml      | 22 +++++
 FakeMon/src/fmon/stat/data/statuses.yaml   | 42 +++++-----
 7 files changed, 127 insertions(+), 56 deletions(-)

diff --git a/FakeMon/src/fmon/Game.scala b/FakeMon/src/fmon/Game.scala
index aa61187..3064738 100644
--- a/FakeMon/src/fmon/Game.scala
+++ b/FakeMon/src/fmon/Game.scala
@@ -14,13 +14,12 @@ case class Prop(url : Seq[String])
 
 object Game {
   def main(args : Array[String]): Unit = {
-    println(Element("Water").effect)
-    println(Status("psn"))
+    
     implicit val rng = new scala.util.Random()
     val form1 = Form("Diabolo")
     val form2 = Form("Chanilla")
-    val movepool1 = IndexedSeq(Move("Ice Beam"))
-    val movepool2 = IndexedSeq(Move("Scald"))
+    val movepool1 = IndexedSeq(Move("Aqua Jet"))
+    val movepool2 = IndexedSeq(Move("Thunder Wave"))
     val p1 = TrainerID("Jaeda", Gender.Female, 0)
     val p2 = TrainerID("Wild Monster", Gender.Male, 0)
     val party1 = new Party(p1, new MonsterPtr(new Monster(new StorageMon("Allied Mon", 500, Gene.randomGene(null, form1), form1, Statistic.emptyEvs, movepool1))), IndexedSeq())
diff --git a/FakeMon/src/fmon/battle/BattleEngine.scala b/FakeMon/src/fmon/battle/BattleEngine.scala
index 579c16e..0ae5985 100644
--- a/FakeMon/src/fmon/battle/BattleEngine.scala
+++ b/FakeMon/src/fmon/battle/BattleEngine.scala
@@ -51,7 +51,7 @@ class BattleEngine(val player: Party, val enemy: Party)(implicit val rng: Random
     val move = action.move
     val target = action.target
     if (user.isAlive) {
-      if (user.status.forall(_.onBeforeMove(user, move, target, rng))) {
+      if (user.status.forall(_.onBeforeMove(user, move, target))) {
         move.useMove(user, target)
       }
     }
diff --git a/FakeMon/src/fmon/stat/Move.scala b/FakeMon/src/fmon/stat/Move.scala
index bc3493e..c4a312f 100644
--- a/FakeMon/src/fmon/stat/Move.scala
+++ b/FakeMon/src/fmon/stat/Move.scala
@@ -58,7 +58,7 @@ abstract class Move extends MoveTurn {
   val target: Target
   val boosts: Map[Stat, Int]
   val crit: Int
-  val status: Status
+  val status: StatusTemplate
   val drain: Fraction
   val selfEffect: Secondary
   val secondary: Secondary
@@ -152,17 +152,18 @@ abstract class Move extends MoveTurn {
     }
   }
 
-  def applyEffect(target: Monster, effect: Secondary) {
+  def applyEffect(target: Monster, effect: Secondary)(implicit rng: Random) {
     applyBoosts(target, effect.boosts)
     applyStatus(target, effect.status)
   }
 
-  def applyStatus(target: Monster, status: Status) {
+  def applyStatus(target: Monster, status: StatusTemplate)(implicit rng: Random) {
     if (target.isAlive && status != null) {
       if (target.status == None) {
         // apply status
-        target.status = Some(status)
-        status.onStart(target)
+        val s = status.build
+        target.status = Some(s)
+        s.onStart(target)
       } else {
         println("But it failed!")
       }
diff --git a/FakeMon/src/fmon/stat/Secondary.scala b/FakeMon/src/fmon/stat/Secondary.scala
index 4a1f212..150f969 100644
--- a/FakeMon/src/fmon/stat/Secondary.scala
+++ b/FakeMon/src/fmon/stat/Secondary.scala
@@ -3,7 +3,7 @@ package fmon.stat
 abstract class Secondary {
   val chance : Int
   val boosts : Map[Stat, Int]
-  val status : Status
+  val status : StatusTemplate
   // val volatileStatus
   //val onHit : 
   // val self
diff --git a/FakeMon/src/fmon/stat/Status.scala b/FakeMon/src/fmon/stat/Status.scala
index b4002f6..213175e 100644
--- a/FakeMon/src/fmon/stat/Status.scala
+++ b/FakeMon/src/fmon/stat/Status.scala
@@ -1,5 +1,6 @@
 package fmon.stat
 
+import scala.collection.mutable.{Map => MutMap}
 import scala.reflect.runtime.universe._
 import scala.tools.reflect.ToolBox
 import scala.util.Random
@@ -8,21 +9,44 @@ import scala.io.Source
 
 import fmon.util._
 
-abstract class Status {
+class Status(template: StatusTemplate) {
+  def name = template.name
+  // val id
+  // val effectType
+  def onStart(mon: Monster)(implicit rng: Random) = template.onStart(this, mon, rng)
+  def onEnd(mon: Monster) = template.onEnd(this, mon)
+  def onModifyStat(mon: Monster, stat: Stat) = template.onModifyStat(this, mon, stat)
+  // val onBeforeMovePriority : Int
+  def onBeforeMove(mon: Monster, move: MoveTurn, target: Monster)(implicit rng: Random) = template.onBeforeMove(this, mon, move, target, rng)
+  // val onModifyMove
+  // val onHit
+  def onResidualOrder = template.onResidualOrder
+  def onResidual(mon: Monster) = template.onResidual(this, mon)
+  // val onSwitchIn
+
+  val intData: MutMap[String, Int] = MutMap[String, Int]()
+  val stringData = MutMap[String, String]()
+  
+  override def toString = name
+}
+
+abstract class StatusTemplate {
   val name: String
   // val id
   // val effectType
-  val onStart: Monster => Unit
-  val onEnd: Monster => Unit
-  val onModifyStat: (Monster, Stat) => Fraction
+  val onStart: (Status, Monster, Random) => Unit
+  val onEnd: (Status, Monster) => Unit
+  val onModifyStat: (Status, Monster, Stat) => Fraction
   // val onBeforeMovePriority : Int
-  val onBeforeMove: (Monster, MoveTurn, Monster, Random) => Boolean
+  val onBeforeMove: (Status, Monster, MoveTurn, Monster, Random) => Boolean
   // val onModifyMove
   // val onHit
   val onResidualOrder: Int
-  val onResidual: Monster => Unit
+  val onResidual: (Status, Monster) => Unit
   // val onSwitchIn
 
+  def build = new Status(this)
+  
   override def toString = name
 }
 
@@ -36,10 +60,10 @@ case class StatusToken(
   val onResidual:      String) {
   def instantiate() = {
     val self = this
-    new Status {
+    new StatusTemplate {
       val name = self.name
       val onStart = Status.compileOnStart(self.onStart)
-      val onEnd = Status.compileOnStart(self.onEnd)
+      val onEnd = Status.compileOnEnd(self.onEnd)
       val onBeforeMove = Status.compileOnBeforeMove(self.onBeforeMove)
       val onModifyStat = Status.compileOnModifyStat(self.onModifyStat)
       val onResidualOrder = self.onResidualOrder
@@ -49,7 +73,7 @@ case class StatusToken(
 }
 
 object Status {
-  private var statuses = Map[String, Status]()
+  private var statuses = Map[String, StatusTemplate]()
   val tokens = YamlHelper.extractSeq[StatusToken](Source.fromInputStream(Move.getClass.getResourceAsStream("data/statuses.yaml"))).map(t => (t.name, t)).toMap
 
   def apply(name: String) = {
@@ -61,74 +85,97 @@ object Status {
 
   private val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
 
-  def compileOnStart(code: String): (Monster /*, source, source effect */ ) => Unit = {
+  def compileOnStart(code: String): (Status, Monster /*, source, source effect */, Random ) => Unit = {
     if (code == null) {
-      _ => ()
+      (_, _, _) => ()
     } else {
       val tree = tb.parse(
         s"""
+           |import scala.util.Random
            |import fmon.stat._
            |import fmon.stat.Statistic._
-           |def onStart(mon : Monster) = {
+           |import fmon.util._
+           |def onStart(self:Status, mon: Monster, rng: Random) = {
            |  $code
            |}
            |onStart _
           """.stripMargin)
       val f = tb.compile(tree)
       val wrapper = f()
-      wrapper.asInstanceOf[Monster => Unit]
+      wrapper.asInstanceOf[(Status, Monster, Random) => Unit]
+    }
+  }
+  
+  def compileOnEnd(code: String): (Status, Monster) => Unit = {
+    if (code == null) {
+      (_, _) => ()
+    } else {
+      val tree = tb.parse(
+        s"""
+           |import fmon.stat._
+           |import fmon.stat.Statistic._
+           |import fmon.util._
+           |def onStart(self:Status, mon: Monster) = {
+           |  $code
+           |}
+           |onStart _
+          """.stripMargin)
+      val f = tb.compile(tree)
+      val wrapper = f()
+      wrapper.asInstanceOf[(Status, Monster) => Unit]
     }
   }
 
-  def compileOnBeforeMove(code: String): (Monster, MoveTurn, Monster, Random) => Boolean = {
+  def compileOnBeforeMove(code: String): (Status, Monster, MoveTurn, Monster, Random) => Boolean = {
     if (code == null) {
-      (_, _, _, _) => true
+      (_, _, _, _, _) => true
     } else {
       val tree = tb.parse(
         s"""
           import scala.util.Random
           import fmon.stat._
           import fmon.stat.Statistic._
-          def onBeforeMove(mon: Monster, move: MoveTurn, target: Monster, rng: Random): Boolean = {
+          def onBeforeMove(self: Status, mon: Monster, move: MoveTurn, target: Monster, rng: Random): Boolean = {
             $code
           }
           onBeforeMove _
           """)
       val f = tb.compile(tree)
       val wrapper = f()
-      wrapper.asInstanceOf[(Monster, MoveTurn, Monster, Random) => Boolean]
+      wrapper.asInstanceOf[(Status, Monster, MoveTurn, Monster, Random) => Boolean]
     }
   }
 
-  def compileOnModifyStat(code: String): (Monster, Stat) => Fraction = {
+  def compileOnModifyStat(code: String): (Status, Monster, Stat) => Fraction = {
     if (code == null) {
-      (_, _) => 1.frac
+      (_, _, _) => 1.frac
     } else {
       val tree = tb.parse(
         s"""
         import fmon.stat._
         import fmon.stat.Statistic._
         import fmon.util._
-        def onModifyStat(mon: Monster, stat: Stat): Fraction = {
+        def onModifyStat(self: Status, mon: Monster, stat: Stat): Fraction = {
           $code
         }
         onModifyStat _
         """)
       val f = tb.compile(tree)
       val wrapper = f()
-      wrapper.asInstanceOf[(Monster, Stat) => Fraction]
+      wrapper.asInstanceOf[(Status, Monster, Stat) => Fraction]
     }
   }
 
-  def compileOnResidual(code: String): Monster => Unit = {
+  def compileOnResidual(code: String): (Status, Monster) => Unit = {
     if (code == null) {
-      _ => ()
+      (_, _) => ()
     } else {
       val tree = tb.parse(
         s"""
            |import fmon.stat._
            |import fmon.stat.Statistic._
-           |def onResidual(mon : Monster) = {
+           |import fmon.util._
+           |def onResidual(self: Status, mon: Monster) = {
            |  $code
            |}
            |onResidual _
@@ -136,7 +183,7 @@ object Status {
       val f = tb.compile(tree)
       val wrapper = f()
 
-      wrapper.asInstanceOf[Monster => Unit]
+      wrapper.asInstanceOf[(Status, Monster) => Unit]
     }
   }
 }
\ No newline at end of file
diff --git a/FakeMon/src/fmon/stat/data/moves.yaml b/FakeMon/src/fmon/stat/data/moves.yaml
index a8d2fcc..3dea64e 100644
--- a/FakeMon/src/fmon/stat/data/moves.yaml
+++ b/FakeMon/src/fmon/stat/data/moves.yaml
@@ -278,6 +278,28 @@
   zMovePower: 100
   contestType: "Tough"
 
+- name: Spore
+  accuracy: 100
+  basePower: 0
+  category: Status
+  contestType: Beautiful
+  flags:
+    mirror: 1
+    powder: 1
+    protect: 1
+    reflectable: 1
+  id: spore
+  isViable: true
+  num: 147
+  pp: 15
+  priority: 0
+  secondary: null
+  shortDesc: Causes the target to fall asleep.
+  status: slp
+  target: Normal
+  type: Grass
+  zMoveEffect: clearnegativeboost
+
 - name: Tackle
   num: 33
   accuracy: 100
diff --git a/FakeMon/src/fmon/stat/data/statuses.yaml b/FakeMon/src/fmon/stat/data/statuses.yaml
index f58eacc..a320d98 100644
--- a/FakeMon/src/fmon/stat/data/statuses.yaml
+++ b/FakeMon/src/fmon/stat/data/statuses.yaml
@@ -60,6 +60,8 @@
   num: 0
   effectType: 'Status'
   onStart: |
+    println(s"${mon} fell asleep!")
+    /*
     if (sourceEffect && sourceEffect.effectType === 'Ability') {
       this.add('-status', target, 'slp', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
     } else if (sourceEffect && sourceEffect.effectType === 'Move') {
@@ -67,30 +69,27 @@
     } else {
       this.add('-status', target, 'slp');
     }
+    */
     // 1-3 turns
-    this.effectData.startTime = this.random(2, 5);
-    this.effectData.time = this.effectData.startTime;
+    self.intData("startTime") = rng.nextInt(2, 5)
+    self.intData("time") = self.intData("startTime")
   onEnd: |
     println(s"${mon} woke up!")
   onBeforeMovePriority: 10
   onBeforeMove: |
     /*
-    if (pokemon.hasAbility('earlybird')) {
-      pokemon.statusData.time--;
+    if (mon.hasAbility('earlybird')) {
+      this.intData.time--;
     }
     */
-    pokemon.statusData.time--;
-    if (pokemon.statusData.time <= 0) {
-      pokemon.cureStatus();
-      return;
+    self.intData("time") -= 1;
+    if (self.intData("time") <= 0) {
+      mon.cureStatus();
+      true
+    } else {
+      println(s"${mon} is fast asleep.")
+      !move.flags("sleepUsable")
     }
-    /*
-    this.add('cant', pokemon, 'slp');
-    if (move.sleepUsable) {
-      return;
-    }
-    */
-    return false;
   
 - name: 'frz'
   effectType: Status
@@ -153,19 +152,22 @@
   num: 0
   effectType: 'Status'
   onStart: |
-    this.effectData.stage = 0;
+    println(s"${mon} was baddly poisoned!")
+    self.intData("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++;
+    if (self.intData("stage") < 15) {
+      self.intData("stage") += 1
     }
-    this.damage(this.clampIntRange(pokemon.maxhp / 16, 1) * this.effectData.stage);
+    mon.takeDamage(self.intData("stage") \\ 16 * mon(Hp));
+    println(s"${mon} was damaged by poison!")