From 2db3a3946f92084bb58adb2b8d2a5f81333d2b83 Mon Sep 17 00:00:00 2001 From: adam-arold Date: Fri, 17 May 2019 22:58:15 +0200 Subject: [PATCH] Add items and inventory --- gradle.properties | 6 +- .../org/hexworks/cavesofzircon/GameConfig.kt | 1 + .../cavesofzircon/attributes/Inventory.kt | 34 ++++++++++ .../cavesofzircon/attributes/ItemIcon.kt | 6 ++ .../attributes/types/EntityTypes.kt | 6 +- .../cavesofzircon/attributes/types/Item.kt | 16 +++++ .../attributes/types/ItemHolder.kt | 15 +++++ .../cavesofzircon/builders/EntityFactory.kt | 23 ++++++- .../cavesofzircon/builders/GameColors.kt | 1 + .../builders/GameTileRepository.kt | 6 ++ .../cavesofzircon/commands/DropItem.kt | 13 ++++ .../commands/InspectInventory.kt | 11 ++++ .../cavesofzircon/commands/PickItemUp.kt | 14 ++++ .../extensions/EntityExtensions.kt | 10 +++ .../cavesofzircon/extensions/TypeAliases.kt | 6 ++ .../cavesofzircon/systems/InputReceiver.kt | 12 ++++ .../systems/InventoryInspector.kt | 64 +++++++++++++++++++ .../cavesofzircon/systems/ItemDropper.kt | 26 ++++++++ .../cavesofzircon/systems/ItemPicker.kt | 39 +++++++++++ .../hexworks/cavesofzircon/view/StartView.kt | 4 ++ .../view/fragment/InventoryFragment.kt | 42 ++++++++++++ .../view/fragment/InventoryRowFragment.kt | 28 ++++++++ .../cavesofzircon/world/GameBuilder.kt | 9 +++ 23 files changed, 386 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/attributes/Inventory.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/attributes/ItemIcon.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/Item.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/ItemHolder.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/commands/DropItem.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/commands/InspectInventory.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/commands/PickItemUp.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/systems/InventoryInspector.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/systems/ItemDropper.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/systems/ItemPicker.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/view/fragment/InventoryFragment.kt create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/view/fragment/InventoryRowFragment.kt diff --git a/gradle.properties b/gradle.properties index eabc4f0..98066ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,9 +9,9 @@ version=2019.0.1-PREVIEW kotlin_version=1.3.21 shadow_version=4.0.2 -cobalt_version=2018.1.1-PREVIEW -amethyst_version=2019.0.4-PREVIEW -zircon_version=2019.0.14-PREVIEW +cobalt_version=2019.1.0-PREVIEW +amethyst_version=2019.1.0-PREVIEW +zircon_version=2019.1.0-PREVIEW junit_version=4.12 mockito_version=1.10.19 diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt b/src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt index be43394..a14fc43 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt @@ -25,6 +25,7 @@ object GameConfig { // entities const val FUNGI_PER_LEVEL = 15 const val BATS_PER_LEVEL = 10 + const val ZIRCONS_PER_LEVEL = 20 const val MAXIMUM_FUNGUS_SPREAD = 20 fun buildAppConfig() = AppConfigs.newConfig() diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/Inventory.kt b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/Inventory.kt new file mode 100644 index 0000000..7a925f3 --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/Inventory.kt @@ -0,0 +1,34 @@ +package org.hexworks.cavesofzircon.attributes + +import org.hexworks.amethyst.api.Attribute +import org.hexworks.cavesofzircon.extensions.GameItem +import org.hexworks.cobalt.datatypes.Identifier +import org.hexworks.cobalt.datatypes.Maybe + +class Inventory(val size: Int) : Attribute { + + private val currentItems = mutableListOf() + + val items: List + get() = currentItems.toList() + + val isEmpty: Boolean + get() = currentItems.isEmpty() + + val isFull: Boolean + get() = currentItems.size >= size + + fun findItemBy(id: Identifier): Maybe { + return Maybe.ofNullable(items.firstOrNull { it.id == id }) + } + + fun addItem(item: GameItem): Boolean { + return if (isFull.not()) { + currentItems.add(item) + } else false + } + + fun removeItem(entity: GameItem): Boolean { + return currentItems.remove(entity) + } +} diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/ItemIcon.kt b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/ItemIcon.kt new file mode 100644 index 0000000..47aaf74 --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/ItemIcon.kt @@ -0,0 +1,6 @@ +package org.hexworks.cavesofzircon.attributes + +import org.hexworks.amethyst.api.Attribute +import org.hexworks.zircon.api.data.GraphicalTile + +data class ItemIcon(val iconTile: GraphicalTile) : Attribute diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/EntityTypes.kt b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/EntityTypes.kt index 2c01dee..63eeb90 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/EntityTypes.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/EntityTypes.kt @@ -3,7 +3,7 @@ package org.hexworks.cavesofzircon.attributes.types import org.hexworks.amethyst.api.base.BaseEntityType object Player : BaseEntityType( - name = "player"), Combatant + name = "player"), Combatant, ItemHolder object Wall : BaseEntityType( name = "wall") @@ -20,4 +20,8 @@ object StairsDown : BaseEntityType( object StairsUp : BaseEntityType( name = "stairs up") +object Zircon : BaseEntityType( + name = "Zircon", + description = "A small piece of Zircon. Its value is unfathomable."), Item + object FogOfWarType : BaseEntityType() diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/Item.kt b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/Item.kt new file mode 100644 index 0000000..c93705f --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/Item.kt @@ -0,0 +1,16 @@ +package org.hexworks.cavesofzircon.attributes.types + +import org.hexworks.amethyst.api.entity.EntityType +import org.hexworks.cavesofzircon.attributes.EntityTile +import org.hexworks.cavesofzircon.attributes.ItemIcon +import org.hexworks.cavesofzircon.extensions.GameEntity +import org.hexworks.zircon.api.data.GraphicalTile +import org.hexworks.zircon.api.data.Tile + +interface Item : EntityType + +val GameEntity.tile: Tile + get() = findAttribute(EntityTile::class).get().tile + +val GameEntity.iconTile: GraphicalTile + get() = findAttribute(ItemIcon::class).get().iconTile diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/ItemHolder.kt b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/ItemHolder.kt new file mode 100644 index 0000000..4066b9b --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/ItemHolder.kt @@ -0,0 +1,15 @@ +package org.hexworks.cavesofzircon.attributes.types + +import org.hexworks.amethyst.api.entity.EntityType +import org.hexworks.cavesofzircon.attributes.Inventory +import org.hexworks.cavesofzircon.extensions.GameItem +import org.hexworks.cavesofzircon.extensions.GameItemHolder + +interface ItemHolder : EntityType + +fun GameItemHolder.addItem(item: GameItem) = inventory.addItem(item) + +fun GameItemHolder.removeItem(item: GameItem) = inventory.removeItem(item) + +val GameItemHolder.inventory: Inventory + get() = findAttribute(Inventory::class).get() diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/builders/EntityFactory.kt b/src/main/kotlin/org/hexworks/cavesofzircon/builders/EntityFactory.kt index 83d773c..294fe76 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/builders/EntityFactory.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/builders/EntityFactory.kt @@ -8,6 +8,8 @@ import org.hexworks.cavesofzircon.attributes.EntityActions import org.hexworks.cavesofzircon.attributes.EntityPosition import org.hexworks.cavesofzircon.attributes.EntityTile import org.hexworks.cavesofzircon.attributes.FungusSpread +import org.hexworks.cavesofzircon.attributes.Inventory +import org.hexworks.cavesofzircon.attributes.ItemIcon import org.hexworks.cavesofzircon.attributes.Vision import org.hexworks.cavesofzircon.attributes.flags.BlockOccupier import org.hexworks.cavesofzircon.attributes.flags.VisionBlocker @@ -17,6 +19,7 @@ import org.hexworks.cavesofzircon.attributes.types.Player import org.hexworks.cavesofzircon.attributes.types.StairsDown import org.hexworks.cavesofzircon.attributes.types.StairsUp import org.hexworks.cavesofzircon.attributes.types.Wall +import org.hexworks.cavesofzircon.attributes.types.Zircon import org.hexworks.cavesofzircon.commands.Attack import org.hexworks.cavesofzircon.commands.Dig import org.hexworks.cavesofzircon.entities.FogOfWar @@ -26,12 +29,17 @@ import org.hexworks.cavesofzircon.systems.Destructible import org.hexworks.cavesofzircon.systems.Diggable import org.hexworks.cavesofzircon.systems.FungusGrowth import org.hexworks.cavesofzircon.systems.InputReceiver +import org.hexworks.cavesofzircon.systems.InventoryInspector +import org.hexworks.cavesofzircon.systems.ItemDropper +import org.hexworks.cavesofzircon.systems.ItemPicker import org.hexworks.cavesofzircon.systems.Movable import org.hexworks.cavesofzircon.systems.StairClimber import org.hexworks.cavesofzircon.systems.StairDescender import org.hexworks.cavesofzircon.systems.Wanderer import org.hexworks.cavesofzircon.world.Game import org.hexworks.cavesofzircon.world.GameContext +import org.hexworks.zircon.api.GraphicalTilesetResources +import org.hexworks.zircon.api.Tiles fun newGameEntityOfType(type: T, init: EntityBuilder.() -> Unit) = Entities.newEntityOfType(type, init) @@ -69,9 +77,11 @@ object EntityFactory { attackValue = 10, defenseValue = 5), EntityTile(GameTileRepository.PLAYER), - EntityActions(Dig::class, Attack::class)) + EntityActions(Dig::class, Attack::class), + Inventory(10)) behaviors(InputReceiver) - facets(Movable, CameraMover, StairClimber, StairDescender, Attackable, Destructible) + facets(Movable, CameraMover, StairClimber, StairDescender, Attackable, Destructible, ItemPicker, + InventoryInspector, ItemDropper) } fun newFungus(fungusSpread: FungusSpread = FungusSpread()) = newGameEntityOfType(Fungus) { @@ -99,5 +109,14 @@ object EntityFactory { facets(Movable, Attackable, Destructible) behaviors(Wanderer) } + + fun newZircon() = newGameEntityOfType(Zircon) { + attributes(ItemIcon(Tiles.newBuilder() + .withName("white gem") + .withTileset(GraphicalTilesetResources.nethack16x16()) + .buildGraphicTile()), + EntityPosition(), + EntityTile(GameTileRepository.ZIRCON)) + } } diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameColors.kt b/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameColors.kt index 9ebe6d3..09cb182 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameColors.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameColors.kt @@ -13,6 +13,7 @@ object GameColors { val BAT_COLOR = TileColors.fromString("#2348b2") val ACCENT_COLOR = TileColors.fromString("#FFCD22") + val ZIRCON_COLOR = TileColors.fromString("#dddddd") val UNREVEALED_COLOR = TileColors.fromString("#000000") } diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameTileRepository.kt b/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameTileRepository.kt index ef1fb9b..7451e28 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameTileRepository.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameTileRepository.kt @@ -50,6 +50,12 @@ object GameTileRepository { .withForegroundColor(GameColors.BAT_COLOR) .buildCharacterTile() + val ZIRCON = Tiles.newBuilder() + .withCharacter(',') + .withBackgroundColor(GameColors.FLOOR_BACKGROUND) + .withForegroundColor(GameColors.ZIRCON_COLOR) + .buildCharacterTile() + val UNREVEALED = Tiles.newBuilder() .withCharacter(' ') .withBackgroundColor(GameColors.UNREVEALED_COLOR) diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/commands/DropItem.kt b/src/main/kotlin/org/hexworks/cavesofzircon/commands/DropItem.kt new file mode 100644 index 0000000..35129a2 --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/commands/DropItem.kt @@ -0,0 +1,13 @@ +package org.hexworks.cavesofzircon.commands + +import org.hexworks.amethyst.api.entity.EntityType +import org.hexworks.cavesofzircon.extensions.GameCommand +import org.hexworks.cavesofzircon.extensions.GameItem +import org.hexworks.cavesofzircon.extensions.GameItemHolder +import org.hexworks.cavesofzircon.world.GameContext +import org.hexworks.zircon.api.data.impl.Position3D + +data class DropItem(override val context: GameContext, + override val source: GameItemHolder, + val item: GameItem, + val position: Position3D) : GameCommand diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/commands/InspectInventory.kt b/src/main/kotlin/org/hexworks/cavesofzircon/commands/InspectInventory.kt new file mode 100644 index 0000000..f125ec5 --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/commands/InspectInventory.kt @@ -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.GameItemHolder +import org.hexworks.cavesofzircon.world.GameContext +import org.hexworks.zircon.api.data.impl.Position3D + +data class InspectInventory(override val context: GameContext, + override val source: GameItemHolder, + val position: Position3D) : GameCommand diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/commands/PickItemUp.kt b/src/main/kotlin/org/hexworks/cavesofzircon/commands/PickItemUp.kt new file mode 100644 index 0000000..ecb3da5 --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/commands/PickItemUp.kt @@ -0,0 +1,14 @@ +package org.hexworks.cavesofzircon.commands + +import org.hexworks.cavesofzircon.attributes.types.ItemHolder +import org.hexworks.cavesofzircon.extensions.GameCommand +import org.hexworks.cavesofzircon.extensions.GameItemHolder +import org.hexworks.cavesofzircon.world.GameContext +import org.hexworks.zircon.api.data.impl.Position3D + +/** + * A [GameCommand] representing [source] picking up an item at [position]. + */ +data class PickItemUp(override val context: GameContext, + override val source: GameItemHolder, + val position: Position3D) : GameCommand diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/extensions/EntityExtensions.kt b/src/main/kotlin/org/hexworks/cavesofzircon/extensions/EntityExtensions.kt index 16d8367..c08d3dc 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/extensions/EntityExtensions.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/extensions/EntityExtensions.kt @@ -1,15 +1,20 @@ +@file:Suppress("UNCHECKED_CAST") + package org.hexworks.cavesofzircon.extensions import org.hexworks.amethyst.api.Attribute import org.hexworks.amethyst.api.Consumed import org.hexworks.amethyst.api.Pass import org.hexworks.amethyst.api.Response +import org.hexworks.amethyst.api.entity.Entity +import org.hexworks.amethyst.api.entity.EntityType import org.hexworks.cavesofzircon.attributes.EntityActions import org.hexworks.cavesofzircon.attributes.EntityPosition import org.hexworks.cavesofzircon.attributes.EntityTile import org.hexworks.cavesofzircon.attributes.flags.BlockOccupier import org.hexworks.cavesofzircon.attributes.flags.VisionBlocker import org.hexworks.cavesofzircon.attributes.types.Combatant +import org.hexworks.cavesofzircon.attributes.types.Item import org.hexworks.cavesofzircon.attributes.types.Player import org.hexworks.cavesofzircon.attributes.types.combatStats import org.hexworks.cavesofzircon.world.GameContext @@ -17,6 +22,7 @@ import org.hexworks.cobalt.datatypes.extensions.map import org.hexworks.cobalt.datatypes.extensions.orElseThrow import org.hexworks.zircon.api.data.Tile import kotlin.reflect.KClass +import kotlin.reflect.full.isSuperclassOf var AnyGameEntity.position get() = tryToFindAttribute(EntityPosition::class).position @@ -35,6 +41,10 @@ val AnyGameEntity.tile: Tile val AnyGameEntity.isPlayer: Boolean get() = this.type == Player +inline fun Iterable.filterType(): List> { + return filter { T::class.isSuperclassOf(it.type::class) }.toList() as List> +} + val AnyGameEntity.blocksVision: Boolean get() = this.findAttribute(VisionBlocker::class).isPresent diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/extensions/TypeAliases.kt b/src/main/kotlin/org/hexworks/cavesofzircon/extensions/TypeAliases.kt index ee8eac1..76c901f 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/extensions/TypeAliases.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/extensions/TypeAliases.kt @@ -3,6 +3,8 @@ 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.attributes.types.Item +import org.hexworks.cavesofzircon.attributes.types.ItemHolder import org.hexworks.cavesofzircon.world.GameContext /** @@ -19,3 +21,7 @@ typealias GameEntity = Entity * Specializes [Command] with our [GameContext] type. */ typealias GameCommand = Command + +typealias GameItem = GameEntity + +typealias GameItemHolder = GameEntity diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/systems/InputReceiver.kt b/src/main/kotlin/org/hexworks/cavesofzircon/systems/InputReceiver.kt index 987bcd8..2b54794 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/systems/InputReceiver.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/systems/InputReceiver.kt @@ -3,9 +3,11 @@ package org.hexworks.cavesofzircon.systems import org.hexworks.amethyst.api.base.BaseBehavior import org.hexworks.amethyst.api.entity.EntityType import org.hexworks.cavesofzircon.attributes.types.Player +import org.hexworks.cavesofzircon.commands.InspectInventory import org.hexworks.cavesofzircon.commands.MoveDown import org.hexworks.cavesofzircon.commands.MoveTo import org.hexworks.cavesofzircon.commands.MoveUp +import org.hexworks.cavesofzircon.commands.PickItemUp import org.hexworks.cavesofzircon.extensions.GameEntity import org.hexworks.cavesofzircon.extensions.position import org.hexworks.cavesofzircon.world.GameContext @@ -29,6 +31,8 @@ object InputReceiver : BaseBehavior() { KeyCode.KEY_D -> player.moveTo(currentPos.withRelativeX(1), context) KeyCode.KEY_R -> player.moveUp(context) KeyCode.KEY_F -> player.moveDown(context) + KeyCode.KEY_P -> player.pickItemUp(currentPos, context) + KeyCode.KEY_I -> player.inspectInventory(currentPos, context) else -> { logger.debug("UI Event ($uiEvent) does not have a corresponding command, it is ignored.") } @@ -37,6 +41,14 @@ object InputReceiver : BaseBehavior() { return true } + private fun GameEntity.inspectInventory(position: Position3D, context: GameContext) { + executeCommand(InspectInventory(context, this, position)) + } + + private fun GameEntity.pickItemUp(position: Position3D, context: GameContext) { + executeCommand(PickItemUp(context, this, position)) + } + private fun GameEntity.moveTo(position: Position3D, context: GameContext) { executeCommand(MoveTo(context, this, position)) } diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/systems/InventoryInspector.kt b/src/main/kotlin/org/hexworks/cavesofzircon/systems/InventoryInspector.kt new file mode 100644 index 0000000..38ce29e --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/systems/InventoryInspector.kt @@ -0,0 +1,64 @@ +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.GameConfig +import org.hexworks.cavesofzircon.attributes.types.inventory +import org.hexworks.cavesofzircon.commands.DropItem +import org.hexworks.cavesofzircon.commands.InspectInventory +import org.hexworks.cavesofzircon.extensions.GameCommand +import org.hexworks.cavesofzircon.view.fragment.InventoryFragment +import org.hexworks.cavesofzircon.world.GameContext +import org.hexworks.zircon.api.Components +import org.hexworks.zircon.api.Sizes +import org.hexworks.zircon.api.builder.component.ModalBuilder +import org.hexworks.zircon.api.component.ComponentAlignment.BOTTOM_LEFT +import org.hexworks.zircon.api.extensions.onComponentEvent +import org.hexworks.zircon.api.uievent.ComponentEventType.ACTIVATED +import org.hexworks.zircon.api.uievent.Processed +import org.hexworks.zircon.internal.component.modal.EmptyModalResult + +object InventoryInspector : BaseFacet() { + + val DIALOG_SIZE = Sizes.create(33, 14) + + override fun executeCommand(command: GameCommand) = command + .responseWhenCommandIs(InspectInventory::class) { (context, itemHolder, position) -> + + val screen = context.screen + + val fragment = InventoryFragment(itemHolder.inventory, DIALOG_SIZE.width - 3) { item -> + itemHolder.executeCommand(DropItem(context, itemHolder, item, position)) + } + + val panel = Components.panel() + .withSize(DIALOG_SIZE) + .wrapWithBox(true) + .wrapWithShadow(true) + .withTitle("Inventory") + .build() + + panel.addFragment(fragment) + + val modal = ModalBuilder.newBuilder() + .withParentSize(screen.size) + .withComponent(panel) + .build() + + panel.addComponent(Components.button() + .withText("Close") + .withAlignmentWithin(panel, BOTTOM_LEFT) + .build().apply { + onComponentEvent(ACTIVATED) { + modal.close(EmptyModalResult) + Processed + } + }) + + modal.applyColorTheme(GameConfig.THEME) + screen.openModal(modal) + Consumed + } + +} diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/systems/ItemDropper.kt b/src/main/kotlin/org/hexworks/cavesofzircon/systems/ItemDropper.kt new file mode 100644 index 0000000..e7f49fa --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/systems/ItemDropper.kt @@ -0,0 +1,26 @@ +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.attributes.types.inventory +import org.hexworks.cavesofzircon.attributes.types.removeItem +import org.hexworks.cavesofzircon.commands.DropItem +import org.hexworks.cavesofzircon.extensions.GameCommand +import org.hexworks.cavesofzircon.extensions.isPlayer +import org.hexworks.cavesofzircon.functions.logGameEvent +import org.hexworks.cavesofzircon.world.GameContext + +object ItemDropper : BaseFacet() { + + override fun executeCommand(command: GameCommand) = command + .responseWhenCommandIs(DropItem::class) { (context, itemHolder, item, position) -> + if (itemHolder.removeItem(item)) { + context.world.addEntity(item, position) + val subject = if (itemHolder.isPlayer) "You" else "The $itemHolder" + val verb = if (itemHolder.isPlayer) "drop" else "drops" + logGameEvent("$subject $verb the $item.") + } + Consumed + } +} diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/systems/ItemPicker.kt b/src/main/kotlin/org/hexworks/cavesofzircon/systems/ItemPicker.kt new file mode 100644 index 0000000..5eb0498 --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/systems/ItemPicker.kt @@ -0,0 +1,39 @@ +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.attributes.types.Item +import org.hexworks.cavesofzircon.attributes.types.addItem +import org.hexworks.cavesofzircon.commands.PickItemUp +import org.hexworks.cavesofzircon.extensions.GameCommand +import org.hexworks.cavesofzircon.extensions.filterType +import org.hexworks.cavesofzircon.extensions.isPlayer +import org.hexworks.cavesofzircon.functions.logGameEvent +import org.hexworks.cavesofzircon.world.GameContext +import org.hexworks.cavesofzircon.world.World +import org.hexworks.cobalt.datatypes.Maybe +import org.hexworks.cobalt.datatypes.extensions.flatMap +import org.hexworks.cobalt.datatypes.extensions.map +import org.hexworks.zircon.api.data.impl.Position3D + +object ItemPicker : BaseFacet() { + + override fun executeCommand(command: GameCommand) = command.responseWhenCommandIs(PickItemUp::class) { (context, itemHolder, position) -> + val world = context.world + world.findTopItem(position).map { item -> + if (itemHolder.addItem(item)) { + world.removeEntity(item) + val subject = if (itemHolder.isPlayer) "You" else "The $itemHolder" + val verb = if (itemHolder.isPlayer) "pick up" else "picks up" + logGameEvent("$subject $verb the $item.") + } + } + Consumed + } + + private fun World.findTopItem(position: Position3D) = + fetchBlockAt(position).flatMap { block -> + Maybe.ofNullable(block.entities.filterType().firstOrNull()) + } +} diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/view/StartView.kt b/src/main/kotlin/org/hexworks/cavesofzircon/view/StartView.kt index 4374248..81f9c22 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/view/StartView.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/view/StartView.kt @@ -1,5 +1,8 @@ package org.hexworks.cavesofzircon.view +import org.hexworks.cavesofzircon.attributes.Inventory +import org.hexworks.cavesofzircon.builders.EntityFactory +import org.hexworks.cavesofzircon.view.fragment.InventoryFragment import org.hexworks.zircon.api.ColorThemes import org.hexworks.zircon.api.Components import org.hexworks.zircon.api.component.ComponentAlignment @@ -36,6 +39,7 @@ class StartView : BaseView() { close() Processed } + screen.addComponent(header) screen.addComponent(startButton) } diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/view/fragment/InventoryFragment.kt b/src/main/kotlin/org/hexworks/cavesofzircon/view/fragment/InventoryFragment.kt new file mode 100644 index 0000000..ac8083c --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/view/fragment/InventoryFragment.kt @@ -0,0 +1,42 @@ +package org.hexworks.cavesofzircon.view.fragment + +import org.hexworks.cavesofzircon.attributes.Inventory +import org.hexworks.cavesofzircon.extensions.GameItem +import org.hexworks.zircon.api.Components +import org.hexworks.zircon.api.component.Fragment +import org.hexworks.zircon.api.extensions.onComponentEvent +import org.hexworks.zircon.api.uievent.ComponentEventType.ACTIVATED +import org.hexworks.zircon.api.uievent.Processed + +class InventoryFragment(inventory: Inventory, + width: Int, + onDrop: (GameItem) -> Unit) : Fragment { + + override val root = Components.vbox() + .withSize(width, inventory.size + 1) + .build().apply { + val list = this + addComponent(Components.hbox() + .withSpacing(1) + .withSize(width, 1) + .build().apply { + addComponent(Components.label().withText("").withSize(1, 1)) + addComponent(Components.header().withText("Name").withSize(NAME_COLUMN_WIDTH, 1)) + addComponent(Components.header().withText("Actions").withSize(ACTIONS_COLUMN_WIDTH, 1)) + }) + inventory.items.forEach { item -> + addFragment(InventoryRowFragment(width, item).apply { + dropButton.onComponentEvent(ACTIVATED) { + list.removeComponent(this.root) + onDrop(item) + Processed + } + }) + } + } + + companion object { + const val NAME_COLUMN_WIDTH = 15 + const val ACTIONS_COLUMN_WIDTH = 10 + } +} diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/view/fragment/InventoryRowFragment.kt b/src/main/kotlin/org/hexworks/cavesofzircon/view/fragment/InventoryRowFragment.kt new file mode 100644 index 0000000..b6c3329 --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/view/fragment/InventoryRowFragment.kt @@ -0,0 +1,28 @@ +package org.hexworks.cavesofzircon.view.fragment + +import org.hexworks.cavesofzircon.attributes.types.iconTile +import org.hexworks.cavesofzircon.extensions.GameItem +import org.hexworks.zircon.api.Components +import org.hexworks.zircon.api.component.Fragment +import org.hexworks.zircon.api.graphics.Symbols + +class InventoryRowFragment(width: Int, item: GameItem) : Fragment { + + val dropButton = Components.button() + .wrapSides(false) + .withText("${Symbols.ARROW_DOWN}") + .build() + + override val root = Components.hbox() + .withSpacing(1) + .withSize(width, 1) + .build().apply { + addComponent(Components.icon() + .withIcon(item.iconTile)) + addComponent(Components.label() + .withSize(InventoryFragment.NAME_COLUMN_WIDTH, 1) + .withText(item.name)) + addComponent(dropButton) + } + +} diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt b/src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt index 8470c18..b476dbc 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt @@ -30,6 +30,7 @@ class GameBuilder(val worldSize: Size3D = WORLD_SIZE) { val player = addPlayer() addFungi() addBats() + addZircons() val game = Game.create( player = player, @@ -58,6 +59,14 @@ class GameBuilder(val worldSize: Size3D = WORLD_SIZE) { } } + private fun addZircons() = also { + repeat(world.actualSize().zLength) { level -> + repeat(GameConfig.ZIRCONS_PER_LEVEL) { + EntityFactory.newZircon().addToWorld(level) + } + } + } + private fun addBats() = also { repeat(world.actualSize().zLength) { level -> repeat(BATS_PER_LEVEL) {