Skip to content

Commit

Permalink
add player movement and scrolling
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-arold committed Mar 28, 2019
1 parent d8e7f78 commit 3779a0e
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object GameConfig {
const val WINDOW_WIDTH = 80
const val WINDOW_HEIGHT = 50

val WORLD_SIZE = Sizes.create3DSize(WINDOW_WIDTH - SIDEBAR_WIDTH, WINDOW_HEIGHT - LOG_AREA_HEIGHT, DUNGEON_LEVELS)
val WORLD_SIZE = Sizes.create3DSize(WINDOW_WIDTH * 2, WINDOW_HEIGHT * 2 , DUNGEON_LEVELS)

fun buildAppConfig() = AppConfigs.newConfig()
.enableBetaFeatures()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.attributes.EntityPosition
import org.hexworks.cavesofzircon.attributes.EntityTile
import org.hexworks.cavesofzircon.attributes.types.Player
import org.hexworks.cavesofzircon.systems.CameraMover
import org.hexworks.cavesofzircon.systems.InputReceiver
import org.hexworks.cavesofzircon.systems.Movable
import org.hexworks.cavesofzircon.world.GameContext

fun <T : EntityType> newGameEntityOfType(type: T, init: EntityBuilder<T, GameContext>.() -> Unit) =
Expand All @@ -15,7 +18,7 @@ object EntityFactory {

fun newPlayer() = newGameEntityOfType(Player) {
attributes(EntityPosition(), EntityTile(GameTileRepository.PLAYER))
behaviors()
facets()
behaviors(InputReceiver)
facets(Movable, CameraMover)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.hexworks.cavesofzircon.commands

enum class CameraMoveDirection {
UP,
DOWN,
LEFT,
RIGHT
}
11 changes: 11 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/commands/MoveCamera.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.hexworks.cavesofzircon.commands

import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.extensions.GameCommand
import org.hexworks.cavesofzircon.extensions.GameEntity
import org.hexworks.cavesofzircon.world.GameContext
import org.hexworks.zircon.api.data.impl.Position3D

data class MoveCamera(override val context: GameContext,
override val source: GameEntity<EntityType>,
val previousPosition: Position3D) : GameCommand<EntityType>
14 changes: 14 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/commands/MoveTo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.hexworks.cavesofzircon.commands

import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.extensions.GameCommand
import org.hexworks.cavesofzircon.extensions.GameEntity
import org.hexworks.cavesofzircon.world.GameContext
import org.hexworks.zircon.api.data.impl.Position3D

/**
* A [GameCommand] representing moving [source] to [position].
*/
data class MoveTo(override val context: GameContext,
override val source: GameEntity<EntityType>,
val position: Position3D) : GameCommand<EntityType>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.hexworks.cavesofzircon.extensions

import org.hexworks.amethyst.api.Command
import org.hexworks.amethyst.api.entity.Entity
import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.world.GameContext
Expand All @@ -13,3 +14,8 @@ typealias AnyGameEntity = Entity<EntityType, GameContext>
* Specializes [Entity] with our [GameContext] type.
*/
typealias GameEntity<T> = Entity<T, GameContext>

/**
* Specializes [Command] with our [GameContext] type.
*/
typealias GameCommand<T> = Command<T, GameContext>
36 changes: 36 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/systems/CameraMover.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.hexworks.cavesofzircon.systems

import org.hexworks.amethyst.api.Consumed
import org.hexworks.amethyst.api.base.BaseFacet
import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.commands.MoveCamera
import org.hexworks.cavesofzircon.extensions.GameCommand
import org.hexworks.cavesofzircon.extensions.position
import org.hexworks.cavesofzircon.world.GameContext

object CameraMover : BaseFacet<GameContext>() {

override fun executeCommand(command: GameCommand<out EntityType>) = command.responseWhenCommandIs(MoveCamera::class) { cmd ->
val (context, source, previousPosition) = cmd
val world = context.world
val screenPos = source.position - world.visibleOffset() // 1
val halfHeight = world.visibleSize().yLength / 2 // 2
val halfWidth = world.visibleSize().xLength / 2 // 3
val currentPosition = source.position
when { // 4
previousPosition.y > currentPosition.y && screenPos.y < halfHeight -> {
world.scrollOneBackward()
}
previousPosition.y < currentPosition.y && screenPos.y > halfHeight -> {
world.scrollOneForward()
}
previousPosition.x > currentPosition.x && screenPos.x < halfWidth -> {
world.scrollOneLeft()
}
previousPosition.x < currentPosition.x && screenPos.x > halfWidth -> {
world.scrollOneRight()
}
}
Consumed
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.hexworks.cavesofzircon.systems

import org.hexworks.amethyst.api.base.BaseBehavior
import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.commands.MoveTo
import org.hexworks.cavesofzircon.extensions.GameEntity
import org.hexworks.cavesofzircon.extensions.position
import org.hexworks.cavesofzircon.world.GameContext
import org.hexworks.zircon.api.uievent.KeyCode
import org.hexworks.zircon.api.uievent.KeyboardEvent

object InputReceiver : BaseBehavior<GameContext>() {

override fun update(entity: GameEntity<out EntityType>, context: GameContext): Boolean {
val (_, _, uiEvent, player) = context
val currentPos = player.position
if (uiEvent is KeyboardEvent) {
val newPosition = when (uiEvent.code) {
KeyCode.KEY_W -> currentPos.withRelativeY(-1)
KeyCode.KEY_A -> currentPos.withRelativeX(-1)
KeyCode.KEY_S -> currentPos.withRelativeY(1)
KeyCode.KEY_D -> currentPos.withRelativeX(1)
else -> {
currentPos
}
}
player.executeCommand(MoveTo(context, player, newPosition))
}
return true
}
}
32 changes: 32 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/systems/Movable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.hexworks.cavesofzircon.systems

import org.hexworks.amethyst.api.CommandResponse
import org.hexworks.amethyst.api.Consumed
import org.hexworks.amethyst.api.Pass
import org.hexworks.amethyst.api.Response
import org.hexworks.amethyst.api.base.BaseFacet
import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.attributes.types.Player
import org.hexworks.cavesofzircon.commands.MoveCamera
import org.hexworks.cavesofzircon.commands.MoveTo
import org.hexworks.cavesofzircon.extensions.GameCommand
import org.hexworks.cavesofzircon.extensions.position
import org.hexworks.cavesofzircon.world.GameContext

object Movable : BaseFacet<GameContext>() {

override fun executeCommand(command: GameCommand<out EntityType>) = command.responseWhenCommandIs(MoveTo::class) { (context, entity, position) ->
val world = context.world
val previousPosition = entity.position // 1
var result: Response = Pass
if (world.moveEntity(entity, position)) {
result = if (entity.type == Player) { // 2
CommandResponse(MoveCamera( // 3
context = context,
source = entity,
previousPosition = previousPosition))
} else Consumed
}
result
}
}
8 changes: 8 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/view/PlayView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,23 @@ import org.hexworks.zircon.api.Components
import org.hexworks.zircon.api.GameComponents
import org.hexworks.zircon.api.component.ComponentAlignment
import org.hexworks.zircon.api.data.Tile
import org.hexworks.zircon.api.extensions.onKeyboardEvent
import org.hexworks.zircon.api.game.ProjectionMode
import org.hexworks.zircon.api.mvc.base.BaseView
import org.hexworks.zircon.api.uievent.KeyboardEventType
import org.hexworks.zircon.api.uievent.Processed

class PlayView(private val game: Game = GameBuilder.defaultGame()) : BaseView() {

override val theme = GameConfig.THEME

override fun onDock() {

screen.onKeyboardEvent(KeyboardEventType.KEY_PRESSED) { event, _ ->
game.world.update(screen, event, game)
Processed
}

val sidebar = Components.panel()
.withSize(GameConfig.SIDEBAR_WIDTH, GameConfig.WINDOW_HEIGHT)
.wrapWithBox()
Expand Down
35 changes: 27 additions & 8 deletions src/main/kotlin/org/hexworks/cavesofzircon/world/World.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import org.hexworks.zircon.api.data.Tile
import org.hexworks.zircon.api.data.impl.Position3D
import org.hexworks.zircon.api.data.impl.Size3D
import org.hexworks.zircon.api.game.GameArea
import org.hexworks.zircon.api.screen.Screen
import org.hexworks.zircon.api.uievent.UIEvent

class World(startingBlocks: Map<Position3D, GameBlock>,
visibleSize: Size3D,
Expand All @@ -39,11 +41,14 @@ class World(startingBlocks: Map<Position3D, GameBlock>,
}
}

/**
* Adds the given [Entity] at the given [Position3D].
* Has no effect if this world already contains the
* given [Entity].
*/
fun update(screen: Screen, uiEvent: UIEvent, game: Game) {
engine.update(GameContext(
world = this,
screen = screen,
uiEvent = uiEvent,
player = game.player))
}

fun addEntity(entity: Entity<EntityType, GameContext>, position: Position3D) {
entity.position = position
engine.addEntity(entity)
Expand All @@ -52,6 +57,23 @@ class World(startingBlocks: Map<Position3D, GameBlock>,
}
}

fun moveEntity(entity: GameEntity<EntityType>, position: Position3D): Boolean {
var success = false
val oldBlock = fetchBlockAt(entity.position)
val newBlock = fetchBlockAt(position)

if (bothBlocksPresent(oldBlock, newBlock)) {
success = true
oldBlock.get().removeEntity(entity)
entity.position = position
newBlock.get().addEntity(entity)
}
return success
}

private fun bothBlocksPresent(oldBlock: Maybe<GameBlock>, newBlock: Maybe<GameBlock>) =
oldBlock.isPresent && newBlock.isPresent

fun addAtEmptyPosition(entity: GameEntity<EntityType>,
offset: Position3D = Positions.default3DPosition(),
size: Size3D = actualSize()): Boolean {
Expand All @@ -66,9 +88,6 @@ class World(startingBlocks: Map<Position3D, GameBlock>,

}

/**
* Finds an empty location within the given area (offset and size) on this [World].
*/
fun findEmptyLocationWithin(offset: Position3D, size: Size3D): Maybe<Position3D> {
var position = Maybe.empty<Position3D>()
val maxTries = 10
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<root level="WARN">
<appender-ref ref="STDOUT" />
</root>

<logger name="org.hexworks.cavesofzircon" level="INFO" />

</configuration>

0 comments on commit 3779a0e

Please sign in to comment.