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 } }