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
Line 
1import pymunk
2import pymunk.pygame_util
3
4import math
5
6from nagslang import render
7from nagslang.constants import COLLISION_TYPE_PLAYER, ZORDER_MID
8from nagslang.game_object import GameObject, Physicser, make_body
9from nagslang.mutators import FLIP_H
10from nagslang.resources import resources
11
12
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
29class Protagonist(GameObject):
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'
36 HUMAN_FORM_BACK = 'human_back'
37 WOLF_FORM = 'wolf'
38 WOLF_FORM_BACK = 'wolf_back'
39
40 def __init__(self, space, position):
41 physicser = self._make_physics(space, position)
42 self._setup_renderers()
43 self.inventory = {}
44 self.form = self.HUMAN_FORM
45 self.render_form = self.HUMAN_FORM
46
47 super(Protagonist, self).__init__(
48 physicser, self._renderers[self.form])
49 self.zorder = ZORDER_MID
50
51 self.go_human()
52
53 def _make_physics(self, space, position):
54 body = make_body(10, pymunk.inf, position, 0.8)
55 body.velocity_limit = 1000
56
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,
63 }
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 })
77
78 def _get_image(self, name, *transforms):
79 return resources.get_image('creatures', name, transforms=transforms)
80
81 def _setup_renderers(self):
82 self.angle = 0
83 self._renderers = {
84 self.HUMAN_FORM: render.AnimatedFacingImageRenderer(
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))),
97 self.HUMAN_FORM_BACK: render.AnimatedFacingImageRenderer(
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))),
110 self.WOLF_FORM: render.AnimatedFacingImageRenderer(
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))),
123 self.WOLF_FORM_BACK: render.AnimatedFacingImageRenderer(
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))),
136 }
137 for renderer in self._renderers.values():
138 renderer.set_game_object(self)
139
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
148 def get_render_angle(self):
149 return self.angle
150
151 def go_werewolf(self):
152 self.physicser.switch_form(self.form, self.WOLF_FORM)
153 self.form = self.WOLF_FORM
154 self.impulse_factor = 4000
155
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]
163
164 def go_human(self):
165 self.physicser.switch_form(self.form, self.HUMAN_FORM)
166 self.form = self.HUMAN_FORM
167 self.impulse_factor = 500
168
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]
190
191 def set_direction(self, dx, dy):
192 if (dx, dy) == (0, 0):
193 self.renderer.stop()
194 return
195 old_angle = self.angle
196 self.angle = pymunk.Vec2d((dx, dy)).angle
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()
206 self.physicser.apply_impulse(
207 (dx * self.impulse_factor, dy * self.impulse_factor))
208 self.renderer.start()
209
210 def set_position(self, position):
211 self.physicser.position = position
212
213 def copy_state(self, old_protagonist):
214 self.physicser.position = old_protagonist.physicser.position
215 self.physicser.switch_form(self.form, old_protagonist.form)
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
222 def toggle_form(self):
223 if self.form == self.WOLF_FORM:
224 self.go_human()
225 else:
226 self.go_werewolf()
227
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
247
248 def environmental_movement(self, dx, dy):
249 if (dx, dy) == (0, 0):
250 return
251 self.physicser.apply_impulse((dx, dy))
Note: See TracBrowser for help on using the repository browser.