Skip to content

Commit

Permalink
Add the first monster in the game
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-arold committed Apr 10, 2019
1 parent 9986a6f commit e906f03
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 13 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,13 @@

This project is part of the [How to Make a Roguelike](https://hexworks.org/posts/tutorials/2018/11/04/how-to-make-a-roguelike.html)
tutorial. Check out the article series if you want to make the most ouf of this project.


## Improvements

These are the future improvements we'll add to the tutorial based on feedback:

- [x] Explain `also` (and other scoping functions) in #4 (where it is first used) instead of #6
- [ ] Explain the point of `tryToFindAttribute` instead of using `findAttribute`: we don't need
the flexibility and we're using mandatory attributes

4 changes: 4 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ object GameConfig {

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

// entities
const val FUNGI_PER_LEVEL = 15
const val MAXIMUM_FUNGUS_SPREAD = 20

fun buildAppConfig() = AppConfigs.newConfig()
.enableBetaFeatures()
.withDefaultTileset(TILESET)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.hexworks.cavesofzircon.attributes

import org.hexworks.amethyst.api.Attribute
import org.hexworks.cavesofzircon.GameConfig

data class FungusSpread(
var spreadCount: Int = 0,
val maximumSpread: Int = GameConfig.MAXIMUM_FUNGUS_SPREAD) : Attribute
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ object Player : BaseEntityType(

object Wall : BaseEntityType(
name = "wall")

object Fungus : BaseEntityType(
name = "fungus")
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ package org.hexworks.cavesofzircon.builders
import org.hexworks.amethyst.api.Entities
import org.hexworks.amethyst.api.builder.EntityBuilder
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.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
import org.hexworks.cavesofzircon.attributes.FungusSpread
import org.hexworks.cavesofzircon.attributes.flags.BlockOccupier
import org.hexworks.cavesofzircon.attributes.types.Fungus
import org.hexworks.cavesofzircon.attributes.types.Player
import org.hexworks.cavesofzircon.attributes.types.Wall
import org.hexworks.cavesofzircon.attributes.EntityActions
import org.hexworks.cavesofzircon.commands.Attack
import org.hexworks.cavesofzircon.commands.Dig
import org.hexworks.cavesofzircon.systems.Diggable
import org.hexworks.cavesofzircon.systems.*
import org.hexworks.cavesofzircon.world.GameContext

fun <T : EntityType> newGameEntityOfType(type: T, init: EntityBuilder<T, GameContext>.() -> Unit) =
Entities.newEntityOfType(type, init)
Expand All @@ -25,7 +25,7 @@ object EntityFactory {
attributes(
EntityPosition(),
EntityTile(GameTileRepository.PLAYER),
EntityActions(Dig::class))
EntityActions(Dig::class, Attack::class))
behaviors(InputReceiver)
facets(Movable, CameraMover)
}
Expand All @@ -37,5 +37,14 @@ object EntityFactory {
EntityTile(GameTileRepository.WALL))
facets(Diggable)
}

fun newFungus(fungusSpread: FungusSpread = FungusSpread()) = newGameEntityOfType(Fungus) {
attributes(BlockOccupier,
EntityPosition(),
EntityTile(GameTileRepository.FUNGUS),
fungusSpread)
facets(Attackable)
behaviors(FungusGrowth)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ object GameColors {
val FLOOR_FOREGROUND = TileColors.fromString("#75715E")
val FLOOR_BACKGROUND = TileColors.fromString("#1e2320")

val FUNGUS_COLOR = TileColors.fromString("#85DD1B")

val ACCENT_COLOR = TileColors.fromString("#FFCD22")
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@ object GameTileRepository {
.withBackgroundColor(GameColors.FLOOR_BACKGROUND)
.withForegroundColor(GameColors.ACCENT_COLOR)
.buildCharacterTile()

val FUNGUS = Tiles.newBuilder()
.withCharacter('f')
.withBackgroundColor(GameColors.FLOOR_BACKGROUND)
.withForegroundColor(GameColors.FUNGUS_COLOR)
.buildCharacterTile()
}
9 changes: 9 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/commands/Attack.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.hexworks.cavesofzircon.commands

import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.extensions.GameEntity
import org.hexworks.cavesofzircon.world.GameContext

data class Attack(override val context: GameContext,
override val source: GameEntity<EntityType>,
override val target: GameEntity<EntityType>) : EntityAction<EntityType, EntityType>
16 changes: 16 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/systems/Attackable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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.Attack
import org.hexworks.cavesofzircon.extensions.GameCommand
import org.hexworks.cavesofzircon.world.GameContext

object Attackable : BaseFacet<GameContext>() {

override fun executeCommand(command: GameCommand<out EntityType>) = command.responseWhenCommandIs(Attack::class) { (context, attacker, target) ->
context.world.removeEntity(target)
Consumed
}
}
32 changes: 32 additions & 0 deletions src/main/kotlin/org/hexworks/cavesofzircon/systems/FungusGrowth.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.base.BaseBehavior
import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.attributes.FungusSpread
import org.hexworks.cavesofzircon.builders.EntityFactory
import org.hexworks.cavesofzircon.extensions.GameEntity
import org.hexworks.cavesofzircon.extensions.position
import org.hexworks.cavesofzircon.extensions.tryToFindAttribute
import org.hexworks.cavesofzircon.world.GameContext
import org.hexworks.cobalt.datatypes.extensions.map
import org.hexworks.zircon.api.Sizes

object FungusGrowth : BaseBehavior<GameContext>(FungusSpread::class) {

override fun update(entity: GameEntity<out EntityType>, context: GameContext): Boolean {
val world = context.world
val fungusSpread = entity.tryToFindAttribute(FungusSpread::class)
val (spreadCount, maxSpread) = fungusSpread
return if (spreadCount < maxSpread && Math.random() < 0.015) {
world.findEmptyLocationWithin(
offset = entity.position
.withRelativeX(-1)
.withRelativeY(-1),
size = Sizes.create3DSize(3, 3, 0)).map { emptyLocation ->
world.addEntity(EntityFactory.newFungus(fungusSpread), emptyLocation)
fungusSpread.spreadCount++
}
true
} else false
}
}
28 changes: 23 additions & 5 deletions src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.hexworks.cavesofzircon.world

import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.cavesofzircon.GameConfig
import org.hexworks.cavesofzircon.GameConfig.WORLD_SIZE
import org.hexworks.cavesofzircon.attributes.types.Player
import org.hexworks.cavesofzircon.builders.EntityFactory
import org.hexworks.cavesofzircon.extensions.GameEntity
import org.hexworks.zircon.api.Positions
import org.hexworks.zircon.api.Sizes
import org.hexworks.zircon.api.data.Size
import org.hexworks.zircon.api.data.impl.Size3D

class GameBuilder(val worldSize: Size3D = WORLD_SIZE) {
Expand All @@ -25,6 +27,7 @@ class GameBuilder(val worldSize: Size3D = WORLD_SIZE) {
prepareWorld()

val player = addPlayer()
addFungi()

return Game.create(
player = player,
Expand All @@ -36,11 +39,26 @@ class GameBuilder(val worldSize: Size3D = WORLD_SIZE) {
}

private fun addPlayer(): GameEntity<Player> {
val player = EntityFactory.newPlayer()
world.addAtEmptyPosition(player,
offset = Positions.default3DPosition().withZ(GameConfig.DUNGEON_LEVELS - 1),
size = world.visibleSize().copy(zLength = 0))
return player
return EntityFactory.newPlayer().addToWorld(
atLevel = GameConfig.DUNGEON_LEVELS - 1,
atArea = world.visibleSize().to2DSize())
}

private fun addFungi() = also {
repeat(world.actualSize().zLength) { level ->
repeat(GameConfig.FUNGI_PER_LEVEL) {
EntityFactory.newFungus().addToWorld(level)
}
}
}

private fun <T : EntityType> GameEntity<T>.addToWorld(
atLevel: Int,
atArea: Size = world.actualSize().to2DSize()): GameEntity<T> {
world.addAtEmptyPosition(this,
offset = Positions.default3DPosition().withZ(atLevel),
size = Size3D.from2DSize(atArea))
return this
}

companion object {
Expand Down

0 comments on commit e906f03

Please sign in to comment.