Testing out JmonkeyEngine to make a game in Scala with Akka Actors within a pure FP layer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

138 lines
4.2 KiB

package wow.doge.mygame.game.entities
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import cats.Show
import cats.kernel.Eq
import io.estatico.newtype.macros.newtype
final case class CharacterStats(
hp: CharacterStats.Health,
stamina: CharacterStats.Stamina
)
object CharacterStats {
@newtype final case class HealHealth(toInt: Int)
@newtype final case class DamageHealth(toInt: Int)
@newtype final case class Health(toInt: Int)
object Health {
implicit class HealthOps(private val h: Health) extends AnyVal {
def :+(v: HealHealth): Health = Health(h.toInt + v.toInt)
def -(v: DamageHealth): Health = Health(h.toInt - v.toInt)
}
}
@newtype final case class HealStamina(toInt: Int)
@newtype final case class DamageStamina(toInt: Int)
@newtype final case class Stamina(toInt: Int)
object Stamina {
implicit class StaminaOps(private val h: Stamina) extends AnyVal {
def :+(v: HealStamina): Stamina = Stamina(h.toInt + v.toInt)
def -(v: DamageStamina): Stamina = Stamina(h.toInt - v.toInt)
}
}
implicit val show = Show.fromToString[CharacterStats]
implicit val eq = Eq.fromUniversalEquals[CharacterStats]
}
object StatsActor {
import CharacterStats._
sealed trait Status
object Status {
case object Alive extends Status
case object Dead extends Status
}
sealed trait Command
final case class TakeDamageResult(
value: CharacterStats.DamageHealth,
replyTo: ActorRef[(Status, CharacterStats)]
) extends Command
final case class ConsumeStaminaResult(
value: CharacterStats.DamageStamina,
replyTo: ActorRef[(Status, CharacterStats)]
) extends Command
final case class HealHealthResult(
value: HealHealth,
replyTo: ActorRef[CharacterStats]
) extends Command
final case class HealStaminaResult(
value: HealStamina,
replyTo: ActorRef[CharacterStats]
) extends Command
final case class CurrentStats(replyTo: ActorRef[CharacterStats])
extends Command
class Props(
startingHealth: CharacterStats.Health,
startingStamina: CharacterStats.Stamina
) {
def behavior =
Behaviors.setup[Command] { ctx =>
new StatsActor(ctx, this)
.receive(
State(CharacterStats(startingHealth, startingStamina))
)
}
}
final case class State(stats: CharacterStats)
}
class StatsActor(
ctx: ActorContext[StatsActor.Command],
props: StatsActor.Props
) {
import StatsActor._
import CharacterStats._
import com.softwaremill.quicklens._
def receive(state: State): Behavior[Command] =
Behaviors.receiveMessage[Command] {
// Todo add min max values
// case TakeDamage(value) =>
// val nextState =
// if (state.stats.hp - value <= 0)
// state.modify(_.stats.hp).setTo(0)
// else
// state.modify(_.stats.hp).using(_ - value)
// receive(nextState)
case TakeDamageResult(value, replyTo) =>
val nextState = if ((state.stats.hp - value).toInt <= 0) {
val s = state.modify(_.stats.hp).setTo(Health(0))
replyTo ! Status.Dead -> s.stats
s
} else {
val s = state.modify(_.stats.hp).using(_ - value)
replyTo ! Status.Alive -> s.stats
s
}
receive(nextState)
case ConsumeStaminaResult(value, replyTo) =>
val nextState = if ((state.stats.stamina - value).toInt <= 0) {
val s = state.modify(_.stats.stamina).setTo(Stamina(0))
replyTo ! Status.Alive -> s.stats
s
} else {
val s = state.modify(_.stats.stamina).using(_ - value)
replyTo ! Status.Alive -> s.stats
s
}
receive(nextState)
case HealHealthResult(value, replyTo) =>
val nextState = receive(state.modify(_.stats.hp).using(_ :+ value))
replyTo ! state.stats
nextState
case HealStaminaResult(value, replyTo) =>
val nextState = receive(state.modify(_.stats.stamina).using(_ :+ value))
replyTo ! state.stats
nextState
case CurrentStats(replyTo) =>
replyTo ! state.stats
Behaviors.same
}
}