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
 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
 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,
                          '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
                          '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)))
 
 
         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):
 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'])]
         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)
                                              ['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)
                        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'
 
     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:
 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)))
             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])))
             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.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
 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
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.player_id = 0
+        self.player_is_alive = True
 
     @property
     def player(self):
 
     @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
         (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)
         """
         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)
             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):
                 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,
                          '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,
                          '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)))
             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]))
         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):
             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()
 
             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):
         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).
 
         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._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:
         try:
             self.task.check()
         except GameError as e:
@@ -163,8 +172,10 @@ class ThingAnimate(Thing):
 
 class ThingHuman(ThingAnimate):
     type_ = 'human'
 
 class ThingHuman(ThingAnimate):
     type_ = 'human'
+    health = 100
 
 
 
 class ThingMonster(ThingAnimate):
     type_ = 'monster'
 
 
 
 class ThingMonster(ThingAnimate):
     type_ = 'monster'
+    health = 50