home · contact · privacy
Add ever-decreasing health to animate things, and death.
authorChristian Heller <c.heller@plomlompom.de>
Wed, 10 Apr 2019 23:54:31 +0000 (01:54 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Wed, 10 Apr 2019 23:54:31 +0000 (01:54 +0200)
new/example_client.py
new/plomrogue/commands.py
new/plomrogue/game.py
new/plomrogue/things.py

index 1a7fe74bc501aa8ecfb6fbc780dbe37881a7dee8..c9fb4b1f638cce13f4b638cf18829ba6b506a66c 100755 (executable)
@@ -3,7 +3,8 @@ import curses
 import socket
 import threading
 from plomrogue.parser import ArgError, Parser
-from plomrogue.commands import cmd_MAP, cmd_THING_POS, cmd_PLAYER_ID
+from plomrogue.commands import (cmd_MAP, cmd_THING_POS, cmd_PLAYER_ID,
+                                cmd_THING_HEALTH)
 from plomrogue.game import Game, WorldBase
 from plomrogue.mapping import MapHex
 from plomrogue.io import PlomSocket
@@ -149,6 +150,7 @@ class Game:
                          'MAP': cmd_MAP,
                          'PICKABLE_ITEMS': cmd_PICKABLE_ITEMS,
                          'THING_TYPE': cmd_THING_TYPE,
+                         'THING_HEALTH': cmd_THING_HEALTH,
                          'THING_POS': cmd_THING_POS}
         self.log_text = ''
         self.do_quit = False
@@ -448,6 +450,14 @@ class TurnWidget(Widget):
         self.safe_write((str(self.tui.game.world.turn), curses.color_pair(2)))
 
 
+class HealthWidget(Widget):
+
+    def draw(self):
+        if hasattr(self.tui.game.world.player, 'health'):
+            self.safe_write((str(self.tui.game.world.player.health),
+                             curses.color_pair(2)))
+
+
 class TextLineWidget(Widget):
 
     def __init__(self, text_line, *args, **kwargs):
@@ -605,15 +615,17 @@ class TUI:
         edit_widget.children += [edit_line_widget]
         turn_widget = TextLineWidget('TURN:', self, (2, 0), (1, 20))
         turn_widget.children += [TurnWidget(self, (2, 6), (1, 14), ['turn'])]
-        log_widget = LogWidget(self, (4, 0), (None, 20), ['log'])
-        descriptor_widget = DescriptorWidget(self, (4, 0), (None, 20),
+        health_widget = TextLineWidget('HEALTH:', self, (3, 0), (1, 20))
+        health_widget.children += [HealthWidget(self, (3, 8), (1, 12), ['turn'])]
+        log_widget = LogWidget(self, (5, 0), (None, 20), ['log'])
+        descriptor_widget = DescriptorWidget(self, (5, 0), (None, 20),
                                              ['map'], False)
         map_widget = MapWidget(self, (0, 21), (None, None), ['map'])
         inventory_widget = InventoryWidget(self, (0, 21), (None, None),
                                            ['inventory'], False)
         pickable_items_widget = PickableItemsWidget(self, (0, 21), (None, None),
                                                     ['pickable_items'], False)
-        top_widgets = [edit_widget, turn_widget, log_widget,
+        top_widgets = [edit_widget, turn_widget, health_widget, log_widget,
                        descriptor_widget, map_widget, inventory_widget,
                        pickable_items_widget]
         popup_widget = PopUpWidget(self, (0, 0), (1, 1), visible=False)
index 92f583dc4c90df047f0fbe5c036d2ee62d8bbc60..9d93e6cb8871b384986a8577b646492a6f04b374 100644 (file)
@@ -46,6 +46,11 @@ def cmd_THING_INVENTORY(game, id_, ids):
     t.inventory = ids  # TODO: test whether valid IDs
 cmd_THING_INVENTORY.argtypes = 'int:nonneg seq:int:nonneg'
 
+def cmd_THING_HEALTH(game, id_, health):
+    t = game.world.get_thing(id_)
+    t.health = health
+cmd_THING_HEALTH.argtypes = 'int:nonneg int:nonneg'
+
 def cmd_GET_PICKABLE_ITEMS(game, connection_id):
     pickable_ids = game.world.player.get_pickable_items()
     if len(pickable_ids) > 0:
@@ -92,6 +97,8 @@ def cmd_SAVE(game):
             write(f, 'THING_TYPE %s %s' % (thing.id_, thing.type_))
             write(f, 'THING_POS %s %s' % (thing.id_,
                                           stringify_yx(thing.position)))
+            if hasattr(thing, 'health'):
+                write(f, 'THING_HEALTH %s %s' % (thing.id_, thing.health))
             if len(thing.inventory) > 0:
                 write(f, 'THING_INVENTORY %s %s' %
                       (thing.id_,','.join([str(i) for i in thing.inventory])))
index e8e80c0f1e8785f00771a3700fce11e5fb0a194e..2e780928cd073d6e23d2248ff30307abfcc2b40e 100755 (executable)
@@ -1,10 +1,12 @@
 from plomrogue.tasks import Task_WAIT, Task_MOVE, Task_PICKUP, Task_DROP
-from plomrogue.errors import ArgError
-from plomrogue.commands import (cmd_GEN_WORLD, cmd_GET_GAMESTATE, cmd_MAP,
-                                cmd_MAP, cmd_THING_TYPE, cmd_THING_POS,
-                                cmd_THING_INVENTORY, cmd_GET_PICKABLE_ITEMS,
-                                cmd_TERRAIN_LINE, cmd_PLAYER_ID, cmd_TURN,
-                                cmd_SWITCH_PLAYER, cmd_SAVE)
+from plomrogue.errors import ArgError, GameError
+from plomrogue.commands import (cmd_GEN_WORLD, cmd_GET_GAMESTATE,
+                                cmd_MAP, cmd_MAP, cmd_THING_TYPE,
+                                cmd_THING_POS, cmd_THING_INVENTORY,
+                                cmd_THING_HEALTH,
+                                cmd_GET_PICKABLE_ITEMS,
+                                cmd_TERRAIN_LINE, cmd_PLAYER_ID,
+                                cmd_TURN, cmd_SWITCH_PLAYER, cmd_SAVE)
 from plomrogue.mapping import MapHex
 from plomrogue.parser import Parser
 from plomrogue.io import GameIO
@@ -37,6 +39,7 @@ class World(WorldBase):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.player_id = 0
+        self.player_is_alive = True
 
     @property
     def player(self):
@@ -60,7 +63,9 @@ class World(WorldBase):
         (after incrementing the world turn) all that come before the
         player; then the player's .proceed() is run, and if it does
         not finish his task, the loop starts at the beginning. Once
-        the player's task is finished, the loop breaks.
+        the player's task is finished, or the player is dead, the loop
+        breaks.
+
         """
         while True:
             player_i = self.things.index(self.player)
@@ -70,7 +75,7 @@ class World(WorldBase):
             for thing in self.things[:player_i]:
                 thing.proceed()
             self.player.proceed(is_AI=False)
-            if self.player.task is None:
+            if self.player.task is None or not self.player_is_alive:
                 break
 
     def make_new(self, yx, seed):
@@ -117,6 +122,7 @@ class Game:
                          'MAP': cmd_MAP,
                          'THING_TYPE': cmd_THING_TYPE,
                          'THING_POS': cmd_THING_POS,
+                         'THING_HEALTH': cmd_THING_HEALTH,
                          'THING_INVENTORY': cmd_THING_INVENTORY,
                          'TERRAIN_LINE': cmd_TERRAIN_LINE,
                          'GET_PICKABLE_ITEMS': cmd_GET_PICKABLE_ITEMS,
@@ -151,6 +157,9 @@ class Game:
             self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
             self.io.send('THING_POS %s %s' % (thing.id_,
                                               stringify_yx(thing.position)))
+            if hasattr(thing, 'health'):
+                self.io.send('THING_HEALTH %s %s' % (thing.id_,
+                                                     thing.health))
         if len(self.world.player.inventory) > 0:
             self.io.send('PLAYER_INVENTORY %s' %
                          ','.join([str(i) for i in self.world.player.inventory]))
@@ -184,6 +193,8 @@ class Game:
             return p
 
         def cmd_TASK_colon(task_name, game, *args):
+            if not game.world.player_is_alive:
+                raise GameError('You are dead.')
             game.world.player.set_task(task_name, args)
             game.proceed()
 
index 20ce4867c2211882b506de309d8881eb16842379..112a1ce9449399700afe28e33b4f4c1a52990c8e 100644 (file)
@@ -94,18 +94,27 @@ class ThingAnimate(Thing):
         self.task.check()  # will throw GameError if necessary
 
     def proceed(self, is_AI=True):
-        """Further the thing in its tasks.
+        """Further the thing in its tasks, decrease its health.
 
-        Decrements .task.todo; if it thus falls to <= 0, enacts method
-        whose name is 'task_' + self.task.name and sets .task =
-        None. If is_AI, calls .decide_task to decide a self.task.
-
-        Before doing anything, ensures an empty map visibility stencil
-        and checks that task is still possible, and aborts it
+        First, ensures an empty map, decrements .health and kills
+        thing if crossing zero (removes from self.world.things for AI
+        thing, or unsets self.world.player_is_alive for player thing);
+        then checks that self.task is still possible and aborts if
         otherwise (for AI things, decides a new task).
 
+        Then decrements .task.todo; if it thus falls to <= 0, enacts
+        method whose name is 'task_' + self.task.name and sets .task =
+        None. If is_AI, calls .decide_task to decide a self.task.
+
         """
         self._stencil = None
+        self.health -= 1
+        if self.health <= 0:
+            if self is self.world.player:
+                self.world.player_is_alive = False
+            else:
+                del self.world.things[self.world.things.index(self)]
+            return
         try:
             self.task.check()
         except GameError as e:
@@ -163,8 +172,10 @@ class ThingAnimate(Thing):
 
 class ThingHuman(ThingAnimate):
     type_ = 'human'
+    health = 100
 
 
 
 class ThingMonster(ThingAnimate):
     type_ = 'monster'
+    health = 50