all repos — Legends-RPG @ c2350b7bb8b1b66528b9e82367b99775c399da81

A fantasy mini-RPG built with Python and Pygame.

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                      'battle resting': self.battle_resting}
 81
 82        return state_dict
 83
 84    def create_vector_dict(self):
 85        """Return a dictionary of x and y velocities set to
 86        direction keys."""
 87        vector_dict = {'up': (0, -1),
 88                       'down': (0, 1),
 89                       'left': (-1, 0),
 90                       'right': (1, 0)}
 91
 92        return vector_dict
 93
 94    def update(self, current_time, *args):
 95        """Implemented by inheriting classes"""
 96        self.blockers = self.set_blockers()
 97        self.current_time = current_time
 98        self.image_list = self.animation_dict[self.direction]
 99        state_function = self.state_dict[self.state]
100        state_function()
101        self.location = self.get_tile_location()
102
103
104
105    def set_blockers(self):
106        """Sets blockers to prevent collision with other sprites"""
107        blockers = []
108
109        if self.state == 'resting' or self.state == 'autoresting':
110            blockers.append(pg.Rect(self.rect.x, self.rect.y, 32, 32))
111
112        elif self.state == 'moving' or self.state == 'automoving':
113            if self.rect.x % 32 == 0:
114                tile_float = self.rect.y / float(32)
115                tile1 = (self.rect.x, math.ceil(tile_float)*32)
116                tile2 = (self.rect.x, math.floor(tile_float)*32)
117                tile_rect1 = pg.Rect(tile1[0], tile1[1], 32, 32)
118                tile_rect2 = pg.Rect(tile2[0], tile2[1], 32, 32)
119                blockers.extend([tile_rect1, tile_rect2])
120
121            elif self.rect.y % 32 == 0:
122                tile_float = self.rect.x / float(32)
123                tile1 = (math.ceil(tile_float)*32, self.rect.y)
124                tile2 = (math.floor(tile_float)*32, self.rect.y)
125                tile_rect1 = pg.Rect(tile1[0], tile1[1], 32, 32)
126                tile_rect2 = pg.Rect(tile2[0], tile2[1], 32, 32)
127                blockers.extend([tile_rect1, tile_rect2])
128
129        return blockers
130
131    def get_tile_location(self):
132        """
133        Convert pygame coordinates into tile coordinates.
134        """
135        if self.rect.x == 0:
136            tile_x = 0
137        elif self.rect.x % 32 == 0:
138            tile_x = (self.rect.x / 32)
139        else:
140            tile_x = 0
141
142        if self.rect.y == 0:
143            tile_y = 0
144        elif self.rect.y % 32 == 0:
145            tile_y = (self.rect.y / 32)
146        else:
147            tile_y = 0
148
149        return [tile_x, tile_y]
150
151
152    def make_wander_box(self):
153        """
154        Make a list of rects that surround the initial location
155        of a sprite to limit his/her wandering.
156        """
157        x = int(self.location[0])
158        y = int(self.location[1])
159        box_list = []
160        box_rects = []
161
162        for i in range(x-3, x+4):
163            box_list.append([i, y-3])
164            box_list.append([i, y+3])
165
166        for i in range(y-2, y+3):
167            box_list.append([x-3, i])
168            box_list.append([x+3, i])
169
170        for box in box_list:
171            left = box[0]*32
172            top = box[1]*32
173            box_rects.append(pg.Rect(left, top, 32, 32))
174
175        return box_rects
176
177
178    def resting(self):
179        """
180        When the Person is not moving between tiles.
181        Checks if the player is centered on a tile.
182        """
183        self.image = self.image_list[self.index]
184
185        assert(self.rect.y % 32 == 0), ('Player not centered on tile: '
186                                        + str(self.rect.y) + " : " + str(self.name))
187        assert(self.rect.x % 32 == 0), ('Player not centered on tile'
188                                        + str(self.rect.x))
189
190    def moving(self):
191        """
192        Increment index and set self.image for animation.
193        """
194        self.animation()
195        assert(self.rect.x % 32 == 0 or self.rect.y % 32 == 0), \
196            'Not centered on tile'
197
198    def animated_resting(self):
199        self.animation(500)
200
201    def animation(self, freq=100):
202        """
203        Adjust sprite image frame based on timer.
204        """
205        if (self.current_time - self.timer) > freq:
206            if self.index < (len(self.image_list) - 1):
207                self.index += 1
208            else:
209                self.index = 0
210            self.timer = self.current_time
211
212        self.image = self.image_list[self.index]
213
214    def begin_moving(self, direction):
215        """
216        Transition the player into the 'moving' state.
217        """
218        self.direction = direction
219        self.image_list = self.animation_dict[direction]
220        self.timer = self.current_time
221        self.move_timer = self.current_time
222        self.state = 'moving'
223
224        if self.rect.x % 32 == 0:
225            self.y_vel = self.vector_dict[self.direction][1]
226        if self.rect.y % 32 == 0:
227            self.x_vel = self.vector_dict[self.direction][0]
228
229
230    def begin_resting(self):
231        """
232        Transition the player into the 'resting' state.
233        """
234        self.state = 'resting'
235        self.index = 1
236        self.x_vel = self.y_vel = 0
237
238    def begin_auto_moving(self, direction):
239        """
240        Transition sprite to a automatic moving state.
241        """
242        self.direction = direction
243        self.image_list = self.animation_dict[direction]
244        self.state = 'automoving'
245        self.x_vel = self.vector_dict[direction][0]
246        self.y_vel = self.vector_dict[direction][1]
247        self.move_timer = self.current_time
248
249    def begin_auto_resting(self):
250        """
251        Transition sprite to an automatic resting state.
252        """
253        self.state = 'autoresting'
254        self.index = 1
255        self.x_vel = self.y_vel = 0
256        self.move_timer = self.current_time
257
258
259    def auto_resting(self):
260        """
261        Determine when to move a sprite from resting to moving in a random
262        direction.
263        """
264        #self.image = self.image_list[self.index]
265        self.image_list = self.animation_dict[self.direction]
266        self.image = self.image_list[self.index]
267
268        assert(self.rect.y % 32 == 0), ('Player not centered on tile: '
269                                        + str(self.rect.y))
270        assert(self.rect.x % 32 == 0), ('Player not centered on tile'
271                                        + str(self.rect.x))
272
273        if (self.current_time - self.move_timer) > 2000:
274            direction_list = ['up', 'down', 'left', 'right']
275            random.shuffle(direction_list)
276            direction = direction_list[0]
277            self.begin_auto_moving(direction)
278            self.move_timer = self.current_time
279
280    def battle_resting(self):
281        """
282        Player stays still during battle state unless he attacks.
283        """
284        pass
285
286    def auto_moving(self):
287        """
288        Animate sprite and check to stop.
289        """
290        self.animation()
291
292        assert(self.rect.x % 32 == 0 or self.rect.y % 32 == 0), \
293            'Not centered on tile'
294
295
296class Player(Person):
297    """
298    User controlled character.
299    """
300
301    def __init__(self, direction, x=0, y=0, state='resting'):
302        super(Player, self).__init__('player', x, y, direction, state)
303
304    def create_vector_dict(self):
305        """Return a dictionary of x and y velocities set to
306        direction keys."""
307        vector_dict = {'up': (0, -2),
308                       'down': (0, 2),
309                       'left': (-2, 0),
310                       'right': (2, 0)}
311
312        return vector_dict
313
314    def update(self, keys, current_time):
315        """Updates player behavior"""
316        self.blockers = self.set_blockers()
317        self.keys = keys
318        self.current_time = current_time
319        self.check_for_input()
320        state_function = self.state_dict[self.state]
321        state_function()
322        self.location = self.get_tile_location()
323
324    def check_for_input(self):
325        """Checks for player input"""
326        if self.state == 'resting':
327            if self.keys[pg.K_UP]:
328                self.begin_moving('up')
329            elif self.keys[pg.K_DOWN]:
330                self.begin_moving('down')
331            elif self.keys[pg.K_LEFT]:
332                self.begin_moving('left')
333            elif self.keys[pg.K_RIGHT]:
334                self.begin_moving('right')
335
336
337
338
339class Well(pg.sprite.Sprite):
340    """Talking well"""
341    def __init__(self, x, y):
342        super(Well, self).__init__()
343        self.image = pg.Surface((32, 32))
344        self.image.set_colorkey((0,0,0))
345        self.rect = self.image.get_rect(left=x, top=y)
346        self.location = self.get_location()
347        self.dialogue = ["I'm a well!"]
348        self.blockers = [self.rect]
349        self.x_vel = self.y_vel = 0
350        self.state = 'resting'
351        self.direction = 'down'
352        self.default_direction = self.direction
353        self.item = None
354        self.wander_box = []
355
356    def get_location(self):
357        """Get tile location"""
358        x = self.rect.x / 32
359        y = self.rect.y / 32
360
361        return [x, y]
362
363    def begin_auto_resting(self):
364        """Placeholder"""
365        pass
366
367