diff options
author | Gonzalo Odiard <godiard@gmail.com> | 2014-08-02 04:15:18 (GMT) |
---|---|---|
committer | Gonzalo Odiard <godiard@gmail.com> | 2014-08-02 04:15:18 (GMT) |
commit | 4edd526a488399b32b0c968e270ff0124a3e25c2 (patch) | |
tree | ae3867258bea5663bbcf2f36d3b2be6f6aa7d4ac | |
parent | 550125bf2455ea43f28adc8101a339dc4f2e399a (diff) |
-rwxr-xr-x | game.py | 448 |
1 files changed, 0 insertions, 448 deletions
diff --git a/game.py b/game.py deleted file mode 100755 index 3c9bbd0..0000000 --- a/game.py +++ /dev/null @@ -1,448 +0,0 @@ -#!/usr/bin/python -import os -import math -import json - -import pygame -from gi.repository import Gtk - -DATA_DIR = 'images' -STROKE_COLOR = 0, 0, 0 # black -FILL_COLOR = 255, 255, 255 # white -KEY_COLOR = 0, 255, 255 # cyan -BACKGROUND_COLOR = 10, 10, 10 # dark gray -FRAMES_PER_SECOND = 12 -SCREEN_SIZE = 320, 240 -AT_2X_SCALE = False -DEBUG = False - - -class ImageManager(object): - def __init__(self): - self.images = {} - - def _load_image(self, name, set_keycolor=False): - image_filename = name + '.png' - image_path = os.path.join(DATA_DIR, image_filename) - image = pygame.image.load(image_path) - image = image.convert() - if set_keycolor: - image.set_colorkey(KEY_COLOR) - if AT_2X_SCALE: - image = pygame.transform.scale2x(image) - return image - - def get_image(self, name, colors=None, set_keycolor=False): - """Get an image by name, load it if necessary. - - If the image wasn't get previously, load it from the - filesystem. The next time it is asked, the load will not be - made. - - If colors are given, return a copy of the image, colorized - with stroke and fill colors. - - If set_keycolor is True, the KEY_COLOR color will become - transparent in the image. - - """ - image = self.images.get(name) - if image is None: - image = self._load_image(name, set_keycolor) - self.images[name] = image - - if colors is None: - return image - else: - new_image = image.copy() - stroke_color, fill_color = colors - pixel_array = pygame.PixelArray(new_image) - pixel_array.replace(FILL_COLOR, fill_color, 0.1) - pixel_array.replace(STROKE_COLOR, stroke_color, 0.1) - return new_image - - -IMAGE_MANAGER = ImageManager() - - -def _load_animation_data(name): - """Return the animation metadata parsing a text file.""" - data_filename = name + '.json' - data_path = os.path.join(DATA_DIR, data_filename) - f = open(data_path, 'rb') - reader = json.load(f) - displacement = reader['displacement'] - frames_data = [] - for frame in list(reader['frames']): - area = frame[:4] - delta = list(frame[4:]) - frames_data.append({'area': area, 'delta': delta}) - return frames_data, displacement - - -class CharacterAnimation(object): - def __init__(self, name, colors=None, set_keycolor=True): - self._frames_image = IMAGE_MANAGER.get_image(name, - colors, set_keycolor) - self._frames_image_mirror = pygame.transform.flip(self._frames_image, - True, False) - self._frames_data, self._displacement = _load_animation_data(name) - if AT_2X_SCALE: - self._resize_frames_data() - self._convert_frames_data() - self._mirror_frames_data() - self._frames_len = len(self._frames_data) - self._cur_frame = None - self._is_playing = None - self._direction = None - self._mirror = None - - def _resize_frames_data(self): - """ Multiply the area data by two. - - This needs to be done if displaying images at 2x. - - """ - for data in self._frames_data: - area = data['area'] - resized_area = tuple((elem * 2 for elem in area)) - data['area'] = resized_area - - def _convert_frames_data(self): - """Convert the area data to pygame rects.""" - for data in self._frames_data: - area = data['area'] - converted_area = pygame.Rect(*area) - data['area'] = converted_area - - def _mirror_frames_data(self): - self._frames_data_mirror = [] - for data in self._frames_data: - mirror_data = {} - mirror_data['area'] = data['area'].copy() - mirror_data['area'].left = self._frames_image.get_width() \ - - data['area'].left - data['area'].width - mirror_data['delta'] = list(data['delta']) - mirror_data['delta'][0] = -data['delta'][0] - data['area'].width - self._frames_data_mirror.append(mirror_data) - - def play(self, origin=(0, 0), direction='forward', mirror=False): - self._origin = origin - if direction == 'forward': - self._cur_frame = 0 - elif direction == 'backward': - self._cur_frame = self._frames_len - 1 - else: - raise NotImplementedError - self._direction = direction - self._mirror = mirror - self._is_playing = True - - def stop(self): - self._is_playing = False - - def get_dist_to_rect(self): - if self._mirror: - data = self._frames_data_mirror[self._cur_frame] - else: - data = self._frames_data[self._cur_frame] - dx = data['delta'][0] + (data['area'].width / 2.0) - return dx, 0 - - def _at_last_frame(self): - return self._cur_frame == self._frames_len - 1 - - def _at_first_frame(self): - return self._cur_frame == 0 - - def _next_frame(self): - if ((self._direction == 'forward' and self._at_last_frame()) or - (self._direction == 'backward' and self._at_first_frame())): - self._is_playing = False - return 'finished' - if self._direction == 'forward': - self._cur_frame += 1 - elif self._direction == 'backward': - self._cur_frame -= 1 - - def update(self): - if self._is_playing: - return self._next_frame() - - def draw(self, surface): - if not self._mirror: - cur_frame_data = self._frames_data[self._cur_frame] - frames_image = self._frames_image - else: - cur_frame_data = self._frames_data_mirror[self._cur_frame] - frames_image = self._frames_image_mirror - area = cur_frame_data['area'] - delta = cur_frame_data['delta'] - dx = self._origin[0] + delta[0] - dy = self._origin[1] + delta[1] - return surface.blit(frames_image, (dx, dy), area) - - def get_displacement(self): - """Return the absolute displacement. - - Takes into account the direction and if the animation is - mirrored. - - """ - result = None - if self._direction == 'forward': - result = self._displacement - elif self._direction == 'backward': - result = list((elem * -1 for elem in self._displacement)) - - if self._mirror: - result = -1 * result[0], result[1] - - return result - - -class Stage(object): - """A stage contains a wall and a floor.""" - def __init__(self, horizon): - """Stage constructor. - - Horizon is a number between 0 and 1 that is used to draw the - wall and the floor above and below a line. - - """ - self._wall = IMAGE_MANAGER.get_image('wall_tile') - self._floor = IMAGE_MANAGER.get_image('floor_tile') - self._horizon = horizon - - def draw(self, surface): - """Draw the wall and the floor. - - The wall and the floor are drawn using tiles. - - """ - surface_width = surface.get_width() - surface_height = surface.get_height() - tile_width = self._wall.get_width() - tile_height = self._wall.get_height() - wall_height = surface_height * (1.0 - self._horizon) - floor_height = surface_height * self._horizon - - def get_tiles_to_fill(distance, tile_length): - """Return the number of tiles needed to fill the distance""" - return int(math.ceil(float(distance) / tile_length)) - - tiles_per_width = get_tiles_to_fill(surface_width, tile_width) - wall_tiles_per_height = get_tiles_to_fill(wall_height, tile_width) - floor_tiles_per_height = get_tiles_to_fill(floor_height, tile_width) - - # draw wall above horizon - for tile_x in range(tiles_per_width): - dx = tile_width * tile_x - for tile_y in range(wall_tiles_per_height): - dy = wall_height - (tile_height * (tile_y + 1)) - surface.blit(self._wall, (dx, dy)) - - # draw floor below horizon - for tile_x in range(tiles_per_width): - dx = tile_width * tile_x - for tile_y in range(floor_tiles_per_height): - dy = wall_height + (tile_height * tile_y) - surface.blit(self._floor, (dx, dy)) - - -class Character(object): - def __init__(self, pos_x, pos_y, direction=None, orientation=None): - self._pos_x = pos_x - self._pos_y = pos_y - self._animations = {} - self._cur_animation = None - self._on_loop = True - if orientation is None: - orientation = 'right' - self.orientation = orientation - if direction is None: - direction = 'forward' - self.direction = direction - - def add_action(self, animation_name, animation): - self._animations[animation_name] = animation - - def act(self, animation, clean=True): - """ - Play the given animation. - - The animation parameter can be an animation name, previously - passed to the add_animation method, or the animation itself. - - """ - if self._cur_animation is not None and clean: - - self._cur_animation.stop() - dx, dy = self._cur_animation.get_dist_to_rect() - self._pos_x += dx - self._pos_y += dy - if isinstance(animation, str): - self._cur_animation = self._animations[animation] - else: - self._cur_animation = animation - origin = (self._pos_x, self._pos_y) - mirror = self.orientation == 'left' - self._cur_animation.play(origin, self.direction, mirror) - - def draw(self, surface): - return self._cur_animation.draw(surface) - - def update(self): - result = self._cur_animation.update() - if result == 'finished': - displacement = self._cur_animation.get_displacement() - self._pos_x += displacement[0] - self._pos_y += displacement[1] - if self._on_loop: - self.act(self._cur_animation, clean=False) - - def flip_direction(self): - if self.direction == 'backward': - self.direction = 'forward' - elif self.direction == 'forward': - self.direction = 'backward' - - -class Game(object): - """Ingenium Machina game""" - def __init__(self, colors=None): - - self._running = True - self._clock = pygame.time.Clock() - self._background = None - self._char = None - self._click_x = None - self._colors = colors - if self._colors is not None: - self._convert_colors() - - def _convert_colors(self): - """Convert the given colors to pygame colors.""" - self._colors = list((pygame.Color(color) for color in self._colors)) - - def setup(self): - """Setup the game elements.""" - screen = pygame.display.get_surface() - - # graphics that don't move get blitted in this surface - self._background = screen.copy() - - horizon = 1.0 / 5 - self._stage = Stage(horizon) - - char_pos_x = screen.get_width() / 2 - char_pos_y = screen.get_height() * (1 - horizon) - self._char = Character(char_pos_x, char_pos_y) - stand_ani = CharacterAnimation('stand', self._colors) - self._char.add_action('stand', stand_ani) - walk_ani = CharacterAnimation('walk', self._colors) - self._char.add_action('walk', walk_ani) - self._char.act('stand') - - def run(self): - """Game loop.""" - self.setup() - - screen = pygame.display.get_surface() - - screen.fill(BACKGROUND_COLOR) - self._stage.draw(self._background) - screen.blit(self._background, (0, 0)) - - pygame.display.flip() - - # a list of the affected screen areas, for updating only those - self._dirty_rects = [] - - while self._running: - a, b, c, d = pygame.cursors.load_xbm('my_cursor.xbm','my_cursor_mask.xbm') - pygame.mouse.set_cursor(a, b, c, d) - - # try to stay at the given frames per second - self._clock.tick(FRAMES_PER_SECOND) - - # pump gtk messages - while Gtk.events_pending(): - Gtk.main_iteration() - - # pump pygame messages - for event in pygame.event.get(): - if event.type == pygame.QUIT: - self._running = False - elif event.type == pygame.VIDEORESIZE: - pygame.display.set_mode(event.size, pygame.RESIZABLE) - elif event.type == pygame.KEYDOWN: - if event.key == pygame.K_ESCAPE: - self._running = False - elif event.key == pygame.K_LEFT: - self._click_x = None - self._char.orientation = 'left' - self._char.act('walk') - elif event.key == pygame.K_RIGHT: - self._click_x = None - self._char.orientation = 'right' - self._char.act('walk') - elif event.type == pygame.KEYUP: - self._click_x = None - self._char.act('stand') - elif event.type == pygame.MOUSEBUTTONUP: - mouse_pos = pygame.mouse.get_pos() - self._click_x = mouse_pos[0] - if mouse_pos[0] < self._char._pos_x: - self._char.orientation = 'left' - else: - self._char.orientation = 'right' - self._char.act('walk') - - # clean the background, filling the affected areas of the - # screen with the background - self._old_dirty_rects = [] - for rect in self._dirty_rects: - old_rect = screen.blit(self._background, rect, rect) - self._old_dirty_rects.append(old_rect) - - self._dirty_rects = [] - - # move graphics - if self._click_x is not None: - stop_animation = False - if self._char.orientation == 'right': - if self._char._pos_x > self._click_x: - stop_animation = True - elif self._char.orientation == 'left': - if self._char._pos_x < self._click_x: - stop_animation = True - if stop_animation: - self._char.act('stand') - self._click_x = None - self._char.update() - - # draw char - rect = self._char.draw(screen) - self._dirty_rects.append(rect) - - # update the display - pygame.display.update(self._dirty_rects + self._old_dirty_rects) - - -def main(): - """Setup pygame and run game. - - This function is called when the game is run directly from the - command line as: ./game.py - - """ - pygame.init() - pygame.display.set_mode((0, 0), pygame.RESIZABLE) - - colors = ['#101010', '#ffffff'] - game = Game(colors) - game.run() - -if __name__ == '__main__': - main() |