Skip to content

Commit

Permalink
Resolves #75: New info sidebar (#95)
Browse files Browse the repository at this point in the history
* Moved views to views package

* Added InputSidebar, need to move stuff from GameView there

* Added new sidebar (empty for now) and moved left sidebar into its own class

* moved mouse pos fragment to right sidebar

* rudimentary mouse handling in InfoSidebar

* Moved logArea to the top to see how it feels

* Created lockable fragment to display information about a tile

* updated codestyle (accidentially...?)

* created new module and basic interface

* Created default implementation of FrontendString

* FrontendStrings can be added ("bindPlusWith"), extended tests

* removed comments in CoordinateAtMousePosition

* Moved strings module one level up, started building CoordinateFrontendString

* Renamed module strings to ui-strings

* fixed packaging of ui pom

* Changed how "transform" works, renamed Length

* Created test for CoordinateFrontendString

* Fixed NPE in DefaultFrontendString

* Fixed DefaultFrontendStringTest

* fixed typo

* Renamed ObjectFormatter to Format

* Minor improvements

* Fixed errors after merge

* Created TerrainFrontendString (including test)

* Fixed test: sidebar width is less now

* Removed old frontendstring stuff

* Fixed DefaultTransformationTest because the sidebar width changed

* Fixed compile error

* transformations for terrain

* Created test for terrain transformations to not accidentally have erroneous ones

* coordinate fragment is also lockable

* Toggle locked state of info sidebar with right click

* Beginning of the element info fragment

* Added icon to TerrainDetailsFragment

* Added simple element info

* Improved sidebar layout, added docs

* Small cleanups

* removed initial text from terrainDesciption

because withFrontendString() updates on bind

* Added dummy MarkersFragment

* "fixed" mapOffset bug

* simplified update of absolutePosition

* git ignores logs

* use world of model not ui

* use data binding instead of overridden method

* use CallSign.name for display

* place logArea code first

* GameView connects InputSidebar to GameComponent

* GameView connects InfoSidebar to GameComponent

* updated screenshot

* Improved logback.xml

* better short names for water

* Better panel borders, improved time display

* Improved documentation in InfoSidebar

* You can now pause the game! Whoohoo!

with space or P

* Show element icon in sidebar

* Opt in to ExperimentalTime

* added TODO in ElementInfoFragment

* Better highlight when game is paused

* Deleted unused fragments

* Unified look and feel of the sidebar

* Updated map screenshot

Co-authored-by: Loomie <[email protected]>
  • Loading branch information
Baret and Loomie authored Nov 19, 2020
1 parent 00d5df3 commit 28f067c
Show file tree
Hide file tree
Showing 37 changed files with 962 additions and 242 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
target/
logs/

# IntelliJ
.DS_Store
Expand Down
Binary file modified artwork/screenshots/map.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import de.gleex.pltcmd.game.engine.entities.types.inConversationWith
import de.gleex.pltcmd.game.options.GameOptions
import de.gleex.pltcmd.game.options.UiOptions
import de.gleex.pltcmd.game.ticks.Ticker
import de.gleex.pltcmd.game.ui.fragments.TickFragment
import de.gleex.pltcmd.game.ui.fragments.TilesetSelectorFragment
import de.gleex.pltcmd.game.ui.fragments.GameTimeFragment
import de.gleex.pltcmd.model.elements.Affiliation
import de.gleex.pltcmd.model.elements.CallSign
import de.gleex.pltcmd.model.elements.Elements
Expand Down Expand Up @@ -166,9 +165,7 @@ fun buildUI(hqSender: ElementEntity, bravoSender: ElementEntity, charlieSender:
withSize(sideBarWidth, contentSize.height).
build().
apply {
addFragment(TickFragment(sideBarWidth))
// TESTING
addFragment(TilesetSelectorFragment(sideBarWidth, hBox, logArea))
addFragment(GameTimeFragment(sideBarWidth))
})

// RadioCommunicator panels
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import de.gleex.pltcmd.game.engine.entities.types.element
import de.gleex.pltcmd.game.options.GameOptions
import de.gleex.pltcmd.game.options.UiOptions
import de.gleex.pltcmd.game.ticks.Ticker
import de.gleex.pltcmd.game.ui.GameView
import de.gleex.pltcmd.game.ui.GeneratingView
import de.gleex.pltcmd.game.ui.MapGenerationProgressController
import de.gleex.pltcmd.game.ui.TitleView
import de.gleex.pltcmd.game.ui.entities.GameWorld
import de.gleex.pltcmd.game.ui.mapgeneration.MapGenerationProgressController
import de.gleex.pltcmd.game.ui.views.GameView
import de.gleex.pltcmd.game.ui.views.GeneratingView
import de.gleex.pltcmd.game.ui.views.TitleView
import de.gleex.pltcmd.model.elements.Affiliation
import de.gleex.pltcmd.model.elements.CallSign
import de.gleex.pltcmd.model.elements.CommandingElement
Expand Down Expand Up @@ -74,7 +74,7 @@ open class Main {

screen.dock(GameView(gameWorld, tileGrid, game, hq, elementsToCommand))

Ticker.start(GameOptions.TickRate.duration, GameOptions.TickRate.timeUnit)
Ticker.start()
// cleanup
screen.onShutdown { Ticker.stop() }
}
Expand Down
26 changes: 25 additions & 1 deletion game/application/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
<configuration scan="true" scanPeriod="30 seconds">

<jmxConfigurator/>

<timestamp key="bySecond" datePattern="yyyyMMdd_HHmmss"/>

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

<appender name="ELEMENTS_PERFORMANCE" class="ch.qos.logback.core.FileAppender">
<file>logs/allElements-${bySecond}.log</file>
<append>false</append>
<immediateFlush>false</immediateFlush>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACE</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>

<encoder>
<pattern>[%d{HH:mm:ss.SSS}] [%thread] - %msg%n</pattern>
</encoder>
</appender>

<logger name="org.hexworks.zircon" level="WARN"/>
<logger name="org.hexworks.cobalt" level="WARN"/>
<!-- <logger name="org.hexworks.amethyst.api.entity.Entity" level="DEBUG"/>-->
<!-- <logger name="org.hexworks.amethyst.api.entity.Entity" level="DEBUG"/>-->

<logger name="de.gleex.pltcmd.game.application" level="DEBUG"/>
<!-- <logger name="de.gleex.pltcmd.game.engine.systems" level="DEBUG"/>-->

<logger name="de.gleex.pltcmd.game.engine.Game" level="TRACE" additivity="false">
<appender-ref ref="ELEMENTS_PERFORMANCE"/>
</logger>

<root level="INFO">
<appender-ref ref="STDOUT"/>
Expand Down
19 changes: 15 additions & 4 deletions game/engine/src/main/kotlin/de/gleex/pltcmd/game/engine/Game.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package de.gleex.pltcmd.game.engine

import de.gleex.pltcmd.game.engine.entities.EntityFactory
import de.gleex.pltcmd.game.engine.entities.toEntity
import de.gleex.pltcmd.game.engine.entities.types.ElementEntity
import de.gleex.pltcmd.game.engine.entities.types.ElementType
import de.gleex.pltcmd.game.engine.entities.types.callsign
import de.gleex.pltcmd.game.engine.entities.types.onDefeat
import de.gleex.pltcmd.game.engine.entities.types.*
import de.gleex.pltcmd.game.engine.extensions.GameEntity
import de.gleex.pltcmd.game.options.GameOptions
import de.gleex.pltcmd.game.ticks.Ticker
Expand All @@ -22,6 +19,8 @@ import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cobalt.databinding.api.extension.toProperty
import org.hexworks.cobalt.logging.api.LoggerFactory
import kotlin.random.Random
import kotlin.time.ExperimentalTime
import kotlin.time.measureTimedValue

data class Game(val engine: Engine<GameContext>, val world: WorldMap, val random: Random) {

Expand Down Expand Up @@ -85,4 +84,16 @@ data class Game(val engine: Engine<GameContext>, val world: WorldMap, val random
return addEntity(elementEntity)
}

/**
* Gets all [ElementEntity]s at the given coordinate.
*/
@OptIn(ExperimentalTime::class)
fun elementsAt(coordinate: Coordinate): Collection<ElementEntity> {
val (elements, duration) = measureTimedValue {
allElements.filter { it.currentPosition == coordinate }
}
log.trace("Finding ${elements.size} of ${allElements.size} elements at $coordinate took ${duration.inMilliseconds} ms")
return elements
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ object UiOptions {
val DEFAULT_TILESET: TilesetResource = CP437TilesetResources.bisasam16x16()
val MAP_TILESET: TilesetResource = CP437TilesetResources.guybrush16x16()

const val WINDOW_WIDTH = 90
const val WINDOW_HEIGHT = 63
/**
* The full width of the sidebars. The content size is probably this - 2.
*/
const val SIDEBAR_WIDTH = 33

const val MAP_VIEW_WDTH = 53
const val MAP_VIEW_WIDTH = 53
const val MAP_VIEW_HEIGHT = 53
const val LOG_AREA_HEIGHT = WINDOW_HEIGHT - MAP_VIEW_HEIGHT
const val LOG_AREA_WIDTH = WINDOW_WIDTH
const val INTERFACE_PANEL_WIDTH = WINDOW_WIDTH - MAP_VIEW_WDTH
const val INTERFACE_PANEL_HEIGHT = WINDOW_HEIGHT - LOG_AREA_HEIGHT

const val WINDOW_WIDTH = MAP_VIEW_WIDTH + (2 * SIDEBAR_WIDTH)
const val WINDOW_HEIGHT = 64

const val SKIP_INTRO = true
}
4 changes: 4 additions & 0 deletions game/ticks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
<groupId>de.gleex.pltcmd.util</groupId>
<artifactId>events</artifactId>
</dependency>
<dependency>
<groupId>de.gleex.pltcmd.game</groupId>
<artifactId>options</artifactId>
</dependency>
</dependencies>

</project>
67 changes: 57 additions & 10 deletions game/ticks/src/main/kotlin/de/gleex/pltcmd/game/ticks/Ticker.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package de.gleex.pltcmd.game.ticks

import de.gleex.pltcmd.game.options.GameConstants
import de.gleex.pltcmd.game.options.GameOptions
import de.gleex.pltcmd.util.events.globalEventBus
import org.hexworks.cobalt.databinding.api.binding.bindTransform
import org.hexworks.cobalt.databinding.api.extension.createPropertyFrom
import org.hexworks.cobalt.databinding.api.extension.toProperty
import org.hexworks.cobalt.databinding.api.property.Property
import org.hexworks.cobalt.databinding.api.value.ObservableValue
import org.hexworks.cobalt.events.api.EventBus
Expand Down Expand Up @@ -31,17 +35,21 @@ object Ticker {
private val _currentTickProperty = createPropertyFrom(TickId(0))

private val _currentTimeProperty: Property<LocalTime>
private val _currentTimeStringProperty: Property<String>

private val executor = Executors.newScheduledThreadPool(1)
private val _currentDayProperty: Property<Int> = 0.toProperty { newDay -> newDay > 0 }

private val _isPausedProperty: Property<Boolean> = true.toProperty()

private var executor = newExecutor()

init {
val initialTime = LocalTime.of(5, 59)
val initialTime = LocalTime.of(23, 50)
_currentTimeProperty = createPropertyFrom(initialTime)
_currentTimeStringProperty = createPropertyFrom(initialTime.toString()).
apply {
updateFrom(_currentTimeProperty, false) { localTime -> localTime.toString()}
}
_currentTimeProperty.onChange { changedTime ->
if(changedTime.newValue.toSecondOfDay() == 0) {
_currentDayProperty.transformValue { it + 1 }
}
}
}

/**
Expand All @@ -51,32 +59,71 @@ object Ticker {
/**
* The current ingame time converted to a string as [ObservableValue] so that it is possible to listen for changes.
*/
val currentTimeString: ObservableValue<String> = _currentTimeStringProperty
val currentTimeString: ObservableValue<String> = _currentTimeProperty.bindTransform { localTime -> localTime.toString() }
/**
* The current ingame day starting at "day 0".
*/
val currentDay: ObservableValue<Int> = _currentDayProperty
/**
* The [currentTick] but observable.
*/
val currentTickObservable: ObservableValue<TickId> = _currentTickProperty

/**
* Observable flag to see if the game is currently paused.
*/
val isPaused: ObservableValue<Boolean> = _isPausedProperty

/**
* Increases the current tick, publishes the corresponding [TickEvent].
*/
fun tick() {
private fun tick() {
_currentTickProperty.value = nextTick
_currentTimeProperty.updateValue(_currentTimeProperty.value.plusMinutes(1))
_currentTimeProperty.updateValue(
_currentTimeProperty
.value
.plusSeconds(GameConstants.Time.secondsSimulatedPerTick.toLong()))
log.debug(" - TICK - Sending tick $currentTick, current time: ${currentTime.value}")
globalEventBus.publishTick(currentTick)
}

/**
* Starts the ticker with the values from [GameOptions].
*/
fun start() =
start(GameOptions.TickRate.duration, GameOptions.TickRate.timeUnit)

/**
* Starts the ticker with the given duration between each tick.
*/
fun start(tickRateDuration: Long, tickRateTimeunit: TimeUnit) {
executor.scheduleAtFixedRate({
tick()
}, 1, tickRateDuration, tickRateTimeunit)
_isPausedProperty.updateValue(false)
}

/**
* Stops the ticker and thus advancement of ingame time (aka "game paused")
*/
fun stop() {
executor.shutdown()
executor = newExecutor()
_isPausedProperty.updateValue(true)
}

/**
* Starts/stops the ticker depending on [isPaused].
*/
fun togglePause() {
if(isPaused.value) {
start()
} else {
stop()
}
}

private fun newExecutor() = Executors.newScheduledThreadPool(1)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ enum class Format(val length: Int) {
* A string that fits into a sidebar. The value is actually [UiOptions.INTERFACE_PANEL_WIDTH] - 2 to subtract
* the border and get its content size.
*/
SIDEBAR(UiOptions.INTERFACE_PANEL_WIDTH - 2),
SIDEBAR(UiOptions.SIDEBAR_WIDTH - 2),

/**
* With this format the string has no length restriction.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package de.gleex.pltcmd.game.ui.strings.extensions
import de.gleex.pltcmd.game.ui.strings.Format
import de.gleex.pltcmd.game.ui.strings.FrontendString
import de.gleex.pltcmd.game.ui.strings.Transformation
import de.gleex.pltcmd.game.ui.strings.transformations.coordinateTransformation
import de.gleex.pltcmd.game.ui.strings.transformations.defaultTransformation
import de.gleex.pltcmd.game.ui.strings.transformations.unitTransformation
import de.gleex.pltcmd.game.ui.strings.transformations.unitsTransformation
import de.gleex.pltcmd.game.ui.strings.transformations.*
import de.gleex.pltcmd.model.elements.units.Unit
import de.gleex.pltcmd.model.elements.units.Units
import de.gleex.pltcmd.model.world.coordinate.Coordinate
import de.gleex.pltcmd.model.world.terrain.Terrain
import de.gleex.pltcmd.model.world.terrain.TerrainHeight
import de.gleex.pltcmd.model.world.terrain.TerrainType
import org.hexworks.cobalt.databinding.api.extension.toProperty
import org.hexworks.cobalt.databinding.api.value.ObservableValue

Expand Down Expand Up @@ -38,6 +38,9 @@ private fun <T> transformationFor(value: T): Transformation<T> {
is Coordinate -> coordinateTransformation
is Units -> unitsTransformation
is Unit -> unitTransformation
is TerrainHeight -> terrainHeightTransformation
is TerrainType -> terrainTypeTransformation
is Terrain -> terrainTransformation
else -> defaultTransformation
} as Transformation<T>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package de.gleex.pltcmd.game.ui.strings.transformations

import de.gleex.pltcmd.game.ui.strings.Format
import de.gleex.pltcmd.game.ui.strings.Transformation
import de.gleex.pltcmd.model.world.terrain.Terrain
import de.gleex.pltcmd.model.world.terrain.TerrainHeight
import de.gleex.pltcmd.model.world.terrain.TerrainType
import org.hexworks.zircon.api.graphics.Symbols

internal val terrainTransformation: Transformation<Terrain> = { format ->
when(format) {
Format.ICON -> type.terrainTypeTransformation(format)
Format.SHORT3 -> "${height.terrainHeightTransformation(Format.ICON)},${type.terrainTypeTransformation(Format.ICON)}"
Format.SHORT5 -> "${height.terrainHeightTransformation(Format.ICON)},${type.terrainTypeTransformation(Format.SHORT3)}"
Format.SIDEBAR -> "${"%3s".format(height.terrainHeightTransformation(Format.SHORT3))}, ${type.terrainTypeTransformation(Format.SIDEBAR)}"
Format.FULL -> "Terrain height: ${height.terrainHeightTransformation(format)}, type: ${type.terrainTypeTransformation(format)}"
}
}

internal val terrainTypeTransformation: Transformation<TerrainType> = { format ->
when(format) {
Format.ICON -> when (this) {
TerrainType.GRASSLAND -> "G"
TerrainType.FOREST -> "F"
TerrainType.MOUNTAIN -> "M"
TerrainType.HILL -> "H"
TerrainType.WATER_DEEP -> "W"
TerrainType.WATER_SHALLOW -> "S"
}
Format.SHORT3 -> when (this) {
TerrainType.GRASSLAND -> "Grs"
TerrainType.FOREST -> "Frs"
TerrainType.MOUNTAIN -> "Mnt"
TerrainType.HILL -> "Hil"
TerrainType.WATER_DEEP -> "Wdp"
TerrainType.WATER_SHALLOW -> "Wsh"
}
Format.SHORT5 -> when (this) {
TerrainType.GRASSLAND -> "Grass"
TerrainType.FOREST -> "Fores"
TerrainType.MOUNTAIN -> "Mount"
TerrainType.HILL -> "Hills"
TerrainType.WATER_DEEP -> "WtrDp"
TerrainType.WATER_SHALLOW -> "WtrSh"
}
Format.SIDEBAR, Format.FULL -> when (this) {
TerrainType.GRASSLAND,
TerrainType.FOREST,
TerrainType.MOUNTAIN -> name.toLowerCase().capitalize()
TerrainType.HILL -> "Hills"
TerrainType.WATER_DEEP -> "Deep water"
TerrainType.WATER_SHALLOW -> "Shallow water"
}
}
}

internal val terrainHeightTransformation: Transformation<TerrainHeight> = { format ->
when(format) {
Format.ICON -> when {
value < 1 -> Symbols.ARROW_DOWN.toString()
value > 9 -> Symbols.ARROW_UP.toString()
else -> "$value"
}
Format.SHORT3 -> "$value"
Format.SHORT5 -> "H: $value"
Format.SIDEBAR -> "Height: $value"
Format.FULL -> "Terrain height: $value"
}
}
Loading

0 comments on commit 28f067c

Please sign in to comment.