source: nagslang/protagonist.py@ 217:d98daba73055

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

Composition-based renderers.

File size: 8.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 = {
[217]84 self.HUMAN_FORM: render.FacingSelectionRenderer(
85 {
86 'left': render.MovementAnimatedRenderer(
87 [self._get_image('human_1.png'),
88 self._get_image('human_2.png')], 3),
89 'right': render.MovementAnimatedRenderer(
90 [self._get_image('human_1.png', FLIP_H),
91 self._get_image('human_2.png', FLIP_H)], 3),
92 }),
93 self.HUMAN_FORM_BACK: render.FacingSelectionRenderer(
94 {
95 'left': render.MovementAnimatedRenderer(
96 [self._get_image('human_back_1.png'),
97 self._get_image('human_back_2.png')], 3),
98 'right': render.MovementAnimatedRenderer(
99 [self._get_image('human_back_1.png', FLIP_H),
100 self._get_image('human_back_2.png', FLIP_H)], 3),
101 }),
102 self.WOLF_FORM: render.FacingSelectionRenderer(
103 {
104 'left': render.MovementAnimatedRenderer(
105 [self._get_image('werewolf_1.png'),
106 self._get_image('werewolf_2.png')], 3),
107 'right': render.MovementAnimatedRenderer(
108 [self._get_image('werewolf_1.png', FLIP_H),
109 self._get_image('werewolf_2.png', FLIP_H)], 3),
110 }),
111 self.WOLF_FORM_BACK: render.FacingSelectionRenderer(
112 {
113 'left': render.MovementAnimatedRenderer(
114 [self._get_image('werewolf_back_1.png'),
115 self._get_image('werewolf_back_2.png')], 3),
116 'right': render.MovementAnimatedRenderer(
117 [self._get_image('werewolf_back_1.png', FLIP_H),
118 self._get_image('werewolf_back_2.png', FLIP_H)], 3),
119 }),
[65]120 }
[123]121 for renderer in self._renderers.values():
122 renderer.set_game_object(self)
[65]123
[28]124 @classmethod
125 def from_saved_state(cls, saved_state):
126 """Create an instance from the provided serialised state.
127 """
128 obj = cls()
129 # TODO: Update from saved state.
130 return obj
131
[93]132 def get_render_angle(self):
133 return self.angle
134
[65]135 def go_werewolf(self):
[215]136 self.physicser.switch_form(self.form, self.WOLF_FORM)
[65]137 self.form = self.WOLF_FORM
138 self.impulse_factor = 4000
[215]139
[156]140 if self.render_form == self.HUMAN_FORM:
141 self.render_form = self.WOLF_FORM
142 elif self.render_form == self.HUMAN_FORM_BACK:
143 self.render_form = self.WOLF_FORM_BACK
144 else:
145 self.render_form = self.WOLF_FORM
146 self.renderer = self._renderers[self.render_form]
[65]147
148 def go_human(self):
[215]149 self.physicser.switch_form(self.form, self.HUMAN_FORM)
[65]150 self.form = self.HUMAN_FORM
151 self.impulse_factor = 500
[215]152
[156]153 if self.render_form == self.WOLF_FORM:
154 self.render_form = self.HUMAN_FORM
155 elif self.render_form == self.WOLF_FORM_BACK:
156 self.render_form = self.HUMAN_FORM_BACK
157 else:
158 self.render_form = self.HUMAN_FORM
159 self.renderer = self._renderers[self.render_form]
160
161 def _switch_to_back(self):
162 if self.render_form == self.HUMAN_FORM:
163 self.render_form = self.HUMAN_FORM_BACK
164 elif self.render_form == self.WOLF_FORM:
165 self.render_form = self.WOLF_FORM_BACK
166 self.renderer = self._renderers[self.render_form]
167
168 def _switch_to_front(self):
169 if self.render_form == self.HUMAN_FORM_BACK:
170 self.render_form = self.HUMAN_FORM
171 elif self.render_form == self.WOLF_FORM_BACK:
172 self.render_form = self.WOLF_FORM
173 self.renderer = self._renderers[self.render_form]
[65]174
175 def set_direction(self, dx, dy):
176 if (dx, dy) == (0, 0):
177 return
[156]178 old_angle = self.angle
[93]179 self.angle = pymunk.Vec2d((dx, dy)).angle
[156]180 # If we've gone from quadrants 2 & 3 to 1 & 4 (or vice versa)
181 # switch between front & back views
182 if self.angle != math.pi:
183 # == math.pi is going straight left, which can't
184 # trigger a front/back swap and simplifies these checks
185 if self.angle > 0 and old_angle != self.angle:
186 self._switch_to_back()
187 elif self.angle < 0 and old_angle != self.angle:
188 self._switch_to_front()
[215]189 self.physicser.apply_impulse(
[65]190 (dx * self.impulse_factor, dy * self.impulse_factor))
191
[179]192 def set_position(self, position):
[215]193 self.physicser.position = position
[179]194
195 def copy_state(self, old_protagonist):
[215]196 self.physicser.position = old_protagonist.physicser.position
197 self.physicser.switch_form(self.form, old_protagonist.form)
[217]198 self.impulse_factor = old_protagonist.impulse_factor
[179]199 self.form = old_protagonist.form
200 self.angle = old_protagonist.angle
201 self.render_form = old_protagonist.render_form
202 self.inventory = old_protagonist.inventory
203 self.renderer = self._renderers[self.render_form]
204
[65]205 def toggle_form(self):
206 if self.form == self.WOLF_FORM:
207 self.go_human()
208 else:
209 self.go_werewolf()
210
[28]211 def act_on(self, target):
212 """Perform an action on the target.
213 """
214 # TODO: Decide how best to do this.
215 pass
216
217 def attack(self):
218 """Attempt to hurt something.
219 """
220 pass
221
222 def in_wolf_form(self):
223 return self.form == self.WOLF_FORM
224
225 def in_human_form(self):
226 return self.form == self.HUMAN_FORM
227
228 def has_item(self, item):
229 return item in self.inventory
[73]230
231 def environmental_movement(self, dx, dy):
232 if (dx, dy) == (0, 0):
233 return
[215]234 self.physicser.apply_impulse((dx, dy))
Note: See TracBrowser for help on using the repository browser.