data/components/person.py (view raw)
1import math, random
2import pygame as pg
3from .. import setup
4from .. import constants as c
5
6
7class Person(pg.sprite.Sprite):
8 """Base class for all world characters
9 controlled by the computer"""
10
11 def __init__(self, sheet_key, x, y, direction='down', state='resting', index=0):
12 super(Person, self).__init__()
13 self.name = sheet_key
14 self.get_image = setup.tools.get_image
15 self.spritesheet_dict = self.create_spritesheet_dict(sheet_key)
16 self.animation_dict = self.create_animation_dict()
17 self.index = index
18 self.direction = direction
19 self.image_list = self.animation_dict[self.direction]
20 self.image = self.image_list[self.index]
21 self.rect = self.image.get_rect(left=x, top=y)
22 self.origin_pos = self.rect.topleft
23 self.state_dict = self.create_state_dict()
24 self.vector_dict = self.create_vector_dict()
25 self.x_vel = 0
26 self.y_vel = 0
27 self.timer = 0.0
28 self.move_timer = 0.0
29 self.current_time = 0.0
30 self.state = state
31 self.blockers = self.set_blockers()
32 self.location = self.get_tile_location()
33 self.dialogue = ['Location: ' + str(self.location)]
34 self.default_direction = direction
35 self.item = None
36 self.wander_box = self.make_wander_box()
37
38 def create_spritesheet_dict(self, sheet_key):
39 """Implemented by inheriting classes"""
40 image_list = []
41 image_dict = {}
42 sheet = setup.GFX[sheet_key]
43
44 image_keys = ['facing up 1', 'facing up 2',
45 'facing down 1', 'facing down 2',
46 'facing left 1', 'facing left 2',
47 'facing right 1', 'facing right 2']
48
49 for row in range(2):
50 for column in range(4):
51 image_list.append(
52 self.get_image(column*32, row*32, 32, 32, sheet))
53
54 for key, image in zip(image_keys, image_list):
55 image_dict[key] = image
56
57 return image_dict
58
59 def create_animation_dict(self):
60 """Return a dictionary of image lists for animation"""
61 image_dict = self.spritesheet_dict
62
63 left_list = [image_dict['facing left 1'], image_dict['facing left 2']]
64 right_list = [image_dict['facing right 1'], image_dict['facing right 2']]
65 up_list = [image_dict['facing up 1'], image_dict['facing up 2']]
66 down_list = [image_dict['facing down 1'], image_dict['facing down 2']]
67
68 direction_dict = {'left': left_list,
69 'right': right_list,
70 'up': up_list,
71 'down': down_list}
72
73 return direction_dict
74
75 def create_state_dict(self):
76 """Return a dictionary of all state methods"""
77 state_dict = {'resting': self.resting,
78 'moving': self.moving,
79 'animated resting': self.animated_resting,
80 'autoresting': self.auto_resting,
81 'automoving': self.auto_moving,
82 'battle resting': self.battle_resting,
83 'attack': self.attack}
84
85 return state_dict
86
87 def create_vector_dict(self):
88 """Return a dictionary of x and y velocities set to
89 direction keys."""
90 vector_dict = {'up': (0, -1),
91 'down': (0, 1),
92 'left': (-1, 0),
93 'right': (1, 0)}
94
95 return vector_dict
96
97 def update(self, current_time, *args):
98 """Implemented by inheriting classes"""
99 self.blockers = self.set_blockers()
100 self.current_time = current_time
101 self.image_list = self.animation_dict[self.direction]
102 state_function = self.state_dict[self.state]
103 state_function()
104 self.location = self.get_tile_location()
105
106
107
108 def set_blockers(self):
109 """Sets blockers to prevent collision with other sprites"""
110 blockers = []
111
112 if self.state == 'resting' or self.state == 'autoresting':
113 blockers.append(pg.Rect(self.rect.x, self.rect.y, 32, 32))
114
115 elif self.state == 'moving' or self.state == 'automoving':
116 if self.rect.x % 32 == 0:
117 tile_float = self.rect.y / float(32)
118 tile1 = (self.rect.x, math.ceil(tile_float)*32)
119 tile2 = (self.rect.x, math.floor(tile_float)*32)
120 tile_rect1 = pg.Rect(tile1[0], tile1[1], 32, 32)
121 tile_rect2 = pg.Rect(tile2[0], tile2[1], 32, 32)
122 blockers.extend([tile_rect1, tile_rect2])
123
124 elif self.rect.y % 32 == 0:
125 tile_float = self.rect.x / float(32)
126 tile1 = (math.ceil(tile_float)*32, self.rect.y)
127 tile2 = (math.floor(tile_float)*32, self.rect.y)
128 tile_rect1 = pg.Rect(tile1[0], tile1[1], 32, 32)
129 tile_rect2 = pg.Rect(tile2[0], tile2[1], 32, 32)
130 blockers.extend([tile_rect1, tile_rect2])
131
132 return blockers
133
134 def get_tile_location(self):
135 """
136 Convert pygame coordinates into tile coordinates.
137 """
138 if self.rect.x == 0:
139 tile_x = 0
140 elif self.rect.x % 32 == 0:
141 tile_x = (self.rect.x / 32)
142 else:
143 tile_x = 0
144
145 if self.rect.y == 0:
146 tile_y = 0
147 elif self.rect.y % 32 == 0:
148 tile_y = (self.rect.y / 32)
149 else:
150 tile_y = 0
151
152 return [tile_x, tile_y]
153
154
155 def make_wander_box(self):
156 """
157 Make a list of rects that surround the initial location
158 of a sprite to limit his/her wandering.
159 """
160 x = int(self.location[0])
161 y = int(self.location[1])
162 box_list = []
163 box_rects = []
164
165 for i in range(x-3, x+4):
166 box_list.append([i, y-3])
167 box_list.append([i, y+3])
168
169 for i in range(y-2, y+3):
170 box_list.append([x-3, i])
171 box_list.append([x+3, i])
172
173 for box in box_list:
174 left = box[0]*32
175 top = box[1]*32
176 box_rects.append(pg.Rect(left, top, 32, 32))
177
178 return box_rects
179
180
181 def resting(self):
182 """
183 When the Person is not moving between tiles.
184 Checks if the player is centered on a tile.
185 """
186 self.image = self.image_list[self.index]
187
188 assert(self.rect.y % 32 == 0), ('Player not centered on tile: '
189 + str(self.rect.y) + " : " + str(self.name))
190 assert(self.rect.x % 32 == 0), ('Player not centered on tile'
191 + str(self.rect.x))
192
193 def moving(self):
194 """
195 Increment index and set self.image for animation.
196 """
197 self.animation()
198 assert(self.rect.x % 32 == 0 or self.rect.y % 32 == 0), \
199 'Not centered on tile'
200
201 def animated_resting(self):
202 self.animation(500)
203
204 def animation(self, freq=100):
205 """
206 Adjust sprite image frame based on timer.
207 """
208 if (self.current_time - self.timer) > freq:
209 if self.index < (len(self.image_list) - 1):
210 self.index += 1
211 else:
212 self.index = 0
213 self.timer = self.current_time
214
215 self.image = self.image_list[self.index]
216
217 def begin_moving(self, direction):
218 """
219 Transition the player into the 'moving' state.
220 """
221 self.direction = direction
222 self.image_list = self.animation_dict[direction]
223 self.timer = self.current_time
224 self.move_timer = self.current_time
225 self.state = 'moving'
226
227 if self.rect.x % 32 == 0:
228 self.y_vel = self.vector_dict[self.direction][1]
229 if self.rect.y % 32 == 0:
230 self.x_vel = self.vector_dict[self.direction][0]
231
232
233 def begin_resting(self):
234 """
235 Transition the player into the 'resting' state.
236 """
237 self.state = 'resting'
238 self.index = 1
239 self.x_vel = self.y_vel = 0
240
241 def begin_auto_moving(self, direction):
242 """
243 Transition sprite to a automatic moving state.
244 """
245 self.direction = direction
246 self.image_list = self.animation_dict[direction]
247 self.state = 'automoving'
248 self.x_vel = self.vector_dict[direction][0]
249 self.y_vel = self.vector_dict[direction][1]
250 self.move_timer = self.current_time
251
252 def begin_auto_resting(self):
253 """
254 Transition sprite to an automatic resting state.
255 """
256 self.state = 'autoresting'
257 self.index = 1
258 self.x_vel = self.y_vel = 0
259 self.move_timer = self.current_time
260
261
262 def auto_resting(self):
263 """
264 Determine when to move a sprite from resting to moving in a random
265 direction.
266 """
267 #self.image = self.image_list[self.index]
268 self.image_list = self.animation_dict[self.direction]
269 self.image = self.image_list[self.index]
270
271 assert(self.rect.y % 32 == 0), ('Player not centered on tile: '
272 + str(self.rect.y))
273 assert(self.rect.x % 32 == 0), ('Player not centered on tile'
274 + str(self.rect.x))
275
276 if (self.current_time - self.move_timer) > 2000:
277 direction_list = ['up', 'down', 'left', 'right']
278 random.shuffle(direction_list)
279 direction = direction_list[0]
280 self.begin_auto_moving(direction)
281 self.move_timer = self.current_time
282
283 def battle_resting(self):
284 """
285 Player stays still during battle state unless he attacks.
286 """
287 pass
288
289 def enter_attack_state(self, enemy):
290 """
291 Set values for attack state.
292 """
293 self.attacked_enemy = enemy
294 self.x_vel = 1
295 self.state = 'attack'
296
297 def attack(self):
298 """
299 Player does an attack animation.
300 """
301 SLOW_BACK = 1
302 FAST_FORWARD = -5
303 FAST_BACK = 5
304
305 self.rect.x += self.x_vel
306
307 if self.x_vel == SLOW_BACK:
308 if self.rect.x >= self.origin_pos[0] + 20:
309 self.x_vel = FAST_FORWARD
310 elif self.x_vel == FAST_FORWARD:
311 if self.rect.topleft >= self.origin_pos:
312 self.image = self.spritesheet_dict['facing left 1']
313 self.image = pg.transform.scale2x(self.image)
314 elif self.rect.x <= self.origin_pos[0] - 110:
315 self.x_vel = FAST_BACK
316 self.observer.on_notify('attack animation')
317 else:
318 if self.rect.x >= self.origin_pos[0]:
319 self.rect.x = self.origin_pos[0]
320 self.x_vel = 0
321 self.state = 'battle resting'
322 self.image = self.spritesheet_dict['facing left 2']
323 self.image = pg.transform.scale2x(self.image)
324 self.observer.on_notify(c.PLAYER_FINISHED_ATTACK)
325
326
327
328 def auto_moving(self):
329 """
330 Animate sprite and check to stop.
331 """
332 self.animation()
333
334 assert(self.rect.x % 32 == 0 or self.rect.y % 32 == 0), \
335 'Not centered on tile'
336
337
338class Player(Person):
339 """
340 User controlled character.
341 """
342
343 def __init__(self, direction, x=0, y=0, state='resting', index=0):
344 super(Player, self).__init__('player', x, y, direction, state, index)
345
346 def create_vector_dict(self):
347 """Return a dictionary of x and y velocities set to
348 direction keys."""
349 vector_dict = {'up': (0, -2),
350 'down': (0, 2),
351 'left': (-2, 0),
352 'right': (2, 0)}
353
354 return vector_dict
355
356 def update(self, keys, current_time):
357 """Updates player behavior"""
358 self.blockers = self.set_blockers()
359 self.keys = keys
360 self.current_time = current_time
361 self.check_for_input()
362 state_function = self.state_dict[self.state]
363 state_function()
364 self.location = self.get_tile_location()
365
366 def check_for_input(self):
367 """Checks for player input"""
368 if self.state == 'resting':
369 if self.keys[pg.K_UP]:
370 self.begin_moving('up')
371 elif self.keys[pg.K_DOWN]:
372 self.begin_moving('down')
373 elif self.keys[pg.K_LEFT]:
374 self.begin_moving('left')
375 elif self.keys[pg.K_RIGHT]:
376 self.begin_moving('right')
377
378
379
380
381class Well(pg.sprite.Sprite):
382 """Talking well"""
383 def __init__(self, x, y):
384 super(Well, self).__init__()
385 self.image = pg.Surface((32, 32))
386 self.image.set_colorkey((0,0,0))
387 self.rect = self.image.get_rect(left=x, top=y)
388 self.location = self.get_location()
389 self.dialogue = ["I'm a well!"]
390 self.blockers = [self.rect]
391 self.x_vel = self.y_vel = 0
392 self.state = 'resting'
393 self.direction = 'down'
394 self.default_direction = self.direction
395 self.item = None
396 self.wander_box = []
397
398 def get_location(self):
399 """Get tile location"""
400 x = self.rect.x / 32
401 y = self.rect.y / 32
402
403 return [x, y]
404
405 def begin_auto_resting(self):
406 """Placeholder"""
407 pass
408
409