source: nagslang/protagonist.py@ 228:316cff60a309

Last change on this file since 228:316cff60a309 was 218:9e2ef2f15035, checked in by Jeremy Thurgood <firxen@…>, 8 years ago

Better rendering and movement detection.

File size: 8.1 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 ProtagonistFormSelectionRenderer(render.RendererSelectionRenderer):
30 def select_renderer(self):
31 return self.game_object.render_form
32
33
34class Protagonist(GameObject):
35 """Representation of our fearless protagonist.
36
37 TODO: Factor out a bunch of this stuff when we need it for other objects.
38 """
39
40 HUMAN_FORM = 'human'
41 HUMAN_FORM_BACK = 'human_back'
42 WOLF_FORM = 'wolf'
43 WOLF_FORM_BACK = 'wolf_back'
44
45 def __init__(self, space, position):
46 physicser = self._make_physics(space, position)
47 renderer = self._make_renderer()
48 self.inventory = {}
49 self.form = self.HUMAN_FORM
50 self.render_form = self.HUMAN_FORM
51 self.angle = 0
52 self.is_moving = False
53
54 super(Protagonist, self).__init__(physicser, renderer)
55 self.zorder = ZORDER_MID
56
57 self.go_human()
58
59 def _make_physics(self, space, position):
60 body = make_body(10, pymunk.inf, position, 0.8)
61 body.velocity_limit = 1000
62
63 human = pymunk.Poly(body, [(-15, -30), (15, -30), (15, 30), (-15, 30)])
64 human.elasticity = 1.0
65 human.collision_type = COLLISION_TYPE_PLAYER
66 human.protagonist_body_props = {
67 'mass': 10,
68 'damping': 0.8,
69 }
70
71 wolf = pymunk.Circle(body, 30)
72 wolf.elasticity = 1.0
73 wolf.collision_type = COLLISION_TYPE_PLAYER
74 wolf.protagonist_body_props = {
75 'mass': 100,
76 'damping': 0.9,
77 }
78
79 return ProtagonistPhysicser(space, {
80 self.HUMAN_FORM: human,
81 self.WOLF_FORM: wolf,
82 })
83
84 def _get_image(self, name, *transforms):
85 return resources.get_image('creatures', name, transforms=transforms)
86
87 def _make_renderer(self):
88 return ProtagonistFormSelectionRenderer({
89 self.HUMAN_FORM: render.FacingSelectionRenderer(
90 {
91 'left': render.MovementAnimatedRenderer(
92 [self._get_image('human_1.png'),
93 self._get_image('human_2.png')], 3),
94 'right': render.MovementAnimatedRenderer(
95 [self._get_image('human_1.png', FLIP_H),
96 self._get_image('human_2.png', FLIP_H)], 3),
97 }),
98 self.HUMAN_FORM_BACK: render.FacingSelectionRenderer(
99 {
100 'left': render.MovementAnimatedRenderer(
101 [self._get_image('human_back_1.png'),
102 self._get_image('human_back_2.png')], 3),
103 'right': render.MovementAnimatedRenderer(
104 [self._get_image('human_back_1.png', FLIP_H),
105 self._get_image('human_back_2.png', FLIP_H)], 3),
106 }),
107 self.WOLF_FORM: render.FacingSelectionRenderer(
108 {
109 'left': render.MovementAnimatedRenderer(
110 [self._get_image('werewolf_1.png'),
111 self._get_image('werewolf_2.png')], 3),
112 'right': render.MovementAnimatedRenderer(
113 [self._get_image('werewolf_1.png', FLIP_H),
114 self._get_image('werewolf_2.png', FLIP_H)], 3),
115 }),
116 self.WOLF_FORM_BACK: render.FacingSelectionRenderer(
117 {
118 'left': render.MovementAnimatedRenderer(
119 [self._get_image('werewolf_back_1.png'),
120 self._get_image('werewolf_back_2.png')], 3),
121 'right': render.MovementAnimatedRenderer(
122 [self._get_image('werewolf_back_1.png', FLIP_H),
123 self._get_image('werewolf_back_2.png', FLIP_H)], 3),
124 }),
125 })
126
127 @classmethod
128 def from_saved_state(cls, saved_state):
129 """Create an instance from the provided serialised state.
130 """
131 obj = cls()
132 # TODO: Update from saved state.
133 return obj
134
135 def get_render_angle(self):
136 return self.angle
137
138 def go_werewolf(self):
139 self.physicser.switch_form(self.form, self.WOLF_FORM)
140 self.form = self.WOLF_FORM
141 self.impulse_factor = 4000
142
143 if self.render_form == self.HUMAN_FORM:
144 self.render_form = self.WOLF_FORM
145 elif self.render_form == self.HUMAN_FORM_BACK:
146 self.render_form = self.WOLF_FORM_BACK
147 else:
148 self.render_form = self.WOLF_FORM
149
150 def go_human(self):
151 self.physicser.switch_form(self.form, self.HUMAN_FORM)
152 self.form = self.HUMAN_FORM
153 self.impulse_factor = 500
154
155 if self.render_form == self.WOLF_FORM:
156 self.render_form = self.HUMAN_FORM
157 elif self.render_form == self.WOLF_FORM_BACK:
158 self.render_form = self.HUMAN_FORM_BACK
159 else:
160 self.render_form = self.HUMAN_FORM
161
162 def _switch_to_back(self):
163 if self.render_form == self.HUMAN_FORM:
164 self.render_form = self.HUMAN_FORM_BACK
165 elif self.render_form == self.WOLF_FORM:
166 self.render_form = self.WOLF_FORM_BACK
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
174 def set_direction(self, dx, dy):
175 if (dx, dy) == (0, 0):
176 self.is_moving = False
177 return
178 self.is_moving = True
179 old_angle = self.angle
180 self.angle = pymunk.Vec2d((dx, dy)).angle
181 # If we've gone from quadrants 2 & 3 to 1 & 4 (or vice versa)
182 # switch between front & back views
183 if self.angle != math.pi:
184 # == math.pi is going straight left, which can't
185 # trigger a front/back swap and simplifies these checks
186 if self.angle > 0 and old_angle != self.angle:
187 self._switch_to_back()
188 elif self.angle < 0 and old_angle != self.angle:
189 self._switch_to_front()
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.render_form = old_protagonist.render_form
203 self.inventory = old_protagonist.inventory
204
205 def toggle_form(self):
206 if self.form == self.WOLF_FORM:
207 self.go_human()
208 else:
209 self.go_werewolf()
210
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
230
231 def environmental_movement(self, dx, dy):
232 if (dx, dy) == (0, 0):
233 return
234 self.physicser.apply_impulse((dx, dy))
Note: See TracBrowser for help on using the repository browser.