package wow.doge.mygame.subsystems.movement import akka.actor.typed.Behavior import akka.actor.typed.PostStop import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors import com.jme3.math.Vector3f import com.jme3.renderer.Camera import com.jme3.scene.Geometry import com.softwaremill.quicklens._ import wow.doge.mygame.game.subsystems.movement.CanMove import wow.doge.mygame.implicits._ import wow.doge.mygame.math.ImVector3f final case class CardinalDirection( left: Boolean = false, right: Boolean = false, up: Boolean = false, down: Boolean = false ) sealed trait RotateDir object RotateDir { case object Left extends RotateDir case object Right extends RotateDir } object ImMovementActor { sealed trait Command case object Tick extends Command sealed trait Movement extends Command final case class MoveLeft(pressed: Boolean) extends Movement final case class MoveUp(pressed: Boolean) extends Movement final case class MoveRight(pressed: Boolean) extends Movement final case class MoveDown(pressed: Boolean) extends Movement case object Jump extends Movement class Props( val enqueueR: Function1[() => Unit, Unit], // playerMovementEventBus: ActorRef[ // EventBus.Command[PlayerMovementEvent] // ] val camera: Camera ) { def behavior[T: CanMove](movable: T): Behavior[Command] = Behaviors.setup(ctx => new ImMovementActor(ctx, this, movable).receive(State(), new Vector3f) ) } /** * Internal state of the actor * * @param cardinalDir The four directions the character can move */ final case class State(cardinalDir: CardinalDirection = CardinalDirection()) } class ImMovementActor[T]( ctx: ActorContext[ImMovementActor.Command], props: ImMovementActor.Props, val movable: T )(implicit cm: CanMove[T]) { import ImMovementActor._ def receive( state: ImMovementActor.State, walkDirBuf: Vector3f ): Behavior[Command] = Behaviors .receiveMessage[Command] { case m: Movement => m match { case MoveLeft(pressed) => stopIfNotPressed(pressed) receive( state = state.modify(_.cardinalDir.left).setTo(pressed), walkDirBuf ) case MoveUp(pressed) => stopIfNotPressed(pressed) receive( state = state.modify(_.cardinalDir.up).setTo(pressed), walkDirBuf ) case MoveRight(pressed) => stopIfNotPressed(pressed) receive( state = state.modify(_.cardinalDir.right).setTo(pressed), walkDirBuf ) case MoveDown(pressed) => stopIfNotPressed(pressed) receive( state = state.modify(_.cardinalDir.down).setTo(pressed), walkDirBuf ) case Jump => cm.jump(movable) Behaviors.same } case Tick => val camDir = props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f) val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f) val dir = state.cardinalDir walkDirBuf.set(0, 0, 0) if (dir.up) { ctx.log.traceP("up") walkDirBuf += camDir } if (dir.left) { ctx.log.traceP("left") walkDirBuf += camLeft } if (dir.right) { ctx.log.traceP("right") walkDirBuf += -camLeft } if (dir.down) { ctx.log.traceP("down") walkDirBuf += -camDir } walkDirBuf *= 25f *= (1f / 144) cm.move(movable, walkDirBuf.immutable, 20f) Behaviors.same } .receiveSignal { case (_, PostStop) => ctx.log.debugP("Stopped") Behaviors.same } def stopIfNotPressed(pressed: Boolean) = if (!pressed) cm.stop(movable) def getDirection2( cardinalDir: CardinalDirection // debug: sourcecode.Text[String] => sourcecode.Text[String] ) = { val camDir = props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f) val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f) val dir = cardinalDir val walkDir = { val mutWalkDir = new Vector3f() if (dir.up) { ctx.log.traceP("up") mutWalkDir += camDir } if (dir.left) { ctx.log.traceP("left") mutWalkDir += camLeft } if (dir.right) { ctx.log.traceP("right") mutWalkDir += -camLeft } if (dir.down) { ctx.log.traceP("down") mutWalkDir += -camDir } mutWalkDir.immutable } walkDir } } // old/unused object MovementActor { sealed trait Command case object Tick extends Command sealed trait Movement extends Command final case class MovedLeft(pressed: Boolean) extends Movement final case class MovedUp(pressed: Boolean) extends Movement final case class MovedRight(pressed: Boolean) extends Movement final case class MovedDown(pressed: Boolean) extends Movement class Props(val app: com.jme3.app.Application, val geom: Geometry) /** * Internal state of the actor * * @param cardinalDir Immutable, can be shared as is * @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor */ final case class State( cardinalDir: CardinalDirection = CardinalDirection(), walkDirection: Vector3f = Vector3f.UNIT_X ) def apply(props: Props): Behavior[Command] = Behaviors.setup(ctx => new MovementActor(ctx, props).receive(State())) } class MovementActor( ctx: ActorContext[MovementActor.Command], props: MovementActor.Props ) { import MovementActor._ import com.softwaremill.quicklens._ def receive(state: MovementActor.State): Behavior[Command] = Behaviors.receiveMessage { msg => msg match { case m: Movement => m match { case MovedLeft(pressed) => receive(state = state.modify(_.cardinalDir.left).setTo(pressed)) case MovedUp(pressed) => receive(state = state.modify(_.cardinalDir.up).setTo(pressed)) case MovedRight(pressed) => receive(state = state.modify(_.cardinalDir.right).setTo(pressed)) case MovedDown(pressed) => receive(state = state.modify(_.cardinalDir.down).setTo(pressed)) } case Tick => val camDir = props.app.getCamera.getDirection().clone().multLocal(0.6f) val camLeft = props.app.getCamera.getLeft().clone().multLocal(0.4f) val walkDir = state.walkDirection.set(0, 0, 0) // val walkDir = new Vector3f val dir = state.cardinalDir if (dir.up) { ctx.log.debugP("up") // ctx.log.debugP(Thread.currentThread().getName()) // walkDir.addLocal(0, 0, -1) walkDir += camDir } if (dir.left) { ctx.log.debugP("left") // walkDir.addLocal(-1, 0, 0) walkDir.addLocal(camLeft) } if (dir.right) { ctx.log.debugP("right") // walkDir.addLocal(1, 0, 0) walkDir.addLocal(camLeft.negateLocal()) } if (dir.down) { ctx.log.debugP("down") walkDir.addLocal(camDir.negateLocal()) // walkDir.addLocal(0, 0, 1) } // (dir.up, dir.down, dir.left, dir.right) match { // case (true, false, true, false) => // case _ => // } walkDir.multLocal(2f) // walkDir.multLocal(100f) // .multLocal(tpf) // val v = props.geom.getLocalTranslation() // props.geom.setLocalTranslation( // (v += walkDir) // ) props.app.enqueue(new Runnable { override def run(): Unit = { // geom.setLocalTranslation(walkDir) val v = props.geom.getLocalTranslation() props.geom.setLocalTranslation( (v += walkDir) ) } }) Behaviors.same // receive(state = state.modify(_.walkDirection).setTo(walkDir)) } } }