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