diff --git a/conf.py b/conf.py index baa1c73..35bcd5b 100644 --- a/conf.py +++ b/conf.py @@ -5,60 +5,78 @@ Licence: `GNU GPL v3` GNU GPL v3: http://www.gnu.org/licenses/ This file is part of [_ocp3_ project](https://github.com/freezed/ocp3) """ +from math import floor import pygame -import math -# CONFIGURATION -ELEMENT_LIST = [ - {'symbol': 'n', 'name': 'needle', 'cross': True, 'ressurect': False, 'collect': True, 'tile': 'img/3-blue-transp-30.png'}, - {'symbol': 't', 'name': 'tube', 'cross': True, 'ressurect': False, 'collect': True, 'tile': 'img/1-blue-transp-30.png'}, - {'symbol': 'e', 'name': 'ether', 'cross': True, 'ressurect': False, 'collect': True, 'tile': 'img/2-blue-transp-30.png'}, - {'symbol': 'E', 'name': 'exit', 'cross': False, 'ressurect': False, 'collect': False, 'tile': 'img/g-orange-transp-30.png'}, - {'symbol': ' ', 'name': 'void', 'cross': True, 'ressurect': True, 'collect': False, 'tile': 'img/blue-white-30.png'}, - {'symbol': '.', 'name': 'wall', 'cross': False, 'ressurect': False, 'collect': False, 'tile': 'img/transp-30.png'}, - {'symbol': 'X', 'name': 'player', 'cross': False, 'ressurect': False, 'collect': False, 'tile': 'img/player-30.png'}, - {'symbol': '\n', 'name': 'nlin', 'cross': False, 'ressurect': False, 'collect': False, 'tile': False}, -] +ELEMENTS = ( + { + 'symbol': 'n', 'name': 'needle', 'cross': True, + 'ressurect': False, 'collect': True, 'tile': 'img/3-30.png' + }, + { + 'symbol': 't', 'name': 'tube', 'cross': True, + 'ressurect': False, 'collect': True, 'tile': 'img/1-30.png' + }, + { + 'symbol': 'e', 'name': 'ether', 'cross': True, + 'ressurect': False, 'collect': True, 'tile': 'img/2-30.png' + }, + { + 'symbol': 'E', 'name': 'exit', 'cross': False, + 'ressurect': False, 'collect': False, 'tile': 'img/g-30.png' + }, + { + 'symbol': ' ', 'name': 'void', 'cross': True, + 'ressurect': True, 'collect': False, 'tile': 'img/void-30.png' + }, + { + 'symbol': '.', 'name': 'wall', 'cross': False, + 'ressurect': False, 'collect': False, 'tile': 'img/wall-30.png' + }, + { + 'symbol': 'X', 'name': 'player', 'cross': False, + 'ressurect': False, 'collect': False, 'tile': 'img/player-30.png' + }, + { + 'symbol': '\n', 'name': 'nlin', 'cross': False, + 'ressurect': False, 'collect': False, 'tile': False + }, +) -# Possible returns of a move, keep the «ok» at the end -MOVE_STATUS = ['looser', 'wall', 'winner', 'collect', 'ok'] -MOVE_STATUS_MSG = { - 'looser': "Vous vous êtes fait assommé! Pour endormir le garde, il manqait: {}.\nPerdu!", - 'wall': "Le déplacement est stoppé par un mur.", - 'winner': "Gagné! Vous avez endormis le garde avec votre seringue.", - 'collect': "Vous ramassez l'objet «{}»", - 'ok': "Jusqu'ici, tout va bien…" -} - -CELL_SIZE_PX = 30 # Size of the tiles, in pixels -MAZE_SIZE_TIL = 15 # Size of a map, in tiles -FONT_SIZE = math.floor(0.9 * CELL_SIZE_PX) +CELL_SIZE = 30 # Size of the tiles, in pixels +MAZE_SIZE = 15 # Size of a map, in tiles BLACK = (0, 0, 0) BLUE = (0, 0, 128) GREEN = (0, 255, 0) WHITE = (255, 255, 255) # Messages -CAPTION = "OCP3, a pygame maze" +CAPTION = "OCP3, a maze based on pygame" ERR_MAP = "ERR_MAP: «{}»" -HEADER_MESSAGES = { +HEAD_MESSAGES = { 'title': "Welcome in OCP3.", 'status': "Use arrow keys to play, any other key to quit.", 'items': "Items: {}/{}", } +MSG_COLLECT = "You pick «{}»" MSG_END = "End of game, press a key to quit." +MSG_LOSER = "You lose! You were missing: {}." +MSG_OK = "…" MSG_QUIT = "You decide to quit the game" +MSG_WALL = "That's a wall!" +MSG_WINNER = "Cogratulations! You asleep the guard." # Files -BACKGROUND_IMG = 'img/zebra-800.png' +BACKGRND_FILE = 'img/back-800.png' MAP_FILE = '01.map' -UNKNOWN_TILE = 'img/unknown-30.png' +UNKNOWN_FILE = 'img/unknown-30.png' # Constant calculation -HEADER_HEIGHT = (2 * CELL_SIZE_PX) -WINDOW_SIZE_PX_W = CELL_SIZE_PX * MAZE_SIZE_TIL -WINDOW_SIZE_PX_H = WINDOW_SIZE_PX_W + HEADER_HEIGHT -WIN_DIM = (WINDOW_SIZE_PX_W, WINDOW_SIZE_PX_H) +FONT_SIZE = floor(0.9 * CELL_SIZE) +HEAD_SIZE_H = (2 * CELL_SIZE) +WIN_SIZE_W = CELL_SIZE * MAZE_SIZE +WIN_SIZE_H = WIN_SIZE_W + HEAD_SIZE_H +WIN_DIM = (WIN_SIZE_W, WIN_SIZE_H) # FUNCTIONS @@ -66,7 +84,7 @@ WIN_DIM = (WINDOW_SIZE_PX_W, WINDOW_SIZE_PX_H) def elmt_val(kval, ksel, vsel, nline=False): """ - Return value(s) from ELEMENT_LIST + Return value(s) from ELEMENTS :param str kval: key of the value returned :param str ksel: key of the selection criteria @@ -74,53 +92,59 @@ def elmt_val(kval, ksel, vsel, nline=False): :param bool/int nline: Default False, return value(s) in a list, use a int() to return the `n` value from the list - :return str/bool/…: """ try: if nline is False: - return (element[kval] for element in ELEMENT_LIST if element[ksel] == vsel) + return (element[kval] for element in ELEMENTS + if element[ksel] == vsel) else: - return [element[kval] for element in ELEMENT_LIST if element[ksel] == vsel][nline] + return [element[kval] for element in ELEMENTS + if element[ksel] == vsel][nline] except IndexError: return False -def maze_draw(WINDOW, map_string): - """ Take a map string and generate a graphic maze """ +def maze_draw(surface, map_string): + """ + Take a map string and generate a graphic maze + + :param obj surface: a pygame surface object + :param str map_string: map modelized in a string + """ back_tiles = [] for cell, element in enumerate(map_string): img = elmt_val('tile', 'symbol', element, 0) if img is False: - back_tiles.append(pygame.image.load(UNKNOWN_TILE).convert()) + back_tiles.append(pygame.image.load(UNKNOWN_FILE).convert()) else: back_tiles.append(pygame.image.load(img).convert_alpha()) - x = (cell % MAZE_SIZE_TIL) * CELL_SIZE_PX - y = (cell // MAZE_SIZE_TIL) * CELL_SIZE_PX + HEADER_HEIGHT - WINDOW.blit(back_tiles[cell], (x, y)) + x = (cell % MAZE_SIZE) * CELL_SIZE + y = (cell // MAZE_SIZE) * CELL_SIZE + HEAD_SIZE_H + surface.blit(back_tiles[cell], (x, y)) # Refresh pygame.display.flip() -def set_header(WINDOW, messages): +def set_header(surface, messages): """ Set the header message on the window - :param obj WINDOWS: display object + :param obj surface: surface surfaceect :param list/str messages: list of messages per place """ - pygame.draw.rect(WINDOW, BLACK, (0, 0, WINDOW_SIZE_PX_W, HEADER_HEIGHT)) + pygame.draw.rect(surface, BLACK, (0, 0, WIN_SIZE_W, HEAD_SIZE_H)) FONT = pygame.font.Font(None, FONT_SIZE) - h_title = FONT.render(str(messages['title']), True, BLUE, WHITE) - h_status = FONT.render(str(messages['status']), True, WHITE, BLACK) - h_items = FONT.render(str(messages['items']), True, GREEN, BLACK) + h_title = FONT.render(messages['title'], True, BLUE, WHITE) + h_status = FONT.render(messages['status'], True, WHITE, BLACK) + h_items = FONT.render(messages['items'], True, GREEN, BLACK) - h_items_pos = h_items.get_rect(topright=(WINDOW_SIZE_PX_W, 0)) + h_items_pos = h_items.get_rect(topright=(WIN_SIZE_W, 0)) - WINDOW.blit(h_title, (0, 0)) - WINDOW.blit(h_status, (0, CELL_SIZE_PX)) - WINDOW.blit(h_items, h_items_pos) + surface.blit(h_title, (0, 0)) + surface.blit(h_status, (0, CELL_SIZE)) + surface.blit(h_items, h_items_pos) diff --git a/img/1-30.png b/img/1-30.png new file mode 100644 index 0000000..484e631 Binary files /dev/null and b/img/1-30.png differ diff --git a/img/2-30.png b/img/2-30.png new file mode 100644 index 0000000..8db270b Binary files /dev/null and b/img/2-30.png differ diff --git a/img/3-30.png b/img/3-30.png new file mode 100644 index 0000000..71ac93f Binary files /dev/null and b/img/3-30.png differ diff --git a/img/back-800.png b/img/back-800.png new file mode 100644 index 0000000..6e31bdd Binary files /dev/null and b/img/back-800.png differ diff --git a/img/g-30.png b/img/g-30.png new file mode 100644 index 0000000..1ea97ac Binary files /dev/null and b/img/g-30.png differ diff --git a/img/m-30.png b/img/m-30.png new file mode 100644 index 0000000..84ccf35 Binary files /dev/null and b/img/m-30.png differ diff --git a/img/void-30.png b/img/void-30.png new file mode 100644 index 0000000..62f54f1 Binary files /dev/null and b/img/void-30.png differ diff --git a/img/wall-30.png b/img/wall-30.png new file mode 100644 index 0000000..6bd4089 Binary files /dev/null and b/img/wall-30.png differ diff --git a/main.py b/main.py index 1ff241d..60c31e9 100644 --- a/main.py +++ b/main.py @@ -12,30 +12,28 @@ details """ import pygame from pygame.locals import ( - K_UP, K_DOWN, K_RIGHT, K_LEFT, KEYDOWN, QUIT, RESIZABLE + K_UP, K_DOWN, K_RIGHT, K_LEFT, KEYDOWN, QUIT ) from map import Map -from conf import BACKGROUND_IMG, CAPTION, MAP_FILE, HEADER_HEIGHT, maze_draw, MSG_END, MSG_QUIT, set_header, WIN_DIM +from conf import ( + BACKGRND_FILE, CAPTION, MAP_FILE, HEAD_SIZE_H, maze_draw, + MSG_END, MSG_QUIT, set_header, WIN_DIM +) -# Constant calculation GAME_KEYS = [K_UP, K_DOWN, K_RIGHT, K_LEFT] pygame.init() -WINDOW = pygame.display.set_mode(WIN_DIM, RESIZABLE) +WINDOW = pygame.display.set_mode(WIN_DIM) pygame.display.set_caption(CAPTION) -WINDOW.blit(pygame.image.load(BACKGROUND_IMG).convert(), (0, HEADER_HEIGHT)) +WINDOW.blit(pygame.image.load(BACKGRND_FILE).convert(), (0, HEAD_SIZE_H)) # Loading map MAP_GAME = Map(MAP_FILE) - -# Header messaging set_header(WINDOW, MAP_GAME.status_message) - -# Draw maze maze_draw(WINDOW, MAP_GAME.map_print().replace('\n', '')) -pygame.time.Clock().tick(25) # Game loop +# pygame.time.Clock().tick(25) last_wait = True while MAP_GAME.status: for event in pygame.event.get(): @@ -59,6 +57,7 @@ MAP_GAME.status_message['title'] = MSG_END set_header(WINDOW, MAP_GAME.status_message) pygame.display.flip() +# Loop for last messag before exit while last_wait: for event in pygame.event.get(): if event.type == KEYDOWN: diff --git a/map.py b/map.py index 45d4254..fbc7fa4 100644 --- a/map.py +++ b/map.py @@ -8,7 +8,10 @@ This file is part of [_ocp3_ project](https://github.com/freezed/ocp3) import os import random from pygame.locals import K_UP, K_DOWN, K_RIGHT, K_LEFT -from conf import elmt_val, ERR_MAP, MOVE_STATUS_MSG, HEADER_MESSAGES, MAZE_SIZE_TIL +from conf import ( + elmt_val, ERR_MAP, MSG_COLLECT, MSG_LOSER, MSG_OK, + MSG_WALL, MSG_WINNER, HEAD_MESSAGES, MAZE_SIZE +) class Map: @@ -35,22 +38,24 @@ class Map: :rtype map: str() :return: None """ - # Chargement du fichier carte choisi + # Loading map file if os.path.isfile(map_file) is True: with open(map_file, "r") as map_data: - # translate to a splited lines string list + # Translate to a splited lines string list map_in_a_list = map_data.read().splitlines() - # map line number - if len(map_in_a_list) == MAZE_SIZE_TIL: - self._COLUM = MAZE_SIZE_TIL + 1 - self._LINES = MAZE_SIZE_TIL + # Map line number + if len(map_in_a_list) == MAZE_SIZE: + self._COLUM = MAZE_SIZE + 1 + self._LINES = MAZE_SIZE self._MAXIM = (self._COLUM * self._LINES) - 1 - # Add map checking here + + # ++ Add other map checks here ++ # Constructing square map - map_in_a_list = [self.check_line(line) for line in map_in_a_list] + map_in_a_list = [self.check_line(line) + for line in map_in_a_list] self._map_in_a_string = '\n'.join(map_in_a_list) # Player's initial position @@ -59,44 +64,54 @@ class Map: ) # Element under player at start - self._element_under_player = elmt_val('symbol', 'name', 'void', 0) + self._element_under_player = elmt_val( + 'symbol', 'name', 'void', 0 + ) # Place collectables on the map for symbol_to_place in elmt_val('symbol', 'collect', True): - position = random.choice([idx for (idx, value) in enumerate(self._map_in_a_string) if value == elmt_val('symbol', 'name', 'void', 0)]) + position = random.choice( + [idx for (idx, value) in enumerate( + self._map_in_a_string + ) if value == elmt_val( + 'symbol', 'name', 'void', 0 + )]) self.place_element(symbol_to_place, pos=position) self.status = True self.collected_items = [] self.collected_items_num = 0 - self.MAX_ITEMS = sum(1 for _ in elmt_val('name', 'collect', True)) + self.MAX_ITEMS = sum( + 1 for _ in elmt_val('name', 'collect', True) + ) self.status_message = {} - self.status_message['title'] = HEADER_MESSAGES['title'] - self.status_message['status'] = HEADER_MESSAGES['status'] - self.status_message['items'] = HEADER_MESSAGES['items'].format(self.collected_items_num, self.MAX_ITEMS) + self.status_message['title'] = HEAD_MESSAGES['title'] + self.status_message['status'] = HEAD_MESSAGES['status'] + self.status_message['items'] \ + = HEAD_MESSAGES['items'].format( + self.collected_items_num, self.MAX_ITEMS + ) else: self.status = False print(ERR_MAP.format("maplines: " + str(len(map_in_a_list)))) - # Erreur de chargement du fichier + # File error else: self.status = False print(ERR_MAP.format(':'.join(["mapfile", map_file]))) def map_print(self): - """ Affiche la carte avec la position de jeu courante """ + """ Return a string of the map state """ return self._map_in_a_string def move_to(self, pressed_key): """ - Deplace le plyr sur la carte + Move the player on the map - :param str pressed_key: mouvement souhaite - :return int: une cle de la constante MOVE_STATUS + :param str pressed_key: direction (pygame const) """ - # supprime le plyr de son emplacement actuel et on replace - # l'elements «dessous» + # Replace player on the map by the under-element self._map_in_a_string = self._map_in_a_string.replace( elmt_val('symbol', 'name', 'player', 0), self._element_under_player @@ -115,50 +130,66 @@ class Map: elif pressed_key == K_LEFT: next_position = self._player_position - 1 + # TODO nove instruction to __init__ self._element_under_player = elmt_val('symbol', 'name', 'void', 0) - # Traitement en fonction de la case du prochain pas + # Next position treatment if next_position >= 0 and next_position <= self._MAXIM: next_char = self._map_in_a_string[next_position] if next_char == elmt_val('symbol', 'name', 'void', 0): self._player_position = next_position - self.status_message['status'] = MOVE_STATUS_MSG['ok'] + self.status_message['status'] = MSG_OK elif next_char in elmt_val('symbol', 'collect', True): self._player_position = next_position - self._element_under_player = elmt_val('symbol', 'name', 'void', 0) - self.collected_items.append(elmt_val('name', 'symbol', next_char, 0)) + self._element_under_player = elmt_val( + 'symbol', 'name', 'void', 0 + ) + self.collected_items.append( + elmt_val('name', 'symbol', next_char, 0) + ) self.collected_items_num += 1 - self.status_message['status'] = MOVE_STATUS_MSG['collect'].format(elmt_val('name', 'symbol', next_char, 0)) - self.status_message['items'] = HEADER_MESSAGES['items'].format(self.collected_items_num, self.MAX_ITEMS) + self.status_message['status'] = MSG_COLLECT.format( + elmt_val('name', 'symbol', next_char, 0) + ) + self.status_message['items'] \ + = HEAD_MESSAGES['items'].format( + self.collected_items_num, self.MAX_ITEMS + ) elif next_char == elmt_val('symbol', 'name', 'exit', 0): self.status = False - if sorted(self.collected_items) == sorted(elmt_val('name', 'collect', True)): - self.status_message['status'] = MOVE_STATUS_MSG['winner'] + if sorted(self.collected_items) == sorted( + elmt_val('name', 'collect', True) + ): + self.status_message['status'] = MSG_WINNER else: - missed_item_flist = ', '.join((item for item in elmt_val('name', 'collect', True) if item not in self.collected_items)) - self.status_message['status'] = MOVE_STATUS_MSG['looser'].format(missed_item_flist) + missed_item_flist = ', '.join( + (item for item in elmt_val( + 'name', 'collect', True + ) if item not in self.collected_items) + ) + self.status_message['status'] = MSG_LOSER.format( + missed_item_flist + ) - else: # wall, door or nline - self.status_message['status'] = MOVE_STATUS_MSG['wall'] + else: # wall or nline + self.status_message['status'] = MSG_WALL else: - self.status_message['status'] = MOVE_STATUS_MSG['wall'] + self.status_message['status'] = MSG_WALL - # place le plyr sur sa nouvelle position + # Set the player on position self.place_element(elmt_val('symbol', 'name', 'player', 0)) def place_element(self, element, **kwargs): """ - Place l'element sur la carte. + Set an element on the map - La position est celle de l'attribut self._player_position au - moment de l'appel. + The position used is in ._player_position attribute + Used for player and void after collecting items - Utilise pour place le plyrt et remettre les portes. - - :param str element: element a placer sur la carte + :param str element: the string of the element to place """ # FIXME cannot find a way to define default value to the # method's arguments with class attributes @@ -177,12 +208,12 @@ class Map: @staticmethod def check_line(line): """ - Checks if a line has a good length, fill it if it's too small, - truncate if it's too long + Checks if a line has a good length (configured in MAZE_SIZE const). + Fill it if it's too small, truncate if it's too long. """ - differance = MAZE_SIZE_TIL - len(str(line)) + differance = MAZE_SIZE - len(str(line)) if differance < 0: - return line[:MAZE_SIZE_TIL] + return line[:MAZE_SIZE] elif differance > 0: return line + (differance * elmt_val('symbol', 'name', 'void', 0)) else: