def read_level(filename, w, h, types, bonuses): """ Gets level information from file and returns new level based on it w, h - level size types - amount of each type of ball """ with open(filename, 'r') as f: data = json.load(f) name = data.get('Name') amount = data.get('Amount') radius = data.get('Raduis') ball_speed = data.get('BallSpeed') bull_speed = data.get('BulletSpeed') tex_prefix = data.get('TexturesPrefix') points = [(i[0], i[1]) for i in data.get('Checkpoints')] highscores = data.get('Highscores') pos = data.get('PlayerPos') player_pos = (pos[0], pos[1]) while len(highscores) < 5: highscores.append(0) return Level(name, w, h, types, points, amount, radius, ball_speed, player_pos, 3, bull_speed, tex_prefix, highscores, bonuses)
def load_level(filename) -> Level: with open(filename, "rb") as f: board, palette = pickle.load(f) return Level(board, palette)
test_board = Board({ (1, 3): [ResourceTile(Color.RED)], (3, 0): [Boostpad(Direction.WEST)], (-5, 9): [ResourceTile(Color.BLUE)], (-5, -3): [Target(Color.VIOLET, 5)], }) test_palette = Palette([ (EntityPrototype(ResourceExtractor), 2), (EntityPrototype(Boostpad), 1), ]) test_level = Level(test_board, test_palette) # a, b = choice(list(Color)), choice(list(Color)) a, b = Color.RED, Color.BLUE test_board2 = Board({ (-3, 0): [ResourceTile(a), ResourceExtractor(Direction.EAST)], (3, 0): [ResourceTile(b), ResourceExtractor(Direction.WEST)], (0, 0): [Boostpad(Direction.NORTH)], (5, -8): [Target(a + b, 10)], (5, -5): [Piston()], (7, -7): [Piston()], (10, -7): [Piston()], # (5, -6): [Barrel(Color.YELLOW)], # (5, -7): [Barrel(Color.BLUE)],
title_menu = TitleMenu( name="TITLE", titles={ "en": "Tomb Raider", "de": "Tomb Raider", }, track=TR1TrackId.MainTheme, secrets=0, ) lara_home = Level( name="GYM", titles={ "en": "Lara's home", "de": "Laras Haus", }, track=TR1TrackId.Ambience1, secrets=0, use_alternative_lara=True, ) level_sequence = [ Video("SNOW.RPL"), Level( name="LEVEL1", titles={ "en": "Caves", "de": "Die Kavernen", }, track=TR1TrackId.Ambience1, secrets=3,
from engine import Board, Level, Palette from entities import * from level_helpers import disk, random_flood level_1 = Level( Board({ **random_flood((0, 0), 10, ResourceTile(Color.BLUE)), (12, 0): [Target(Color.BLUE, count=10)], }), Palette([ (EntityPrototype(ResourceExtractor), 1) ]), name="Level 1" ) level_2 = Level( Board({ **random_flood((0, 0), 10, ResourceTile(Color.RED)), (8, 8): [Target(Color.RED, count=10)], }), Palette([ (EntityPrototype(ResourceExtractor), 1), (EntityPrototype(Boostpad), 1), ]), name="Level 2" ) level_3 = Level(
def run_editor(board=None): level_filename = None board_width, board_height = None, None root_viewport_rect, main_viewport_rect, palette_viewport_rect = None, None, None # initialize screen; VIDEORESIZE event is generated immediately screen = get_initialized_screen(STARTING_SCREEN_WIDTH, STARTING_SCREEN_HEIGHT) board_layer_cache = None if board is None: board = [[[] for _ in range(BOARD_DEFAULT_DIMS[0])] for _ in range(BOARD_DEFAULT_DIMS[1])] selected_entity = None key_mods = pygame.key.get_mods() board_save_state = board_copy(board) playtest_process = None # discard selected entity and update screen (if CAPS-LOCK is not enabled) def discard_selected_item(): nonlocal selected_entity nonlocal root_viewport_rect, main_viewport_rect, palette_viewport_rect if selected_entity: if not key_mods & pygame.KMOD_CAPS: # discard selected entity selected_entity = None update_screen(screen, board, main_viewport_rect, palette_viewport_rect) else: # keep selected entity update_screen(screen, board, main_viewport_rect, palette_viewport_rect, redraw_board=True, selected_entity=selected_entity, cursor_position=event.pos) # recalculate board dimensions, recalculate viewports, and update screen def refresh_layout(): nonlocal board_width, board_height board_width, board_height = len(board[0]), len(board) nonlocal root_viewport_rect, main_viewport_rect, palette_viewport_rect root_viewport_rect, main_viewport_rect, palette_viewport_rect =\ get_viewport_rects(new_screen_width, new_screen_height, board_width, board_height) update_screen(screen, board, main_viewport_rect, palette_viewport_rect) # update window caption based off level_filename def refresh_caption(): if level_filename: caption = level_filename else: caption = "~ Unsaved Level ~" pygame.display.set_caption(caption) refresh_caption() # main game loop clock = pygame.time.Clock() editor_alive = True while editor_alive: clock.tick(TARGET_FPS) # process input for event in pygame.event.get(): if event.type == pygame.QUIT: if board_save_state != board: # if board_save_state is None or any(any(row) for row in board): if not ask_yes_no( "Level Editor", "You have unsaved work. Are you sure you want to quit?" ): continue editor_alive = False elif event.type == pygame.VIDEORESIZE: new_screen_width = max(event.w, MIN_SCREEN_WIDTH) new_screen_height = max(event.h, MIN_SCREEN_HEIGHT) screen = get_initialized_screen(new_screen_width, new_screen_height) refresh_layout() elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # handle main viewport clicks if main_viewport_rect.collidepoint(event.pos): x_tiles, y_tiles = pixels_to_tiles( *event.pos, main_viewport_rect, board_width, board_height) clicked_tile = board[y_tiles][x_tiles] if selected_entity is None: # select an entity and redraw if len(clicked_tile) > 0: selected_entity = clicked_tile.pop( ) # remove top entity update_screen(screen, board, main_viewport_rect, palette_viewport_rect, redraw_board=True, selected_entity=selected_entity, cursor_position=event.pos) else: # deselect the entity and redraw clicked_tile.append(selected_entity) discard_selected_item() # handle palette viewport clicks elif palette_viewport_rect.collidepoint(event.pos): if selected_entity: selected_entity = None update_screen(screen, board, main_viewport_rect, palette_viewport_rect) else: x_tiles, y_tiles = pixels_to_tiles_palette( *event.pos, palette_viewport_rect, PALETTE_WIDTH, PALETTE_HEIGHT) choice = PALETTE_LAYOUT[y_tiles][x_tiles] selected_entity = choice update_screen(screen, board, main_viewport_rect, palette_viewport_rect, redraw_board=True, selected_entity=selected_entity, cursor_position=event.pos) # handle background clicks (i.e. no viewports) else: discard_selected_item() elif event.button == 3: discard_selected_item() elif event.type == pygame.MOUSEMOTION: if selected_entity: if root_viewport_rect.collidepoint(event.pos): update_screen(screen, board, main_viewport_rect, palette_viewport_rect, redraw_board=False, selected_entity=selected_entity, cursor_position=event.pos) elif event.type == pygame.KEYDOWN: key_mods = pygame.key.get_mods() # handle board size changes board_size_changed = False decreasing = key_mods & pygame.KMOD_SHIFT increasing = not decreasing # # format (x, y, delta) # size_delta = [0, 0, 0] # one of (0,0,0), (-1,0,1), (1,0,1), (0,-1,1), (0,1,1), # # (-1,0,-1), (1,0,-1), (0,-1,-1), (0,1,-1) if event.key == pygame.K_UP: if increasing and board_height < BOARD_HEIGHT_RANGE[1]: board.insert(0, [[] for _ in range(board_width)]) board_size_changed = True elif decreasing and board_height > BOARD_HEIGHT_RANGE[0]: board.pop(0) board_size_changed = True elif event.key == pygame.K_DOWN: if increasing and board_height < BOARD_HEIGHT_RANGE[1]: board.append([[] for _ in range(board_width)]) board_size_changed = True elif decreasing and board_height > BOARD_HEIGHT_RANGE[0]: board.pop() board_size_changed = True elif event.key == pygame.K_RIGHT: if increasing and board_width < BOARD_WIDTH_RANGE[1]: for row in board: row.append([]) board_size_changed = True elif decreasing and board_height > BOARD_WIDTH_RANGE[0]: for row in board: row.pop() board_size_changed = True elif event.key == pygame.K_LEFT: if increasing and board_width < BOARD_WIDTH_RANGE[1]: for row in board: row.insert(0, []) board_size_changed = True elif decreasing and board_height > BOARD_WIDTH_RANGE[0]: for row in board: row.pop() board_size_changed = True if board_size_changed: refresh_layout() # handle keyboard shortcuts if key_mods & pygame.KMOD_CTRL: if event.key == pygame.K_o: # Open if board_save_state != board: if not ask_yes_no( "Level Editor", "You have unsaved work that will be overwitten by opening another level. Are you sure you want to continue?" ): continue if res := ask_open_filename(**FILE_DIALOG_OPTIONS): level_filename = res board = read_level(level_filename) board_save_state = board_copy(board) refresh_layout() refresh_caption() print(f"opened {level_filename}") elif event.key == pygame.K_s: if key_mods & pygame.KMOD_SHIFT: # Save as if res := ask_save_as_filename( **FILE_DIALOG_OPTIONS): level_filename = res write_level(level_filename, board) board_save_state = board_copy(board) refresh_caption() print(f"saved to {level_filename}") else: # Save if level_filename is None: if res := ask_save_as_filename( **FILE_DIALOG_OPTIONS): level_filename = res if level_filename: write_level(level_filename, board) board_save_state = board_copy(board) refresh_caption() print(f"saved to {level_filename}") elif event.key == pygame.K_SPACE: # spawn a new process running play_level (can only have one alive at a time) if playtest_process is None or not playtest_process.is_alive( ): playtest_process = Process(target=play_level, args=(Level( board_copy(board), logging=False), )) playtest_process.start()
if event.key == currently_pressed: currently_pressed = None repeating_inputs = False elif event.type == pygame.VIDEORESIZE: new_screen_width = max(event.w, MIN_SCREEN_WIDTH) new_screen_height = max(event.h, MIN_SCREEN_HEIGHT) screen = get_initialized_screen(new_screen_width, new_screen_height) pygame.display.update() viewport_rect = get_viewport_rect(new_screen_width, new_screen_height, level.width, level.height) update_screen(screen, level, viewport_rect) if currently_pressed is not None: current_timestamp = pygame.time.get_ticks() if current_timestamp - last_input_timestamp > INPUT_REPEAT_BUFFER_MS: repeating_inputs = True if repeating_inputs and current_timestamp - last_input_timestamp > INPUT_REPEAT_PERIOD_MS: process_keypress(event.key) if level.has_won: print("\nCongrats! You beat the level!") pygame.time.wait(1000) level_alive = False if __name__ == "__main__": # load a test level (with logging disabled) test_level = Level(levels[2], logging=False) play_level(test_level)
title_menu = TitleMenu( name="DATA/TITLE.PHD", titles={ "en_GB": "Tomb Raider", "de_DE": "Tomb Raider", }, track=TR1TrackId.MainTheme, ) lara_home = [ Video("FMV/MANSION.RPL"), Level( name="DATA/GYM.PHD", titles={ "en_GB": "Lara's home", "de_DE": "Laras Haus", }, secrets=0, use_alternative_lara=True, allow_save=False, ), ] level_sequence = [ Video("FMV/SNOW.RPL"), ModifyInventory( add_inventory={TR1ItemId.Pistols: 1}, ), Level( name="DATA/LEVEL1.PHD", titles={ "en_GB": "Caves",