source: nagslang/protagonist.py @ 229:329b3044ddef

Last change on this file since 229:329b3044ddef was 229:329b3044ddef, checked in by Jeremy Thurgood <firxen@…>, 7 years ago

Much better facing renderers.

File size: 8.3 KB
Line 
1import pymunk
2import pymunk.pygame_util
3from pymunk.vec2d import Vec2d
4
5import math
6
7from nagslang import render
8from nagslang.constants import COLLISION_TYPE_PLAYER, ZORDER_MID
9from nagslang.game_object import GameObject, Physicser, make_body
10from nagslang.mutators import FLIP_H
11from nagslang.resources import resources
12
13
14class ProtagonistPhysicser(Physicser):
15    def __init__(self, space, form_shapes):
16        self._space = space
17        self._form_shapes = form_shapes
18
19    def switch_form(self, old_form, new_form):
20        self._space.remove(self._form_shapes[old_form])
21        shape = self._form_shapes[new_form]
22        self._space.add(shape)
23        for attr, value in shape.protagonist_body_props.iteritems():
24            setattr(shape.body, attr, value)
25
26    def get_shape(self):
27        return self._form_shapes[self.game_object.form]
28
29
30class ProtagonistFormSelectionRenderer(render.RendererSelectionRenderer):
31    def select_renderer(self):
32        return self.game_object.form
33
34
35class Protagonist(GameObject):
36    """Representation of our fearless protagonist.
37
38    TODO: Factor out a bunch of this stuff when we need it for other objects.
39    """
40
41    HUMAN_FORM = 'human'
42    WOLF_FORM = 'wolf'
43
44    def __init__(self, space, position):
45        physicser = self._make_physics(space, position)
46        renderer = self._make_renderer()
47        self.inventory = {}
48        self.form = self.HUMAN_FORM
49        self.angle = 0
50        self.is_moving = False
51
52        super(Protagonist, self).__init__(physicser, renderer)
53        self.zorder = ZORDER_MID
54
55        self.go_human()
56
57    def _make_physics(self, space, position):
58        body = make_body(10, pymunk.inf, position, 0.8)
59        body.velocity_limit = 1000
60
61        human = pymunk.Poly(body, [(-15, -30), (15, -30), (15, 30), (-15, 30)])
62        human.elasticity = 1.0
63        human.collision_type = COLLISION_TYPE_PLAYER
64        human.protagonist_body_props = {
65            'mass': 10,
66            'damping': 0.8,
67        }
68
69        wolf = pymunk.Circle(body, 30)
70        wolf.elasticity = 1.0
71        wolf.collision_type = COLLISION_TYPE_PLAYER
72        wolf.protagonist_body_props = {
73            'mass': 100,
74            'damping': 0.9,
75        }
76
77        return ProtagonistPhysicser(space, {
78            self.HUMAN_FORM: human,
79            self.WOLF_FORM: wolf,
80        })
81
82    def _get_image(self, name, *transforms):
83        return resources.get_image('creatures', name, transforms=transforms)
84
85    def _make_renderer(self):
86        return ProtagonistFormSelectionRenderer({
87            self.HUMAN_FORM: render.FacingSelectionRenderer(
88                {
89                    'N': render.MovementAnimatedRenderer(
90                        [self._get_image('human_N_1.png'),
91                         self._get_image('human_N_2.png')], 3),
92                    'S': render.MovementAnimatedRenderer(
93                        [self._get_image('human_S_1.png'),
94                         self._get_image('human_S_2.png')], 3),
95                    'W': render.MovementAnimatedRenderer(
96                        [self._get_image('human_W_1.png'),
97                         self._get_image('human_W_2.png')], 3),
98                    'E': render.MovementAnimatedRenderer(
99                        [self._get_image('human_W_1.png', FLIP_H),
100                         self._get_image('human_W_2.png', FLIP_H)], 3),
101                    'NW': render.MovementAnimatedRenderer(
102                        [self._get_image('human_NW_1.png'),
103                         self._get_image('human_NW_2.png')], 3),
104                    'NE': render.MovementAnimatedRenderer(
105                        [self._get_image('human_NW_1.png', FLIP_H),
106                         self._get_image('human_NW_2.png', FLIP_H)], 3),
107                    'SW': render.MovementAnimatedRenderer(
108                        [self._get_image('human_SW_1.png'),
109                         self._get_image('human_SW_2.png')], 3),
110                    'SE': render.MovementAnimatedRenderer(
111                        [self._get_image('human_SW_1.png', FLIP_H),
112                         self._get_image('human_SW_2.png', FLIP_H)], 3),
113                }),
114            self.WOLF_FORM: render.FacingSelectionRenderer(
115                {
116                    'N': render.MovementAnimatedRenderer(
117                        [self._get_image('werewolf_N_1.png'),
118                         self._get_image('werewolf_N_2.png')], 3),
119                    'S': render.MovementAnimatedRenderer(
120                        [self._get_image('werewolf_S_1.png'),
121                         self._get_image('werewolf_S_2.png')], 3),
122                    'W': render.MovementAnimatedRenderer(
123                        [self._get_image('werewolf_W_1.png'),
124                         self._get_image('werewolf_W_2.png')], 3),
125                    'E': render.MovementAnimatedRenderer(
126                        [self._get_image('werewolf_W_1.png', FLIP_H),
127                         self._get_image('werewolf_W_2.png', FLIP_H)], 3),
128                    'NW': render.MovementAnimatedRenderer(
129                        [self._get_image('werewolf_NW_1.png'),
130                         self._get_image('werewolf_NW_2.png')], 3),
131                    'NE': render.MovementAnimatedRenderer(
132                        [self._get_image('werewolf_NW_1.png', FLIP_H),
133                         self._get_image('werewolf_NW_2.png', FLIP_H)], 3),
134                    'SW': render.MovementAnimatedRenderer(
135                        [self._get_image('werewolf_SW_1.png'),
136                         self._get_image('werewolf_SW_2.png')], 3),
137                    'SE': render.MovementAnimatedRenderer(
138                        [self._get_image('werewolf_SW_1.png', FLIP_H),
139                         self._get_image('werewolf_SW_2.png', FLIP_H)], 3),
140                }),
141        })
142
143    @classmethod
144    def from_saved_state(cls, saved_state):
145        """Create an instance from the provided serialised state.
146        """
147        obj = cls()
148        # TODO: Update from saved state.
149        return obj
150
151    def get_render_angle(self):
152        # No image rotation when rendering, please.
153        return 0
154
155    def get_facing_direction(self):
156        # It's easier to work with a vector than an angle here.
157        vec = Vec2d.unit()
158        vec.angle = self.angle
159        # We probably don't have exactly -1, 0, or 1 here.
160        x = int(round(vec.x))
161        y = int(round(vec.y))
162
163        return {
164            (0, 1): 'N',
165            (0, -1): 'S',
166            (-1, 0): 'W',
167            (1, 0): 'E',
168            (1, 1): 'NE',
169            (1, -1): 'SE',
170            (-1, 1): 'NW',
171            (-1, -1): 'SW',
172        }[(x, y)]
173
174    def go_werewolf(self):
175        self.physicser.switch_form(self.form, self.WOLF_FORM)
176        self.form = self.WOLF_FORM
177        self.impulse_factor = 4000
178
179    def go_human(self):
180        self.physicser.switch_form(self.form, self.HUMAN_FORM)
181        self.form = self.HUMAN_FORM
182        self.impulse_factor = 500
183
184    def set_direction(self, dx, dy):
185        if (dx, dy) == (0, 0):
186            self.is_moving = False
187            return
188        self.is_moving = True
189        self.angle = pymunk.Vec2d((dx, dy)).angle
190        self.physicser.apply_impulse(
191            (dx * self.impulse_factor, dy * self.impulse_factor))
192
193    def set_position(self, position):
194        self.physicser.position = position
195
196    def copy_state(self, old_protagonist):
197        self.physicser.position = old_protagonist.physicser.position
198        self.physicser.switch_form(self.form, old_protagonist.form)
199        self.impulse_factor = old_protagonist.impulse_factor
200        self.form = old_protagonist.form
201        self.angle = old_protagonist.angle
202        self.inventory = old_protagonist.inventory
203
204    def toggle_form(self):
205        if self.form == self.WOLF_FORM:
206            self.go_human()
207        else:
208            self.go_werewolf()
209
210    def act_on(self, target):
211        """Perform an action on the target.
212        """
213        # TODO: Decide how best to do this.
214        pass
215
216    def attack(self):
217        """Attempt to hurt something.
218        """
219        pass
220
221    def in_wolf_form(self):
222        return self.form == self.WOLF_FORM
223
224    def in_human_form(self):
225        return self.form == self.HUMAN_FORM
226
227    def has_item(self, item):
228        return item in self.inventory
229
230    def environmental_movement(self, dx, dy):
231        if (dx, dy) == (0, 0):
232            return
233        self.physicser.apply_impulse((dx, dy))
Note: See TracBrowser for help on using the repository browser.