# HG changeset patch # User Jeremy Thurgood # Date 1378389504 -7200 # Node ID 9b56e954c674ce9d1797c3976905f807f1771920 # Parent 7bb6296024c40061caff37c0fd8c7737f4a41f71 Protagonist actions, now required for operating doors. diff -r 7bb6296024c4 -r 9b56e954c674 data/levels/level1 --- a/data/levels/level1 Thu Sep 05 14:12:55 2013 +0200 +++ b/data/levels/level1 Thu Sep 05 15:58:24 2013 +0200 @@ -30,20 +30,6 @@ classname: puzzle.StateLogicalAndPuzzler name: both_switches - args: - - [400, 400] - - level2 - - [900, 200] - - 0 - classname: Door -- args: - - [600, 200] - - level1 - - [600, 700] - - 0 - - door_switch - classname: Door - name: switch_door -- args: - [620, 220] - door_switch classname: FloorLight @@ -58,6 +44,20 @@ - door_switch classname: Bulkhead name: switch_bulkhead +- args: + - [410, 400] + - level2 + - [900, 200] + - 0 + classname: Door +- args: + - [561, 250] + - level1 + - [600, 700] + - 0 + - door_switch + classname: Door + name: switch_door lines: - - [750, 680] - [800, 680] diff -r 7bb6296024c4 -r 9b56e954c674 nagslang/constants.py --- a/nagslang/constants.py Thu Sep 05 14:12:55 2013 +0200 +++ b/nagslang/constants.py Thu Sep 05 15:58:24 2013 +0200 @@ -35,6 +35,13 @@ COLLISION_TYPE_DOOR, ] +NON_GAME_OBJECT_COLLIDERS = [ + # These collision types are excluded from action checks, etc. + COLLISION_TYPE_WALL, + COLLISION_TYPE_PROJECTILE, + COLLISION_TYPE_WEREWOLF_ATTACK, +] + ZORDER_FLOOR = 0 ZORDER_LOW = 1 ZORDER_MID = 2 diff -r 7bb6296024c4 -r 9b56e954c674 nagslang/environment.py --- a/nagslang/environment.py Thu Sep 05 14:12:55 2013 +0200 +++ b/nagslang/environment.py Thu Sep 05 15:58:24 2013 +0200 @@ -74,11 +74,22 @@ return self.func(protagonist) +class PuzzleStateCondition(ProtagonistCondition): + """Condition that is met if the provided function returns `True`. + """ + def __init__(self, puzzler): + self.puzzler = puzzler + + def check(self, protagonist): + return self.puzzler.get_state() + + class Action(object): """Representation of an action that can be performed. If the (optional) condition is met, the provided function will be called - with the protagonist and the target as parameters. + with the protagonist as a parameter. It is assumed that the function + already knows about the target. """ def __init__(self, func, condition=None): self.func = func @@ -89,10 +100,10 @@ return True return self.condition.check(protagonist) - def perform(self, protagonist, target): + def perform(self, protagonist): if not self.check(protagonist): raise ValueError("Attempt to perform invalid action.") - return self.func(protagonist, target) + return self.func(protagonist) class Interactible(object): @@ -102,6 +113,9 @@ def __init__(self, *actions): self.actions = actions + def set_game_object(self, game_object): + self.game_object = game_object + def select_action(self, protagonist): """Select a possible action given the protagonist's state. """ diff -r 7bb6296024c4 -r 9b56e954c674 nagslang/game_object.py --- a/nagslang/game_object.py Thu Sep 05 14:12:55 2013 +0200 +++ b/nagslang/game_object.py Thu Sep 05 15:58:24 2013 +0200 @@ -3,6 +3,7 @@ import math +from nagslang import environment from nagslang import puzzle from nagslang import render from nagslang.constants import ( @@ -105,7 +106,8 @@ zorder = ZORDER_LOW is_moving = False # `True` if a movement animation should play. - def __init__(self, physicser, renderer, puzzler=None, overlay=None): + def __init__(self, physicser, renderer, puzzler=None, overlay=None, + interactible=None): self.physicser = physicser physicser.set_game_object(self) self.physicser.add_to_space() @@ -117,6 +119,9 @@ self.overlay = overlay if overlay is not None: self.overlay.set_game_object(self) + self.interactible = interactible + if interactible is not None: + self.interactible.set_game_object(self) def get_space(self): return self.physicser.get_space() @@ -244,26 +249,26 @@ def __init__(self, space, position, destination, dest_pos, angle, key_state=None): body = make_body(pymunk.inf, pymunk.inf, position, damping=0.5) - self.shape = pymunk.Poly( - body, [(-4, -30), (4, -30), (4, 30), (-4, 30)]) + self.shape = pymunk.Circle(body, 30) self.shape.collision_type = COLLISION_TYPE_DOOR self.shape.body.angle = float(angle) / 180 * math.pi self.shape.sensor = True self.destination = destination self.dest_pos = tuple(dest_pos) - if key_state is None: - puzzler = puzzle.YesPuzzler() - else: + puzzler = None + action = environment.Action(self._post_door_event) + if key_state is not None: puzzler = puzzle.StateProxyPuzzler(key_state) + action.condition = environment.PuzzleStateCondition(puzzler) super(Door, self).__init__( SingleShapePhysicser(space, self.shape), render.ImageRenderer(resources.get_image('objects', 'door.png')), puzzler, + interactible=environment.Interactible(action), ) - def collide_with_protagonist(self, protagonist): - if self.puzzler.get_state(): - DoorEvent.post(self.destination, self.dest_pos) + def _post_door_event(self, protagonist): + DoorEvent.post(self.destination, self.dest_pos) @classmethod def requires(cls): diff -r 7bb6296024c4 -r 9b56e954c674 nagslang/protagonist.py --- a/nagslang/protagonist.py Thu Sep 05 14:12:55 2013 +0200 +++ b/nagslang/protagonist.py Thu Sep 05 15:58:24 2013 +0200 @@ -3,9 +3,10 @@ from pymunk.vec2d import Vec2d from nagslang import render -from nagslang.constants import COLLISION_TYPE_PLAYER, ZORDER_MID, \ - WEREWOLF_SOAK_FACTOR, PROTAGONIST_HEALTH_MIN_LEVEL, \ - PROTAGONIST_HEALTH_MAX_LEVEL +from nagslang.constants import ( + COLLISION_TYPE_PLAYER, ZORDER_MID, WEREWOLF_SOAK_FACTOR, + PROTAGONIST_HEALTH_MIN_LEVEL, PROTAGONIST_HEALTH_MAX_LEVEL, + NON_GAME_OBJECT_COLLIDERS) from nagslang.events import FireEvent from nagslang.game_object import GameObject, Physicser, make_body from nagslang.mutators import FLIP_H @@ -216,11 +217,28 @@ else: self.go_werewolf() - def act_on(self, target): + def get_current_interactible(self): + for shape in self.get_space().shape_query(self.get_shape()): + if shape.collision_type in NON_GAME_OBJECT_COLLIDERS: + # No game object here. + continue + interactible = shape.physicser.game_object.interactible + if interactible is not None: + return interactible + return None + + def perform_action(self): """Perform an action on the target. """ - # TODO: Decide how best to do this. - pass + interactible = self.get_current_interactible() + if interactible is None: + # Nothing to interact with. + return + action = interactible.select_action(self) + if action is None: + # Nothing to do with it. + return + return action.perform(self) def attack(self): """Attempt to hurt something. diff -r 7bb6296024c4 -r 9b56e954c674 nagslang/screens/area.py --- a/nagslang/screens/area.py Thu Sep 05 14:12:55 2013 +0200 +++ b/nagslang/screens/area.py Thu Sep 05 15:58:24 2013 +0200 @@ -129,9 +129,11 @@ if ev.key == pygame.locals.K_c: self.protagonist.toggle_form() self.world.transformations += 1 - if ev.key == pygame.locals.K_SPACE: + if ev.key == pygame.locals.K_z: self.world.attacks += 1 self.protagonist.attack() + if ev.key == pygame.locals.K_SPACE: + self.protagonist.perform_action() elif DoorEvent.matches(ev): self.protagonist.set_position(ev.dest_pos) if ev.destination != self.name: