|
@ -1,35 +1,43 @@ |
|
|
package wow.doge.mygame.game.entities |
|
|
package wow.doge.mygame.game.entities |
|
|
|
|
|
|
|
|
|
|
|
import scala.concurrent.duration._ |
|
|
|
|
|
import scala.util.Failure |
|
|
|
|
|
import scala.util.Success |
|
|
|
|
|
|
|
|
import akka.actor.typed.ActorRef |
|
|
import akka.actor.typed.ActorRef |
|
|
import akka.actor.typed.Behavior |
|
|
import akka.actor.typed.Behavior |
|
|
import akka.actor.typed.SupervisorStrategy |
|
|
import akka.actor.typed.SupervisorStrategy |
|
|
import akka.actor.typed.scaladsl.ActorContext |
|
|
import akka.actor.typed.scaladsl.ActorContext |
|
|
import akka.actor.typed.scaladsl.Behaviors |
|
|
import akka.actor.typed.scaladsl.Behaviors |
|
|
|
|
|
import akka.util.Timeout |
|
|
|
|
|
import monix.execution.CancelableFuture |
|
|
|
|
|
import monix.execution.CancelablePromise |
|
|
|
|
|
import wow.doge.mygame.game.subsystems.movement.CanMove |
|
|
|
|
|
import wow.doge.mygame.implicits._ |
|
|
|
|
|
import wow.doge.mygame.math.ImVector3f |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedDown |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedLeft |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedRight |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedUp |
|
|
import wow.doge.mygame.subsystems.events.Event |
|
|
import wow.doge.mygame.subsystems.events.Event |
|
|
import wow.doge.mygame.subsystems.events.EventBus |
|
|
import wow.doge.mygame.subsystems.events.EventBus |
|
|
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus |
|
|
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus |
|
|
import wow.doge.mygame.subsystems.events.TickEvent |
|
|
import wow.doge.mygame.subsystems.events.TickEvent |
|
|
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick |
|
|
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick |
|
|
import wow.doge.mygame.subsystems.movement.ImMovementActor |
|
|
import wow.doge.mygame.subsystems.movement.ImMovementActor |
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedLeft |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedUp |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedRight |
|
|
|
|
|
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedDown |
|
|
|
|
|
import wow.doge.mygame.math.ImVector3f |
|
|
|
|
|
import wow.doge.mygame.game.subsystems.movement.CanMove |
|
|
|
|
|
import wow.doge.mygame.implicits._ |
|
|
|
|
|
import akka.util.Timeout |
|
|
|
|
|
import scala.concurrent.duration._ |
|
|
|
|
|
import scala.util.Success |
|
|
|
|
|
import scala.util.Failure |
|
|
|
|
|
|
|
|
|
|
|
object NpcActorSupervisor { |
|
|
object NpcActorSupervisor { |
|
|
sealed trait Command |
|
|
sealed trait Command |
|
|
case class Move(pos: ImVector3f) extends Command |
|
|
|
|
|
private case class UpdatePosition(pos: ImVector3f) extends Command |
|
|
|
|
|
|
|
|
final case class Move(pos: ImVector3f) extends Command |
|
|
|
|
|
private final case class InternalMove( |
|
|
|
|
|
move: Move, |
|
|
|
|
|
signal: CancelableFuture[NpcMovementActor2.DoneMoving.type] |
|
|
|
|
|
) extends Command |
|
|
|
|
|
private case object DoneMoving extends Command |
|
|
|
|
|
// private case class MovementResponse(response: CancelableFuture[_]) extends Command |
|
|
private case class LogError(err: Throwable) extends Command |
|
|
private case class LogError(err: Throwable) extends Command |
|
|
case object MovementTick extends Command |
|
|
|
|
|
|
|
|
private case object NoOp extends Command |
|
|
|
|
|
|
|
|
final case class Props( |
|
|
final case class Props( |
|
|
npcMovementActorBehavior: Behavior[NpcMovementActor2.Command], |
|
|
npcMovementActorBehavior: Behavior[NpcMovementActor2.Command], |
|
@ -43,115 +51,186 @@ object NpcActorSupervisor { |
|
|
s"npc-${npcName}-NpcMovementActor" |
|
|
s"npc-${npcName}-NpcMovementActor" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
new NpcActorSupervisor(ctx, this) |
|
|
|
|
|
.idle(State(npcMovementActor, initialPos)) |
|
|
|
|
|
|
|
|
new NpcActorSupervisor(ctx, this, Children(npcMovementActor)) |
|
|
|
|
|
.idle(State()) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
final case class State( |
|
|
final case class State( |
|
|
npcMovementActor: ActorRef[NpcMovementActor2.Command], |
|
|
|
|
|
currentPos: ImVector3f |
|
|
|
|
|
|
|
|
) |
|
|
|
|
|
final case class Children( |
|
|
|
|
|
npcMovementActor: ActorRef[NpcMovementActor2.Command] |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
class NpcActorSupervisor( |
|
|
class NpcActorSupervisor( |
|
|
ctx: ActorContext[NpcActorSupervisor.Command], |
|
|
ctx: ActorContext[NpcActorSupervisor.Command], |
|
|
props: NpcActorSupervisor.Props |
|
|
|
|
|
|
|
|
props: NpcActorSupervisor.Props, |
|
|
|
|
|
children: NpcActorSupervisor.Children |
|
|
) { |
|
|
) { |
|
|
import NpcActorSupervisor._ |
|
|
import NpcActorSupervisor._ |
|
|
implicit val timeout = Timeout(1.second) |
|
|
implicit val timeout = Timeout(1.second) |
|
|
|
|
|
|
|
|
|
|
|
private val movementTimer = ctx.spawn( |
|
|
|
|
|
GenericTimerActor |
|
|
|
|
|
.Props( |
|
|
|
|
|
children.npcMovementActor, |
|
|
|
|
|
NpcMovementActor2.MovementTick, |
|
|
|
|
|
100.millis |
|
|
|
|
|
) |
|
|
|
|
|
.create, |
|
|
|
|
|
s"npc-John-NpcActorTimer" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
def idle(state: State): Behavior[NpcActorSupervisor.Command] = |
|
|
def idle(state: State): Behavior[NpcActorSupervisor.Command] = |
|
|
|
|
|
Behaviors.setup { _ => |
|
|
|
|
|
ctx.log.info("Inside Idle State") |
|
|
Behaviors.receiveMessage[Command] { |
|
|
Behaviors.receiveMessage[Command] { |
|
|
case Move(pos) => { |
|
|
|
|
|
state.npcMovementActor ! NpcMovementActor2.Move(pos) |
|
|
|
|
|
val movementTimer = ctx.spawn( |
|
|
|
|
|
GenericTimerActor.Props(ctx.self, MovementTick, 100.millis).create, |
|
|
|
|
|
s"npc-${props.npcName}-NpcActorTimer" |
|
|
|
|
|
) |
|
|
|
|
|
movementTimer ! GenericTimerActor.Start |
|
|
|
|
|
moving(state, pos, movementTimer) |
|
|
|
|
|
|
|
|
case m @ Move(pos) => |
|
|
|
|
|
ctx.ask( |
|
|
|
|
|
children.npcMovementActor, |
|
|
|
|
|
NpcMovementActor2.MoveTo(pos, _) |
|
|
|
|
|
) { |
|
|
|
|
|
case Success(signal) => InternalMove(m, signal) |
|
|
|
|
|
case Failure(exception) => LogError(exception) |
|
|
} |
|
|
} |
|
|
|
|
|
Behaviors.same |
|
|
|
|
|
case InternalMove(move, signal) => |
|
|
|
|
|
moving(state, move.pos, signal) |
|
|
|
|
|
|
|
|
case LogError(err) => |
|
|
case LogError(err) => |
|
|
ctx.log.warn(err.getMessage()) |
|
|
ctx.log.warn(err.getMessage()) |
|
|
Behaviors.same |
|
|
Behaviors.same |
|
|
case _ => Behaviors.unhandled |
|
|
case _ => Behaviors.unhandled |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
def moving( |
|
|
def moving( |
|
|
state: State, |
|
|
state: State, |
|
|
targetPos: ImVector3f, |
|
|
targetPos: ImVector3f, |
|
|
movementTimer: ActorRef[GenericTimerActor.Command] |
|
|
|
|
|
|
|
|
signal: CancelableFuture[NpcMovementActor2.DoneMoving.type] |
|
|
): Behavior[NpcActorSupervisor.Command] = |
|
|
): Behavior[NpcActorSupervisor.Command] = |
|
|
|
|
|
Behaviors.setup { _ => |
|
|
|
|
|
movementTimer ! GenericTimerActor.Start |
|
|
|
|
|
|
|
|
|
|
|
// ctx |
|
|
|
|
|
// .ask(state.npcMovementActor, NpcMovementActor2.MoveTo(targetPos, _))( |
|
|
|
|
|
// _.fold(LogError(_), MovementResponse(_)) |
|
|
|
|
|
// ) |
|
|
|
|
|
ctx.pipeToSelf(signal) { |
|
|
|
|
|
case Success(value) => DoneMoving |
|
|
|
|
|
case Failure(exception) => LogError(exception) |
|
|
|
|
|
} |
|
|
Behaviors.receiveMessagePartial[Command] { |
|
|
Behaviors.receiveMessagePartial[Command] { |
|
|
case LogError(err) => |
|
|
case LogError(err) => |
|
|
ctx.log.warn(err.getMessage()) |
|
|
|
|
|
|
|
|
ctx.log.error(err.getMessage()) |
|
|
Behaviors.same |
|
|
Behaviors.same |
|
|
case Move(pos) => moving(state, pos, movementTimer) |
|
|
|
|
|
case UpdatePosition(pos) => |
|
|
|
|
|
ctx.log.trace("Current pos = " + state.currentPos.toString()) |
|
|
|
|
|
moving(state.copy(currentPos = pos), targetPos, movementTimer) |
|
|
|
|
|
case MovementTick => |
|
|
|
|
|
val dst = ImVector3f.dst(targetPos, state.currentPos) |
|
|
|
|
|
if (dst <= 10f) { |
|
|
|
|
|
state.npcMovementActor ! NpcMovementActor2.StopMoving |
|
|
|
|
|
|
|
|
case m @ Move(pos) => |
|
|
movementTimer ! GenericTimerActor.Stop |
|
|
movementTimer ! GenericTimerActor.Stop |
|
|
idle(state) |
|
|
|
|
|
} else { |
|
|
|
|
|
// ctx.log.debug("Difference = " + dst.toString()) |
|
|
|
|
|
// ctx.log.debug("Current pos = " + state.currentPos.toString()) |
|
|
|
|
|
|
|
|
|
|
|
ctx.ask(state.npcMovementActor, NpcMovementActor2.AskPosition(_)) { |
|
|
|
|
|
case Success(value) => |
|
|
|
|
|
UpdatePosition(value) |
|
|
|
|
|
|
|
|
children.npcMovementActor ! NpcMovementActor2.StopMoving |
|
|
|
|
|
signal.cancel() |
|
|
|
|
|
ctx.ask( |
|
|
|
|
|
children.npcMovementActor, |
|
|
|
|
|
NpcMovementActor2.MoveTo(pos, _) |
|
|
|
|
|
) { |
|
|
|
|
|
case Success(signal) => InternalMove(m, signal) |
|
|
case Failure(exception) => LogError(exception) |
|
|
case Failure(exception) => LogError(exception) |
|
|
} |
|
|
} |
|
|
// Behaviors.same |
|
|
|
|
|
moving(state, targetPos, movementTimer) |
|
|
|
|
|
|
|
|
Behaviors.same |
|
|
|
|
|
case InternalMove(move, signal) => |
|
|
|
|
|
moving(state, targetPos, signal) |
|
|
|
|
|
case NoOp => Behaviors.same |
|
|
|
|
|
// case MovementResponse(x: CancelableFuture[_]) => |
|
|
|
|
|
// // ctx.pipeToSelf(x)(_.) |
|
|
|
|
|
case DoneMoving => |
|
|
|
|
|
movementTimer ! GenericTimerActor.Stop |
|
|
|
|
|
idle(state) |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
object NpcMovementActor2 { |
|
|
object NpcMovementActor2 { |
|
|
|
|
|
|
|
|
|
|
|
case object DoneMoving |
|
|
|
|
|
|
|
|
sealed trait Command |
|
|
sealed trait Command |
|
|
case class AskPosition(replyTo: ActorRef[ImVector3f]) extends Command |
|
|
case class AskPosition(replyTo: ActorRef[ImVector3f]) extends Command |
|
|
|
|
|
case object MovementTick extends Command |
|
|
case object StopMoving extends Command |
|
|
case object StopMoving extends Command |
|
|
case class Move(target: ImVector3f) extends Command |
|
|
|
|
|
|
|
|
case class MoveTo( |
|
|
|
|
|
target: ImVector3f, |
|
|
|
|
|
doneSignal: ActorRef[CancelableFuture[DoneMoving.type]] |
|
|
|
|
|
) extends Command |
|
|
|
|
|
|
|
|
final class Props[T: CanMove]( |
|
|
final class Props[T: CanMove]( |
|
|
val enqueueR: Function1[() => Unit, Unit], |
|
|
val enqueueR: Function1[() => Unit, Unit], |
|
|
val initialPos: ImVector3f, |
|
|
val initialPos: ImVector3f, |
|
|
val tickEventBus: GameEventBus[TickEvent], |
|
|
|
|
|
|
|
|
// val tickEventBus: GameEventBus[TickEvent], |
|
|
val movable: T |
|
|
val movable: T |
|
|
) { |
|
|
) { |
|
|
def create = |
|
|
def create = |
|
|
Behaviors.setup[Command] { ctx => |
|
|
Behaviors.setup[Command] { ctx => |
|
|
new NpcMovementActor2(ctx, this).receive(State(initialPos)) |
|
|
|
|
|
|
|
|
new NpcMovementActor2(ctx, this).receive(State()) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
final case class State(currentPos: ImVector3f) |
|
|
|
|
|
|
|
|
final case class State() |
|
|
} |
|
|
} |
|
|
class NpcMovementActor2[T]( |
|
|
class NpcMovementActor2[T]( |
|
|
ctx: ActorContext[NpcMovementActor2.Command], |
|
|
ctx: ActorContext[NpcMovementActor2.Command], |
|
|
props: NpcMovementActor2.Props[T] |
|
|
props: NpcMovementActor2.Props[T] |
|
|
) { |
|
|
|
|
|
|
|
|
)(implicit cm: CanMove[T]) { |
|
|
import NpcMovementActor2._ |
|
|
import NpcMovementActor2._ |
|
|
|
|
|
|
|
|
|
|
|
def location = cm.location(props.movable) |
|
|
|
|
|
|
|
|
def receive( |
|
|
def receive( |
|
|
state: State |
|
|
state: State |
|
|
)(implicit cm: CanMove[T]): Behavior[NpcMovementActor2.Command] = |
|
|
|
|
|
Behaviors.receiveMessage[Command] { |
|
|
|
|
|
|
|
|
): Behavior[NpcMovementActor2.Command] = |
|
|
|
|
|
Behaviors.receiveMessagePartial { |
|
|
case AskPosition(replyTo) => |
|
|
case AskPosition(replyTo) => |
|
|
replyTo ! cm.location(props.movable) |
|
|
|
|
|
|
|
|
replyTo ! location |
|
|
Behaviors.same |
|
|
Behaviors.same |
|
|
case Move(target: ImVector3f) => |
|
|
|
|
|
props.enqueueR(() => |
|
|
|
|
|
cm.move(props.movable, (target - state.currentPos) * 0.005f) |
|
|
|
|
|
|
|
|
case StopMoving => |
|
|
|
|
|
ctx.log.debug( |
|
|
|
|
|
"Position at Stop = " + location.toString |
|
|
) |
|
|
) |
|
|
receive(state = state.copy(currentPos = cm.location(props.movable))) |
|
|
|
|
|
|
|
|
props.enqueueR(() => cm.stop(props.movable)) |
|
|
|
|
|
receive(state) |
|
|
|
|
|
case MoveTo( |
|
|
|
|
|
target: ImVector3f, |
|
|
|
|
|
replyTo: ActorRef[CancelableFuture[DoneMoving.type]] |
|
|
|
|
|
) => |
|
|
|
|
|
props.enqueueR(() => cm.move(props.movable, target - location)) |
|
|
|
|
|
val p = CancelablePromise[DoneMoving.type]() |
|
|
|
|
|
replyTo ! p.future |
|
|
|
|
|
ticking(p, target, state) |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
def ticking( |
|
|
|
|
|
reachDestination: CancelablePromise[DoneMoving.type], |
|
|
|
|
|
targetPos: ImVector3f, |
|
|
|
|
|
state: State |
|
|
|
|
|
): Behavior[NpcMovementActor2.Command] = |
|
|
|
|
|
Behaviors.receiveMessagePartial { |
|
|
case StopMoving => |
|
|
case StopMoving => |
|
|
ctx.log.debug( |
|
|
ctx.log.debug( |
|
|
"Position at Stop = " + cm.location(props.movable).toString |
|
|
|
|
|
|
|
|
"Position at Stop = " + location.toString |
|
|
) |
|
|
) |
|
|
props.enqueueR(() => cm.stop(props.movable)) |
|
|
props.enqueueR(() => cm.stop(props.movable)) |
|
|
receive(state = state.copy(currentPos = cm.location(props.movable))) |
|
|
|
|
|
|
|
|
receive(state) |
|
|
|
|
|
|
|
|
|
|
|
case MovementTick => |
|
|
|
|
|
val dst = ImVector3f.dst(targetPos, location) |
|
|
|
|
|
if (dst <= 10f) { |
|
|
|
|
|
ctx.self ! StopMoving |
|
|
|
|
|
reachDestination.success(DoneMoving) |
|
|
|
|
|
receive(state) |
|
|
|
|
|
} else { |
|
|
|
|
|
ctx.log.trace("Difference = " + dst.toString()) |
|
|
|
|
|
ctx.log.trace("Current pos = " + location.toString()) |
|
|
|
|
|
Behaviors.same |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|