From 6c37a16df7e55754ca89a9de0aaf49c3c778155e Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Thu, 11 Apr 2019 01:54:31 +0200 Subject: [PATCH] Add ever-decreasing health to animate things, and death. --- new/example_client.py | 20 ++++++++++++++++---- new/plomrogue/commands.py | 7 +++++++ new/plomrogue/game.py | 27 +++++++++++++++++++-------- new/plomrogue/things.py | 25 ++++++++++++++++++------- 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/new/example_client.py b/new/example_client.py index 1a7fe74..c9fb4b1 100755 --- a/new/example_client.py +++ b/new/example_client.py @@ -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) diff --git a/new/plomrogue/commands.py b/new/plomrogue/commands.py index 92f583d..9d93e6c 100644 --- a/new/plomrogue/commands.py +++ b/new/plomrogue/commands.py @@ -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]))) diff --git a/new/plomrogue/game.py b/new/plomrogue/game.py index e8e80c0..2e78092 100755 --- a/new/plomrogue/game.py +++ b/new/plomrogue/game.py @@ -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() diff --git a/new/plomrogue/things.py b/new/plomrogue/things.py index 20ce486..112a1ce 100644 --- a/new/plomrogue/things.py +++ b/new/plomrogue/things.py @@ -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 -- 2.30.2