From 38900e0b291d5e0f59afaaa239cd237f733b6588 Mon Sep 17 00:00:00 2001 From: Joel Kronqvist Date: Tue, 26 Nov 2024 14:03:23 +0200 Subject: Dying properly --- src/scalevalapokalypsi/Model/Action.scala | 11 +++++----- src/scalevalapokalypsi/Model/Adventure.scala | 25 ++++++++++++++++++---- src/scalevalapokalypsi/Model/Entities/Entity.scala | 19 ++++++++++++++-- .../Model/Entities/NPCs/NPC.scala | 8 ++++--- src/scalevalapokalypsi/Model/Entities/Player.scala | 17 ++++++++------- src/scalevalapokalypsi/Model/SingEffects.scala | 4 +++- 6 files changed, 60 insertions(+), 24 deletions(-) (limited to 'src/scalevalapokalypsi/Model') diff --git a/src/scalevalapokalypsi/Model/Action.scala b/src/scalevalapokalypsi/Model/Action.scala index c7c8a65..21e1286 100644 --- a/src/scalevalapokalypsi/Model/Action.scala +++ b/src/scalevalapokalypsi/Model/Action.scala @@ -40,15 +40,14 @@ class Action(input: String): case "inventory" => Some((false, actor.inventory)) case "sano" => val entityNames = actor.location.getEntityNames.map(_.toLowerCase) - val recipientNamePair = entityNames.map(name => + val recipientNamePair = entityNames.flatMap(name => val possibleNamesWithSuffix = (0 to "ille".length).map(i => - modifiers.takeRight(name.length + i) + modifiers.takeRight(name.length + i) ) possibleNamesWithSuffix.find(s => - s.take(name.length) == name - ) - .map(_.splitAt(name.length)) - ).flatten.headOption + s.take(name.length) == name + ) + .map(_.splitAt(name.length))).headOption val recipient = recipientNamePair.flatMap(p => actor.location.getEntity(p(0)) diff --git a/src/scalevalapokalypsi/Model/Adventure.scala b/src/scalevalapokalypsi/Model/Adventure.scala index ba45abe..b10f7d9 100644 --- a/src/scalevalapokalypsi/Model/Adventure.scala +++ b/src/scalevalapokalypsi/Model/Adventure.scala @@ -46,7 +46,7 @@ class Adventure(val playerNames: Vector[String]): ("Rotten zombie", tangle, 10) ) zombieAttrs.foreach(z => - val zombie = Zombie(z(0), z(1), z(2)) + val zombie = Zombie(this, z(0), z(1), z(2)) npcs += z(0) -> zombie z(1).addEntity(zombie) ) @@ -54,7 +54,7 @@ class Adventure(val playerNames: Vector[String]): def takeNpcTurns(): Unit = npcs.values.foreach(_.act()) - private val gruu = Entity("Gruu", northForest) + private val gruu = Entity(this, "Gruu", northForest) northForest.addEntity(gruu) this.entities += gruu.name -> gruu @@ -67,12 +67,29 @@ class Adventure(val playerNames: Vector[String]): * @return the created player entity */ def addPlayer(name: String): Player = - val newPlayer = Player(name, middle) + val newPlayer = Player(this, name, middle) middle.addEntity(newPlayer) this.entities += name -> newPlayer players += name -> newPlayer newPlayer - + + /** Removes the given entity without further observations. Makes sense in the + * game mostly if the entity's HP is nonpositive. + * + * Removes the entity both from the adventure and the game world + * (i.e. the entitys area). + * + * @param name the name of the entity to remove + * @return whether there was an entity to remove with the given name + */ + def removeEntity(name: String): Boolean = + this.players.remove(name) + this.entities.remove(name).orElse(this.npcs.remove(name)) match + case Some(e) => + e.location.removeEntity(name) + true + case None => false + /** Gets the player entity with the specified name. * * @param name name of the player to find diff --git a/src/scalevalapokalypsi/Model/Entities/Entity.scala b/src/scalevalapokalypsi/Model/Entities/Entity.scala index 336a6b1..aa2a2e2 100644 --- a/src/scalevalapokalypsi/Model/Entities/Entity.scala +++ b/src/scalevalapokalypsi/Model/Entities/Entity.scala @@ -12,6 +12,7 @@ import scala.collection.immutable * @param initialLocation the Area where the entity is instantiated */ class Entity( + val adventure: Adventure, val name: String, initialLocation: Area, initialHP: Int = 100, @@ -31,10 +32,24 @@ class Entity( */ def getVerseAgainst: String = "Esimerkkirivi laulettavaksi" + def isAlive = this.hp > 0 + def takeDamage(amount: Int): Unit = hp -= amount - if hp < 0 then - println("Voi ei, kuolin!") + val event = if this.isAlive then + Event( + Vector(this -> this.condition(0)).toMap, + this.condition(1) + ) + else + println(s"Could remove myself: ${this.adventure.removeEntity(this.name)}") + Event( + Vector(this -> + "Olet täysin menettänyt toimintakykysi. Kaadut elottomana maahan." + ).toMap, + s"${this.name} kaatuu elottomana maahan." + ) + this.location.observeEvent(event) /** Returns a description of the physical condition of this entity, * i.e. the damage it has taken. diff --git a/src/scalevalapokalypsi/Model/Entities/NPCs/NPC.scala b/src/scalevalapokalypsi/Model/Entities/NPCs/NPC.scala index 21709ba..944f2e6 100644 --- a/src/scalevalapokalypsi/Model/Entities/NPCs/NPC.scala +++ b/src/scalevalapokalypsi/Model/Entities/NPCs/NPC.scala @@ -17,19 +17,21 @@ import scala.util.Random * @param initialLocation the NPC’s initial location */ abstract class NPC( + adventure: Adventure, name: String, initialLocation: Area, initialHP: Int, maxHp: Int -) extends Entity(name, initialLocation, initialHP, maxHp): +) extends Entity(adventure, name, initialLocation, initialHP, maxHp): def getDialog: String def act(): Unit class Zombie( + adventure: Adventure, identifier: String, initialLocation: Area, initialHP: Int = 20 -) extends NPC(identifier, initialLocation, initialHP, 20): +) extends NPC(adventure, identifier, initialLocation, initialHP, 20): private val damage = 10 private val dialogs = Vector( @@ -60,7 +62,7 @@ class Zombie( .toVector .lift(directionIndex) .flatMap(this.go(_)) - .map(this.location.observeEvent(_)) + .foreach(this.location.observeEvent(_)) else this.location.observeEvent( this.attack(possibleVictims(index)) diff --git a/src/scalevalapokalypsi/Model/Entities/Player.scala b/src/scalevalapokalypsi/Model/Entities/Player.scala index d6b3529..9fc929d 100644 --- a/src/scalevalapokalypsi/Model/Entities/Player.scala +++ b/src/scalevalapokalypsi/Model/Entities/Player.scala @@ -12,7 +12,11 @@ import scalevalapokalypsi.Model.* * @param name the player's name * @param initialLocation the player’s initial location */ -class Player(name: String, initialLocation: Area) extends Entity(name, initialLocation): +class Player( + adventure: Adventure, + name: String, + initialLocation: Area +) extends Entity(adventure, name, initialLocation): private val observations: Buffer[String] = Buffer.empty private val observedEvents: Buffer[Event] = Buffer.empty @@ -49,8 +53,6 @@ class Player(name: String, initialLocation: Area) extends Entity(name, initialLo * @param singQuality the quality of the song */ def applySingEffect(singQuality: Float): Unit = - val res = this.pendingSingEffect.map(ef => ef(singQuality)) - this.pendingSingEffect = None val qualityDescriptions = if singQuality < .10 then ("säälittävää", "epsilonin suuruinen") @@ -63,11 +65,10 @@ class Player(name: String, initialLocation: Area) extends Entity(name, initialLo else ("erinomaista", "merkittävä") val quality = s"Laulu on ${qualityDescriptions(0)} ja sen vaikutus on ${qualityDescriptions(1)}." - val event = res.map(ev => Event( - ev.inFirstPersons.map((k, v) => (k, s"$quality\n$v")), - s"$quality\n${ev.inThirdPerson}" - )) - event.foreach(this.location.observeEvent(_)) + val event = Event(Map.empty, s"$quality") + this.location.observeEvent(event) + this.pendingSingEffect.map(ef => ef(singQuality)) + this.pendingSingEffect = None diff --git a/src/scalevalapokalypsi/Model/SingEffects.scala b/src/scalevalapokalypsi/Model/SingEffects.scala index 42f5188..23b7d37 100644 --- a/src/scalevalapokalypsi/Model/SingEffects.scala +++ b/src/scalevalapokalypsi/Model/SingEffects.scala @@ -10,4 +10,6 @@ class DefaultSingAttack(target: Entity) extends SingEffect(target): def apply(singQuality: Float): Event = this.target.takeDamage((singQuality * 50).toInt) // TODO: remove magic value val condition = this.target.condition - Event(Map.from(Vector((target, condition(0)))), condition(1)) + Event(Map.empty, "") // The conditions are automatically shown to + // clients through takeDamage, but other effects + // should explain the changes they have. -- cgit v1.2.3