source: nagslang/protagonist.py@ 216:f23ab2dd6ce8

Last change on this file since 216:f23ab2dd6ce8 was 215:325c317cbfa1, checked in by Jeremy Thurgood <firxen@…>, 8 years ago

Better protagonist physicser.

File size: 9.2 KB
RevLine 
[65]1import pymunk
2import pymunk.pygame_util
3
[156]4import math
5
[207]6from nagslang import render
[107]7from nagslang.constants import COLLISION_TYPE_PLAYER, ZORDER_MID
[215]8from nagslang.game_object import GameObject, Physicser, make_body
[76]9from nagslang.mutators import FLIP_H
[65]10from nagslang.resources import resources
[28]11
12
[215]13class ProtagonistPhysicser(Physicser):
14 def __init__(self, space, form_shapes):
15 self._space = space
16 self._form_shapes = form_shapes
17
18 def switch_form(self, old_form, new_form):
19 self._space.remove(self._form_shapes[old_form])
20 shape = self._form_shapes[new_form]
21 self._space.add(shape)
22 for attr, value in shape.protagonist_body_props.iteritems():
23 setattr(shape.body, attr, value)
24
25 def get_shape(self):
26 return self._form_shapes[self.game_object.form]
27
28
[65]29class Protagonist(GameObject):
[28]30 """Representation of our fearless protagonist.
31
32 TODO: Factor out a bunch of this stuff when we need it for other objects.
33 """
34
35 HUMAN_FORM = 'human'
[156]36 HUMAN_FORM_BACK = 'human_back'
[28]37 WOLF_FORM = 'wolf'
[156]38 WOLF_FORM_BACK = 'wolf_back'
[28]39
[93]40 def __init__(self, space, position):
[215]41 physicser = self._make_physics(space, position)
[65]42 self._setup_renderers()
[28]43 self.inventory = {}
44 self.form = self.HUMAN_FORM
[156]45 self.render_form = self.HUMAN_FORM
[28]46
[65]47 super(Protagonist, self).__init__(
[215]48 physicser, self._renderers[self.form])
[107]49 self.zorder = ZORDER_MID
[65]50
51 self.go_human()
52
[215]53 def _make_physics(self, space, position):
54 body = make_body(10, pymunk.inf, position, 0.8)
55 body.velocity_limit = 1000
[65]56
[215]57 human = pymunk.Poly(body, [(-15, -30), (15, -30), (15, 30), (-15, 30)])
58 human.elasticity = 1.0
59 human.collision_type = COLLISION_TYPE_PLAYER
60 human.protagonist_body_props = {
61 'mass': 10,
62 'damping': 0.8,
[93]63 }
[215]64
65 wolf = pymunk.Circle(body, 30)
66 wolf.elasticity = 1.0
67 wolf.collision_type = COLLISION_TYPE_PLAYER
68 wolf.protagonist_body_props = {
69 'mass': 100,
70 'damping': 0.9,
71 }
72
73 return ProtagonistPhysicser(space, {
74 self.HUMAN_FORM: human,
75 self.WOLF_FORM: wolf,
76 })
[65]77
[66]78 def _get_image(self, name, *transforms):
79 return resources.get_image('creatures', name, transforms=transforms)
80
[65]81 def _setup_renderers(self):
[215]82 self.angle = 0
[65]83 self._renderers = {
[207]84 self.HUMAN_FORM: render.AnimatedFacingImageRenderer(
[143]85 (self._get_image('human_1.png'),
86 self._get_image('human_1.png'),
87 self._get_image('human_1.png'),
88 self._get_image('human_2.png'),
89 self._get_image('human_2.png'),
90 self._get_image('human_2.png')),
91 (self._get_image('human_1.png', FLIP_H),
92 self._get_image('human_1.png', FLIP_H),
93 self._get_image('human_1.png', FLIP_H),
94 self._get_image('human_2.png', FLIP_H),
95 self._get_image('human_2.png', FLIP_H),
96 self._get_image('human_2.png', FLIP_H))),
[207]97 self.HUMAN_FORM_BACK: render.AnimatedFacingImageRenderer(
[156]98 (self._get_image('human_back_1.png'),
99 self._get_image('human_back_1.png'),
100 self._get_image('human_back_1.png'),
101 self._get_image('human_back_2.png'),
102 self._get_image('human_back_2.png'),
103 self._get_image('human_back_2.png')),
104 (self._get_image('human_back_1.png', FLIP_H),
105 self._get_image('human_back_1.png', FLIP_H),
106 self._get_image('human_back_1.png', FLIP_H),
107 self._get_image('human_back_2.png', FLIP_H),
108 self._get_image('human_back_2.png', FLIP_H),
109 self._get_image('human_back_2.png', FLIP_H))),
[207]110 self.WOLF_FORM: render.AnimatedFacingImageRenderer(
[143]111 (self._get_image('werewolf_1.png'),
112 self._get_image('werewolf_1.png'),
113 self._get_image('werewolf_1.png'),
114 self._get_image('werewolf_2.png'),
115 self._get_image('werewolf_2.png'),
116 self._get_image('werewolf_2.png')),
117 (self._get_image('werewolf_1.png', FLIP_H),
118 self._get_image('werewolf_1.png', FLIP_H),
119 self._get_image('werewolf_1.png', FLIP_H),
120 self._get_image('werewolf_2.png', FLIP_H),
121 self._get_image('werewolf_2.png', FLIP_H),
122 self._get_image('werewolf_2.png', FLIP_H))),
[207]123 self.WOLF_FORM_BACK: render.AnimatedFacingImageRenderer(
[156]124 (self._get_image('werewolf_back_1.png'),
125 self._get_image('werewolf_back_1.png'),
126 self._get_image('werewolf_back_1.png'),
127 self._get_image('werewolf_back_2.png'),
128 self._get_image('werewolf_back_2.png'),
129 self._get_image('werewolf_back_2.png')),
130 (self._get_image('werewolf_back_1.png', FLIP_H),
131 self._get_image('werewolf_back_1.png', FLIP_H),
132 self._get_image('werewolf_back_1.png', FLIP_H),
133 self._get_image('werewolf_back_2.png', FLIP_H),
134 self._get_image('werewolf_back_2.png', FLIP_H),
135 self._get_image('werewolf_back_2.png', FLIP_H))),
[65]136 }
[123]137 for renderer in self._renderers.values():
138 renderer.set_game_object(self)
[65]139
[28]140 @classmethod
141 def from_saved_state(cls, saved_state):
142 """Create an instance from the provided serialised state.
143 """
144 obj = cls()
145 # TODO: Update from saved state.
146 return obj
147
[93]148 def get_render_angle(self):
149 return self.angle
150
[65]151 def go_werewolf(self):
[215]152 self.physicser.switch_form(self.form, self.WOLF_FORM)
[65]153 self.form = self.WOLF_FORM
154 self.impulse_factor = 4000
[215]155
[156]156 if self.render_form == self.HUMAN_FORM:
157 self.render_form = self.WOLF_FORM
158 elif self.render_form == self.HUMAN_FORM_BACK:
159 self.render_form = self.WOLF_FORM_BACK
160 else:
161 self.render_form = self.WOLF_FORM
162 self.renderer = self._renderers[self.render_form]
[65]163
164 def go_human(self):
[215]165 self.physicser.switch_form(self.form, self.HUMAN_FORM)
[65]166 self.form = self.HUMAN_FORM
167 self.impulse_factor = 500
[215]168
[156]169 if self.render_form == self.WOLF_FORM:
170 self.render_form = self.HUMAN_FORM
171 elif self.render_form == self.WOLF_FORM_BACK:
172 self.render_form = self.HUMAN_FORM_BACK
173 else:
174 self.render_form = self.HUMAN_FORM
175 self.renderer = self._renderers[self.render_form]
176
177 def _switch_to_back(self):
178 if self.render_form == self.HUMAN_FORM:
179 self.render_form = self.HUMAN_FORM_BACK
180 elif self.render_form == self.WOLF_FORM:
181 self.render_form = self.WOLF_FORM_BACK
182 self.renderer = self._renderers[self.render_form]
183
184 def _switch_to_front(self):
185 if self.render_form == self.HUMAN_FORM_BACK:
186 self.render_form = self.HUMAN_FORM
187 elif self.render_form == self.WOLF_FORM_BACK:
188 self.render_form = self.WOLF_FORM
189 self.renderer = self._renderers[self.render_form]
[65]190
191 def set_direction(self, dx, dy):
192 if (dx, dy) == (0, 0):
[143]193 self.renderer.stop()
[65]194 return
[156]195 old_angle = self.angle
[93]196 self.angle = pymunk.Vec2d((dx, dy)).angle
[156]197 # If we've gone from quadrants 2 & 3 to 1 & 4 (or vice versa)
198 # switch between front & back views
199 if self.angle != math.pi:
200 # == math.pi is going straight left, which can't
201 # trigger a front/back swap and simplifies these checks
202 if self.angle > 0 and old_angle != self.angle:
203 self._switch_to_back()
204 elif self.angle < 0 and old_angle != self.angle:
205 self._switch_to_front()
[215]206 self.physicser.apply_impulse(
[65]207 (dx * self.impulse_factor, dy * self.impulse_factor))
[143]208 self.renderer.start()
[65]209
[179]210 def set_position(self, position):
[215]211 self.physicser.position = position
[179]212
213 def copy_state(self, old_protagonist):
[215]214 self.physicser.position = old_protagonist.physicser.position
215 self.physicser.switch_form(self.form, old_protagonist.form)
[179]216 self.form = old_protagonist.form
217 self.angle = old_protagonist.angle
218 self.render_form = old_protagonist.render_form
219 self.inventory = old_protagonist.inventory
220 self.renderer = self._renderers[self.render_form]
221
[65]222 def toggle_form(self):
223 if self.form == self.WOLF_FORM:
224 self.go_human()
225 else:
226 self.go_werewolf()
227
[28]228 def act_on(self, target):
229 """Perform an action on the target.
230 """
231 # TODO: Decide how best to do this.
232 pass
233
234 def attack(self):
235 """Attempt to hurt something.
236 """
237 pass
238
239 def in_wolf_form(self):
240 return self.form == self.WOLF_FORM
241
242 def in_human_form(self):
243 return self.form == self.HUMAN_FORM
244
245 def has_item(self, item):
246 return item in self.inventory
[73]247
248 def environmental_movement(self, dx, dy):
249 if (dx, dy) == (0, 0):
250 return
[215]251 self.physicser.apply_impulse((dx, dy))
Note: See TracBrowser for help on using the repository browser.