From 700c6a4970c62a2e4a9c696fec81c52bc92ebfb8 Mon Sep 17 00:00:00 2001 From: adam-arold Date: Sun, 23 Jun 2019 18:19:39 +0200 Subject: [PATCH] Add aggressive monsters --- .../org/hexworks/cavesofzircon/GameConfig.kt | 3 +-- .../cavesofzircon/attributes/Equipment.kt | 4 +-- .../attributes/types/EntityTypes.kt | 3 +++ .../cavesofzircon/builders/EntityFactory.kt | 25 +++++++++++++++++-- .../cavesofzircon/builders/GameColors.kt | 1 + .../builders/GameTileRepository.kt | 6 +++++ .../cavesofzircon/systems/HunterSeeker.kt | 25 +++++++++++++++++++ .../cavesofzircon/world/GameBuilder.kt | 19 +++----------- .../org/hexworks/cavesofzircon/world/World.kt | 20 +++++++++++++++ 9 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/org/hexworks/cavesofzircon/systems/HunterSeeker.kt diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt b/src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt index bc8533b..55d6d8c 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/GameConfig.kt @@ -27,8 +27,7 @@ object GameConfig { const val BATS_PER_LEVEL = 10 const val ZIRCONS_PER_LEVEL = 20 const val MAXIMUM_FUNGUS_SPREAD = 20 - const val WEAPONS_PER_LEVEL = 3 - const val ARMOR_PER_LEVEL = 3 + const val ZOMBIES_PER_LEVEL = 3 fun buildAppConfig() = AppConfigs.newConfig() .enableBetaFeatures() diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/Equipment.kt b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/Equipment.kt index 4cb3863..58a1cab 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/Equipment.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/Equipment.kt @@ -31,8 +31,8 @@ class Equipment(initialWeapon: GameEntity, val weaponName: String get() = weaponProperty.value.name - private val weapon: GameEntity by weaponProperty.asDelegate() - private val armor: GameEntity by armorProperty.asDelegate() + val weapon: GameEntity by weaponProperty.asDelegate() + val armor: GameEntity by armorProperty.asDelegate() private val weaponStats: String get() = " A: ${weapon.attackValue} D: ${weapon.defenseValue}" 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 b8d83fd..597f42c 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/EntityTypes.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/attributes/types/EntityTypes.kt @@ -14,6 +14,9 @@ object Fungus : BaseEntityType( object Bat : BaseEntityType( name = "bat"), Combatant, ItemHolder +object Zombie : BaseEntityType( + name = "zombie"), Combatant, ItemHolder + object BatMeat : BaseEntityType( name = "Bat meat", description = "Stringy bat meat. It is edible, but not tasty."), Food diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/builders/EntityFactory.kt b/src/main/kotlin/org/hexworks/cavesofzircon/builders/EntityFactory.kt index 1ef9dd3..49db99a 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/builders/EntityFactory.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/builders/EntityFactory.kt @@ -35,6 +35,7 @@ import org.hexworks.cavesofzircon.attributes.types.Sword import org.hexworks.cavesofzircon.attributes.types.Wall import org.hexworks.cavesofzircon.attributes.types.Weapon import org.hexworks.cavesofzircon.attributes.types.Zircon +import org.hexworks.cavesofzircon.attributes.types.Zombie import org.hexworks.cavesofzircon.commands.Attack import org.hexworks.cavesofzircon.commands.Dig import org.hexworks.cavesofzircon.entities.FogOfWar @@ -45,8 +46,8 @@ import org.hexworks.cavesofzircon.systems.Destructible import org.hexworks.cavesofzircon.systems.DigestiveSystem import org.hexworks.cavesofzircon.systems.Diggable import org.hexworks.cavesofzircon.systems.EnergyExpender -import org.hexworks.cavesofzircon.systems.EquipmentHandler import org.hexworks.cavesofzircon.systems.FungusGrowth +import org.hexworks.cavesofzircon.systems.HunterSeeker import org.hexworks.cavesofzircon.systems.InputReceiver import org.hexworks.cavesofzircon.systems.InventoryInspector import org.hexworks.cavesofzircon.systems.ItemDropper @@ -105,7 +106,7 @@ object EntityFactory { initialWeapon = newClub(), initialArmor = newJacket())) behaviors(InputReceiver, EnergyExpender) - facets(EquipmentHandler, Movable, CameraMover, StairClimber, StairDescender, Attackable, Destructible, + facets(Movable, CameraMover, StairClimber, StairDescender, Attackable, Destructible, ItemPicker, InventoryInspector, ItemDropper, EnergyExpender, DigestiveSystem) } @@ -138,6 +139,24 @@ object EntityFactory { behaviors(Wanderer) } + fun newZombie() = newGameEntityOfType(Zombie) { + attributes(BlockOccupier, + EntityPosition(), + EntityTile(GameTileRepository.ZOMBIE), + Vision(10), + CombatStats.create( + maxHp = 25, + attackValue = 8, + defenseValue = 4), + Inventory(2).apply { + addItem(newRandomWeapon()) + addItem(newRandomArmor()) + }, + EntityActions(Attack::class)) + facets(Movable, Attackable, ItemDropper, LootDropper, Destructible) + behaviors(HunterSeeker or Wanderer) + } + fun newBatMeat() = newGameEntityOfType(BatMeat) { attributes(ItemIcon(Tiles.newBuilder() .withName("Meatball") @@ -261,5 +280,7 @@ object EntityFactory { 1 -> newMediumArmor() else -> newHeavyArmor() } + + } diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameColors.kt b/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameColors.kt index e95b8c4..5b4c9a8 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameColors.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameColors.kt @@ -15,6 +15,7 @@ object GameColors { val ACCENT_COLOR = TileColors.fromString("#FFCD22") val ZIRCON_COLOR = TileColors.fromString("#dddddd") val BAT_MEAT_COLOR = TileColors.fromString("#EA4861") + val ZOMBIE_COLOR = TileColors.fromString("#618358") 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 ea5edff..ec69e0b 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameTileRepository.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/builders/GameTileRepository.kt @@ -115,4 +115,10 @@ object GameTileRepository { .withForegroundColor(ANSITileColor.BRIGHT_WHITE) .withBackgroundColor(GameColors.FLOOR_BACKGROUND) .buildCharacterTile() + + val ZOMBIE = Tiles.newBuilder() + .withCharacter('z') + .withForegroundColor(GameColors.ZOMBIE_COLOR) + .withBackgroundColor(GameColors.FLOOR_BACKGROUND) + .buildCharacterTile() } diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/systems/HunterSeeker.kt b/src/main/kotlin/org/hexworks/cavesofzircon/systems/HunterSeeker.kt new file mode 100644 index 0000000..201c902 --- /dev/null +++ b/src/main/kotlin/org/hexworks/cavesofzircon/systems/HunterSeeker.kt @@ -0,0 +1,25 @@ +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.Positions + +object HunterSeeker : BaseBehavior() { + + override fun update(entity: GameEntity, context: GameContext): Boolean { + val (world, _, _, player) = context + var hunted = false + world.whenCanSee(entity, player) { path -> + entity.executeCommand(MoveTo( + context = context, + source = entity, + position = Positions.from2DTo3D(path.iterator().next(), player.position.z))) + hunted = true + } + return hunted + } +} diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt b/src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt index f7cb60a..a0eb533 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/world/GameBuilder.kt @@ -2,9 +2,7 @@ package org.hexworks.cavesofzircon.world import org.hexworks.amethyst.api.entity.EntityType import org.hexworks.cavesofzircon.GameConfig -import org.hexworks.cavesofzircon.GameConfig.ARMOR_PER_LEVEL import org.hexworks.cavesofzircon.GameConfig.BATS_PER_LEVEL -import org.hexworks.cavesofzircon.GameConfig.WEAPONS_PER_LEVEL import org.hexworks.cavesofzircon.GameConfig.WORLD_SIZE import org.hexworks.cavesofzircon.attributes.types.Player import org.hexworks.cavesofzircon.builders.EntityFactory @@ -33,8 +31,7 @@ class GameBuilder(val worldSize: Size3D = WORLD_SIZE) { addFungi() addBats() addZircons() - addWeapons() - addArmor() + addZombies() val game = Game.create( player = player, @@ -79,18 +76,10 @@ class GameBuilder(val worldSize: Size3D = WORLD_SIZE) { } } - private fun addWeapons() = also { + private fun addZombies() = also { repeat(world.actualSize().zLength) { level -> - repeat(WEAPONS_PER_LEVEL) { - EntityFactory.newRandomWeapon().addToWorld(level) - } - } - } - - private fun addArmor() = also { - repeat(world.actualSize().zLength) { level -> - repeat(ARMOR_PER_LEVEL) { - EntityFactory.newRandomArmor().addToWorld(level) + repeat(GameConfig.ZOMBIES_PER_LEVEL) { + EntityFactory.newZombie().addToWorld(level) } } } diff --git a/src/main/kotlin/org/hexworks/cavesofzircon/world/World.kt b/src/main/kotlin/org/hexworks/cavesofzircon/world/World.kt index 5cce27c..ca5ceba 100644 --- a/src/main/kotlin/org/hexworks/cavesofzircon/world/World.kt +++ b/src/main/kotlin/org/hexworks/cavesofzircon/world/World.kt @@ -24,6 +24,7 @@ import org.hexworks.zircon.api.screen.Screen import org.hexworks.zircon.api.shape.EllipseFactory import org.hexworks.zircon.api.shape.LineFactory import org.hexworks.zircon.api.uievent.UIEvent +import kotlin.math.abs class World(startingBlocks: Map, visibleSize: Size3D, @@ -150,6 +151,25 @@ class World(startingBlocks: Map, }.orElse(listOf()) } + fun whenCanSee(looker: GameEntity, target: GameEntity, fn: (path: List) -> Unit) { + looker.findAttribute(Vision::class).map { (radius) -> + val level = looker.position.z + if (looker.position.isWithinRangeOf(target.position, radius)) { + val path = LineFactory.buildLine(looker.position.to2DPosition(), target.position.to2DPosition()) + if (path.none { isVisionBlockedAt(Positions.from2DTo3D(it, level)) }) { + fn(path.positions().toList().drop(1)) + } + } + } + } + + private fun Position3D.isWithinRangeOf(other: Position3D, radius: Int): Boolean { + return this.isUnknown().not() + && other.isUnknown().not() + && this.z == other.z + && abs(x - other.x) + abs(y - other.y) <= radius + } + companion object { private val DEFAULT_BLOCK = GameBlockFactory.floor() }