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

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

Much better facing renderers.

File size: 8.3 KB
RevLine 
[65]1import pymunk
2import pymunk.pygame_util
[229]3from pymunk.vec2d import Vec2d
[65]4
[156]5import math
6
[207]7from nagslang import render
[107]8from nagslang.constants import COLLISION_TYPE_PLAYER, ZORDER_MID
[215]9from nagslang.game_object import GameObject, Physicser, make_body
[76]10from nagslang.mutators import FLIP_H
[65]11from nagslang.resources import resources
[28]12
13
[215]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
[218]30class ProtagonistFormSelectionRenderer(render.RendererSelectionRenderer):
31 def select_renderer(self):
[229]32 return self.game_object.form
[218]33
34
[65]35class Protagonist(GameObject):
[28]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
[93]44 def __init__(self, space, position):
[215]45 physicser = self._make_physics(space, position)
[218]46 renderer = self._make_renderer()
[28]47 self.inventory = {}
48 self.form = self.HUMAN_FORM
[218]49 self.angle = 0
50 self.is_moving = False
[28]51
[218]52 super(Protagonist, self).__init__(physicser, renderer)
[107]53 self.zorder = ZORDER_MID
[65]54
55 self.go_human()
56
[215]57 def _make_physics(self, space, position):
58 body = make_body(10, pymunk.inf, position, 0.8)
59 body.velocity_limit = 1000
[65]60
[215]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,
[93]67 }
[215]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 })
[65]81
[66]82 def _get_image(self, name, *transforms):
83 return resources.get_image('creatures', name, transforms=transforms)
84
[218]85 def _make_renderer(self):
86 return ProtagonistFormSelectionRenderer({
[217]87 self.HUMAN_FORM: render.FacingSelectionRenderer(
88 {
[229]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),
[217]113 }),
114 self.WOLF_FORM: render.FacingSelectionRenderer(
115 {
[229]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),
[217]140 }),
[218]141 })
[65]142
[28]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
[93]151 def get_render_angle(self):
[229]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)]
[93]173
[65]174 def go_werewolf(self):
[215]175 self.physicser.switch_form(self.form, self.WOLF_FORM)
[65]176 self.form = self.WOLF_FORM
177 self.impulse_factor = 4000
[215]178
[65]179 def go_human(self):
[215]180 self.physicser.switch_form(self.form, self.HUMAN_FORM)
[65]181 self.form = self.HUMAN_FORM
182 self.impulse_factor = 500
[215]183
[65]184 def set_direction(self, dx, dy):
185 if (dx, dy) == (0, 0):
[218]186 self.is_moving = False
[65]187 return
[218]188 self.is_moving = True
[93]189 self.angle = pymunk.Vec2d((dx, dy)).angle
[215]190 self.physicser.apply_impulse(
[65]191 (dx * self.impulse_factor, dy * self.impulse_factor))
192
[179]193 def set_position(self, position):
[215]194 self.physicser.position = position
[179]195
196 def copy_state(self, old_protagonist):
[215]197 self.physicser.position = old_protagonist.physicser.position
198 self.physicser.switch_form(self.form, old_protagonist.form)
[217]199 self.impulse_factor = old_protagonist.impulse_factor
[179]200 self.form = old_protagonist.form
201 self.angle = old_protagonist.angle
202 self.inventory = old_protagonist.inventory
203
[65]204 def toggle_form(self):
205 if self.form == self.WOLF_FORM:
206 self.go_human()
207 else:
208 self.go_werewolf()
209
[28]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
[73]229
230 def environmental_movement(self, dx, dy):
231 if (dx, dy) == (0, 0):
232 return
[215]233 self.physicser.apply_impulse((dx, dy))
Note: See TracBrowser for help on using the repository browser.