diff --git a/editor/editor.py b/editor/editor.py index d6e810c..aa9f6f3 100755 --- a/editor/editor.py +++ b/editor/editor.py @@ -3,38 +3,397 @@ # Copyright (c) 2014, Miguel Angel Astor Romero. All rights reserved. # See the file LICENSE for more details. +""" +The map editor for TITLE PENDING, a game for the Cyberpunk Jam 2014. + +Usage: + *) Click on a game_map cell to set it's type. + Use the keypad numbers to change the type of cell a cell will turn into + when clicked. The active cell type is displayed on the title bar. + + *) Left click to place an object on the game_map. + Objects have to be edited by hand once the game_map has been saved. Use + the keys 'd', 'k', 'p', 'n', 'e' and 'q' to set the active object type. + The active object type is displayed on the title bar. + + *) Press 'p' to set the player's starting position. + + *) Press 's' to save the map to the text file 'map_file.map'. This will + replace any previous map with the same name! + + Press escape to quit. +""" + import pygame -SCREEN_SIZE = (800, 600) -TITLE = "Cyberpunk Jam 2014 - Level editor" -FPS = 60 +CELL_SIZE = 10 +MAP_SIZE = (64, 64) +SCREEN_SIZE = ((MAP_SIZE[1] * CELL_SIZE) + 1, (MAP_SIZE[0] * CELL_SIZE) + 1) +SCREEN_FLAGS = pygame.HWSURFACE | pygame.DOUBLEBUF +TITLE = "Cyberpunk Jam 2014 - Level editor" +FPS = 60 + +# Cell types +VOID = 0 +SOLID_WALL = 1 +SECRET_WALL = 2 +CLEAR_WALL = 3 +NEON_WALL = 4 +WINDOW_WALL = 5 +EMPTY_FLOOR = 6 +RUG = 7 +WATER = 8 +BAR = 9 + +# Object types +DOOR = 0 +KEY = 1 +PERSON = 2 +PLAYER = 3 +EXIT = 4 + +def get_object_type_name(game_object): + """ Return a string name for an object type. """ + name = "" + if game_object == DOOR: + name = "DOOR" + elif game_object == KEY: + name = "KEY" + elif game_object == PERSON: + name = "PERSON" + elif game_object == PLAYER: + name = "PLAYER" + elif game_object == EXIT: + name = "EXIT" + elif game_object == None: + name = "NONE" + else: + raise ValueError("Invalid object type.") + + return name + +def get_object_type_color(game_object): + """ Return a string name for an object type. """ + color = (0, 0, 0) + if game_object == DOOR: + color = (255, 0, 255) + elif game_object == KEY: + color = (255, 255, 0) + elif game_object == PERSON: + color = (0, 255, 255) + elif game_object == PLAYER: + color = (128, 255, 0) + elif game_object == EXIT: + color = (255, 0, 255) + else: + raise ValueError("Invalid object type.") + + return color + +def get_floor_type_name(floor_type): + """ Return a string name for a floor type. """ + name = "" + + if floor_type == VOID: + name = "VOID" + elif floor_type == SOLID_WALL: + name = "SOLID WALL" + elif floor_type == SECRET_WALL: + name = "SECRET WALL" + elif floor_type == CLEAR_WALL: + name = "CLEAR WALL" + elif floor_type == NEON_WALL: + name = "NEON WALL" + elif floor_type == WINDOW_WALL: + name = "WINDOW" + elif floor_type == EMPTY_FLOOR: + name = "EMPTY FLOOR" + elif floor_type == RUG: + name = "RUG" + elif floor_type == WATER: + name = "WATER" + elif floor_type == BAR: + name = "BAR" + else: + raise ValueError("Invalid floor type.") + + return name + +def get_floor_type_color(floor_type): + """ Return a string name for a floor type. """ + color = (0, 0, 0) + + if floor_type == VOID: + color = (0, 0, 0) + elif floor_type == SOLID_WALL: + color = (255, 255, 255) + elif floor_type == SECRET_WALL: + color = (128, 128, 128) + elif floor_type == CLEAR_WALL: + color = (50, 50, 50) + elif floor_type == NEON_WALL: + color = (255, 0, 0) + elif floor_type == WINDOW_WALL: + color = (128, 128, 255) + elif floor_type == EMPTY_FLOOR: + color = (128, 128, 0) + elif floor_type == RUG: + color = (128, 0, 128) + elif floor_type == WATER: + color = (64, 64, 255) + elif floor_type == BAR: + color = (255, 255, 0) + else: + raise ValueError("Invalid floor type.") + + return color + +def save_map(game_map): + p_id = 0 + dialogs = list() + f = open("map_file.map", "w") + + f.write("[MAP]\n") + f.write(str(MAP_SIZE [0]) + " " + str(MAP_SIZE[1]) + "\n") + for i in range(0, MAP_SIZE[0]): + for j in range(0, MAP_SIZE[1]): + f_t = game_map[i][j].get_type(); + f.write(str(f_t)) + f.write("\n") + + f.write("[PLAYER]\n") + f.write("%player = X Y\n") + for i in range(0, MAP_SIZE[0]): + for j in range(0, MAP_SIZE[1]): + o_t = game_map[i][j].get_object(); + if o_t is not None and o_t == PLAYER: + f.write("player = " + str(i) + " " + str(j) + "\n") + + f.write("[EXITS]\n") + f.write("%exit = X Y MAP_NAME MAP_X MAP_Y\n") + for i in range(0, MAP_SIZE[0]): + for j in range(0, MAP_SIZE[1]): + o_t = game_map[i][j].get_object(); + if o_t is not None and o_t == EXIT: + f.write("exit = " + str(i) + " " + str(j)) + f.write(" map_file.map 0 0\n") + + f.write("[DOORS]\n") + f.write("%door = X Y ID UNLOCKED\n") + for i in range(0, MAP_SIZE[0]): + for j in range(0, MAP_SIZE[1]): + o_t = game_map[i][j].get_object(); + if o_t is not None and o_t == DOOR: + f.write("door = " + str(i) + " " + str(j) + " 0" + " 0\n") + + f.write("[KEYS]\n") + f.write("%key = X Y ID\n") + for i in range(0, MAP_SIZE[0]): + for j in range(0, MAP_SIZE[1]): + o_t = game_map[i][j].get_object(); + if o_t is not None and o_t == KEY: + f.write("key = " + str(i) + " " + str(j) + " 0\n") + + f.write("[PERSONS]\n") + f.write("%person = X Y NAME DIALOG_ID\n") + for i in range(0, MAP_SIZE[0]): + for j in range(0, MAP_SIZE[1]): + o_t = game_map[i][j].get_object(); + if o_t is not None and o_t == PERSON: + f.write("person = " + str(i) + " " + str(j)) + f.write(" SOMEONE " + str(p_id) + "\n") + dialogs.append(p_id) + p_id += 1 + + f.write("[DIALOGS]\n") + f.write("%dialog = ID TEXT\n") + for id in dialogs: + text = " Heavy boxes perform quick waltzes and jigs.\n" + f.write("dialog = " + str(id) + text) + + f.close() + +class Cell: + """ A game_map cell. """ + def __init__(self, x_size, y_size): + """ Create a new cell. """ + self.x_size = x_size + self.y_size = y_size + + self.floor_type = VOID + self.game_object = None + + # Create the drawing rectangles. + self.border = pygame.Rect((0, 0), (CELL_SIZE, CELL_SIZE)) + self.cell = pygame.Rect((0, 0), (CELL_SIZE - 2, CELL_SIZE - 2)) + + def set_type(self, floor_type): + """ Set this cell floor type. Raises a value error if the type is + not valid. """ + if floor_type < VOID or floor_type > BAR: + raise ValueError("Floor type must be a number between 0 and 9.") + else: + self.floor_type = floor_type + + def get_type(self): + """ Returns this cell's type. """ + return self.floor_type + + def set_object(self, game_object): + """ Set this cells object. """ + if game_object is not None and game_object < DOOR or game_object > EXIT: + raise ValueError("Object type must be a number between 0 and 2.") + else: + self.game_object = game_object + + def get_object(self): + """ Return this cell's object. """ + return self.game_object + + def draw(self, canvas, x_pos, y_pos): + """ Render this cell at the given position on the given screen. """ + self.border.center = (x_pos, y_pos) + self.cell.center = (x_pos, y_pos) + pygame.draw.rect(canvas, (0, 0, 255), self.border) + + color = get_floor_type_color(self.floor_type) + pygame.draw.rect(canvas, color, self.cell) + + if self.game_object is not None and self.game_object < PLAYER: + r = (CELL_SIZE - 2) // 2 + color = get_object_type_color(self.game_object) + pygame.draw.circle(canvas, color, (x_pos, y_pos), r, 1) + elif self.game_object is not None and self.game_object >= PLAYER: + r = (CELL_SIZE - 3) // 2 + color = get_object_type_color(self.game_object) + pygame.draw.circle(canvas, color, (x_pos, y_pos), r, 0) def main(): + """ Application entry point. """ # Local variables done = False + game_map = list() + curr_obj = None + curr_fl = VOID + obj_str = "" + fl_str = "" + mouse_pos = (0, 0) + mouse_r_click = False + mouse_l_click = False - # Initialize Pygame and pgs4a. + # Initialize Pygame. pygame.init() clock = pygame.time.Clock() - screen = pygame.display.set_mode(SCREEN_SIZE, pygame.HWSURFACE | pygame.DOUBLEBUF) - pygame.mouse.set_visible(False) + screen = pygame.display.set_mode(SCREEN_SIZE, SCREEN_FLAGS) + pygame.mouse.set_visible(True) + + for i in range(0, MAP_SIZE[0]): + game_map.append([Cell(10, 10) for j in range(0, MAP_SIZE[1])]) # Main game loop. while(not done): try: fps = clock.get_fps() + 0.001 - pygame.display.set_caption(TITLE + ": " + str(int(fps))) + + # Set title bar. + obj_str = get_object_type_name(curr_obj) + fl_str = get_floor_type_name(curr_fl) + title = TITLE + " :: FPS: " + str(int(fps)) + title += " :: " + obj_str + " :: " + fl_str + pygame.display.set_caption(title) # Input capture. for event in pygame.event.get(): - if (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE) or event.type == pygame.QUIT: + if event.type == pygame.QUIT: + done = True + + if event.type == pygame.MOUSEBUTTONDOWN: + mouse_pos = event.pos + if event.button == 1: + mouse_r_click = True + elif event.button == 3: + mouse_l_click = True + + if event.type == pygame.MOUSEMOTION: + if mouse_r_click: + mouse_pos = event.pos + + if event.type == pygame.MOUSEBUTTONUP: + mouse_pos = event.pos + if event.button == 1: + mouse_r_click = False + elif event.button == 3: + mouse_l_click = False + + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE: done = True + if event.key == pygame.K_d: + curr_obj = DOOR + if event.key == pygame.K_k: + curr_obj = KEY + if event.key == pygame.K_p: + curr_obj = PERSON + if event.key == pygame.K_q: + curr_obj = PLAYER + if event.key == pygame.K_e: + curr_obj = EXIT + if event.key == pygame.K_n: + curr_obj = None + + if event.key == pygame.K_0: + curr_fl = VOID + if event.key == pygame.K_1: + curr_fl = SOLID_WALL + if event.key == pygame.K_2: + curr_fl = SECRET_WALL + if event.key == pygame.K_3: + curr_fl = CLEAR_WALL + if event.key == pygame.K_4: + curr_fl = NEON_WALL + if event.key == pygame.K_5: + curr_fl = WINDOW_WALL + if event.key == pygame.K_6: + curr_fl = EMPTY_FLOOR + if event.key == pygame.K_7: + curr_fl = RUG + if event.key == pygame.K_8: + curr_fl = WATER + if event.key == pygame.K_9: + curr_fl = BAR + + if event.key == pygame.K_s: + save_map(game_map) + # Update cycle. + if mouse_r_click: + i = mouse_pos[1] // CELL_SIZE + j = mouse_pos[0] // CELL_SIZE + if i >= MAP_SIZE[0]: + i = MAP_SIZE[0] - 1 + if j >= MAP_SIZE[1]: + j = MAP_SIZE[1] - 1 + game_map[i][j].set_type(curr_fl) + + if mouse_l_click: + i = mouse_pos[1] // CELL_SIZE + j = mouse_pos[0] // CELL_SIZE + if i >= MAP_SIZE[0]: + i = MAP_SIZE[0] - 1 + if j >= MAP_SIZE[1]: + j = MAP_SIZE[1] - 1 + game_map[i][j].set_object(curr_obj) # Render cycle. screen.fill((0, 0, 0)) + for i in range(0, MAP_SIZE[0]): + for j in range(0, MAP_SIZE[1]): + x = (j * CELL_SIZE) + (CELL_SIZE // 2) + y = (i * CELL_SIZE) + (CELL_SIZE // 2) + game_map[i][j].draw(screen, x, y) + pygame.display.update() clock.tick(FPS)