# Imports import pygame import pickle import os # Constants tile_size = 50 door_height = 75 cols = 24 rows = 20 screen_width = 1530 screen_height = 1000 editor_width = 1200 level_folder = "Gilbert/Levels" world_data = [[0 for _ in range(cols)] for _ in range(rows)] sprite_data = {} current_level = 1 latest_level = 0 show_help = False coin_size = 25 spawn_point = None # Variables for customization inputs speed_input = "" delay_input = "" active_input = None # Initialize pygame pygame.init() pygame.mixer.init() clock = pygame.time.Clock() screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption('Level Editor') # Load background image background_img = pygame.image.load('Gilbert/Images/Background.png') background_img = pygame.transform.scale(background_img, (1201, screen_height)) # Load tile images block_img = pygame.image.load('Gilbert/Images/Block.png') block_img = pygame.transform.scale(block_img, (tile_size, tile_size)) block2_img = pygame.image.load('Gilbert/Images/Block2.png') block2_img = pygame.transform.scale(block2_img, (tile_size, tile_size)) # Load additional tile images block3_img = pygame.image.load('Gilbert/Images/Block3.png') block3_img = pygame.transform.scale(block3_img, (tile_size, tile_size)) block4_img = pygame.image.load('Gilbert/Images/Block4.png') block4_img = pygame.transform.scale(block4_img, (tile_size, tile_size)) block5_img = pygame.image.load('Gilbert/Images/Block5.png') block5_img = pygame.transform.scale(block5_img, (tile_size, tile_size)) block6_img = pygame.image.load('Gilbert/Images/Block6.png') block6_img = pygame.transform.scale(block6_img, (tile_size, tile_size)) block7_img = pygame.image.load('Gilbert/Images/Block7.png') block7_img = pygame.transform.scale(block7_img, (tile_size, tile_size)) block8_img = pygame.image.load('Gilbert/Images/Block8.png') block8_img = pygame.transform.scale(block8_img, (tile_size, tile_size)) block9_img = pygame.image.load('Gilbert/Images/Block9.png') block9_img = pygame.transform.scale(block9_img, (tile_size, tile_size)) block10_img = pygame.image.load('Gilbert/Images/Block10.png') block10_img = pygame.transform.scale(block10_img, (tile_size, tile_size)) block11_img = pygame.image.load('Gilbert/Images/Block11.png') block11_img = pygame.transform.scale(block11_img, (tile_size, tile_size)) block12_img = pygame.image.load('Gilbert/Images/Block12.png') block12_img = pygame.transform.scale(block12_img, (tile_size, tile_size)) block13_img = pygame.image.load('Gilbert/Images/Block13.png') block13_img = pygame.transform.scale(block13_img, (tile_size, tile_size)) block14_img = pygame.image.load('Gilbert/Images/Block14.png') block14_img = pygame.transform.scale(block14_img, (tile_size, tile_size)) block15_img = pygame.image.load('Gilbert/Images/Block15.png') block15_img = pygame.transform.scale(block15_img, (tile_size, tile_size)) crossout_img = pygame.image.load('Gilbert/Images/Crossout.png') crossout_img = pygame.transform.scale(crossout_img, (tile_size, tile_size)) # Adjust button sizes button_width, button_height = 140, 50 reset_button_width = button_width # Load button images button_bg_img = pygame.image.load('Gilbert/Images/Button_BG.png') button_bg_img = pygame.transform.scale(button_bg_img, (329, screen_height)) silver_button_img = pygame.image.load('Gilbert/Images/Silver_Button.png') gold_button_img = pygame.image.load('Gilbert/Images/Gold_Button.png') silver_middle_button_img = pygame.image.load('Gilbert/Images/Silver_Middle_Button.png') gold_middle_button_img = pygame.image.load('Gilbert/Images/Gold_Middle_Button.png') # Resize and flip button images silver_button_img = pygame.transform.scale(silver_button_img, (button_width, button_height)) gold_button_img = pygame.transform.scale(gold_button_img, (button_width, button_height)) silver_button_flipped_img = pygame.transform.flip(silver_button_img, False, True) gold_button_flipped_img = pygame.transform.flip(gold_button_img, False, True) silver_middle_button_img = pygame.transform.scale(silver_middle_button_img, (button_width, button_height)) gold_middle_button_img = pygame.transform.scale(gold_middle_button_img, (button_width, button_height)) silver_button_reset_img = pygame.transform.scale(silver_button_img, (reset_button_width, button_height)) gold_button_reset_img = pygame.transform.scale(gold_button_img, (reset_button_width, button_height)) selection_outline_img = pygame.image.load('Gilbert/Images/Silver_Outline.png') selection_outline_img = pygame.transform.scale(selection_outline_img, (tile_size + 6, tile_size + 6)) # Load sprite inventory images Acid_img = pygame.image.load('Gilbert/Images/Acid.png') Acid_img = pygame.transform.scale(Acid_img, (tile_size, tile_size)) Acid2_img = pygame.image.load('Gilbert/Images/Acid2.png') Acid2_img = pygame.transform.scale(Acid2_img, (tile_size, tile_size)) zombie_img = pygame.image.load('Gilbert/Images/Zombie.png') zombie_img = pygame.transform.scale(zombie_img, (tile_size, tile_size)) moving_tile_img = pygame.image.load('Gilbert/Images/Moving_Tile.png') moving_tile_img = pygame.transform.scale(moving_tile_img, (tile_size, tile_size)) gold_coin_img = pygame.image.load('Gilbert/Images/Gold_1.png') gold_coin_img = pygame.transform.scale(gold_coin_img, (coin_size, coin_size)) key_img = pygame.image.load('Gilbert/Images/Key_0.png') key_img = pygame.transform.scale(key_img, (tile_size, tile_size)) door_inv_img = pygame.image.load('Gilbert/Images/DoorInv.png') door_inv_img = pygame.transform.scale(door_inv_img, (tile_size, tile_size)) door_img = pygame.image.load('Gilbert/Images/DoorLocked.png') door_img = pygame.transform.scale(door_img, (tile_size, door_height)) spawn_point_img = pygame.image.load('Gilbert/Images/Spawnpoint.png') spawn_point_img = pygame.transform.scale(spawn_point_img, (tile_size, tile_size)) # Define colors white = (255, 255, 255) black = (0, 0, 0) green = (144, 201, 120) light_green = (170, 227, 153) red = (255, 0, 0) dark_red = (162, 29, 29) light_red = (255, 102, 102) yellow = (255, 255, 0) blue = (0, 0, 255) # Font font = pygame.font.SysFont('Futura', 24) gameplay_font = pygame.font.Font('Gilbert/Fonts/Gameplay.ttf', 24) # Function for outputting text onto the screen def draw_text(text, font, text_col, x, y): img = font.render(text, True, text_col) screen.blit(img, (x, y)) # Function to draw grid def draw_grid(): for c in range(cols + 1): pygame.draw.line(screen, dark_red, (c * tile_size, 0), (c * tile_size, screen_height)) for r in range(rows + 1): pygame.draw.line(screen, dark_red, (0, r * tile_size), (editor_width, r * tile_size)) # Function to draw world def draw_world(): for row in range(rows): for col in range(cols): if world_data[row][col] == 1: screen.blit(block_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 2: screen.blit(block2_img, (col * tile_size, row * tile_size)) # Draw additional tiles elif world_data[row][col] == 3: screen.blit(block3_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 4: screen.blit(block4_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 5: screen.blit(block5_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 6: screen.blit(block6_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 7: screen.blit(block7_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 8: screen.blit(block8_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 9: screen.blit(block9_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 10: screen.blit(block10_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 11: screen.blit(block11_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 12: screen.blit(block12_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 13: screen.blit(block13_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 14: screen.blit(block14_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 15: screen.blit(block15_img, (col * tile_size, row * tile_size)) # Draw sprite tiles elif world_data[row][col] == 101: screen.blit(Acid_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 102: screen.blit(Acid2_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 103: screen.blit(zombie_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 104: screen.blit(moving_tile_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 105: screen.blit(gold_coin_img, (col * tile_size + (tile_size - coin_size) // 2, row * tile_size + (tile_size - coin_size) // 2)) elif world_data[row][col] == 106: screen.blit(key_img, (col * tile_size, row * tile_size)) elif world_data[row][col] == 107: screen.blit(door_img, (col * tile_size, row * tile_size - (door_height - tile_size))) # Draw the spawn point if spawn_point: screen.blit(spawn_point_img, (spawn_point[1] * tile_size, spawn_point[0] * tile_size)) current_level = 0 def handle_button_click(button_type): global current_level, latest_level, world_data, sprite_data, spawn_point if button_type == "save": # Find the latest level number for file_name in os.listdir(level_folder): if file_name.startswith("level") and file_name.endswith("_data"): level_number = int(file_name.split("_")[0].replace("level", "")) if level_number > latest_level: latest_level = level_number next_level = latest_level + 1 level_name = f"level{next_level}_data" print("Saving level as:", level_name) # Save level data try: with open(os.path.join(level_folder, level_name), 'wb') as f: pickle.dump((world_data, sprite_data, spawn_point), f) print("Level saved successfully.") except Exception as e: print("Error saving level:", e) elif button_type == "load": # Find the latest level number latest_level = 0 for file_name in os.listdir(level_folder): if file_name.startswith("level") and file_name.endswith("_data"): level_number = int(file_name.split("_")[0].replace("level", "")) if level_number > latest_level: latest_level = level_number print("Latest level found:", latest_level) if latest_level > 0: current_level = (current_level % latest_level) + 1 level_name = f"level{current_level}_data" level_path = os.path.join(level_folder, level_name) print("Loading level:", level_path) if os.path.exists(level_path): try: with open(level_path, 'rb') as f: loaded_data = pickle.load(f) world_data, sprite_data, spawn_point = loaded_data print("Level loaded successfully:", level_name) except Exception as e: print("Error loading level:", e) else: print(f"Level {level_name} does not exist.") current_level = 1 else: print("No level files found.") elif button_type == "reset": handle_reset() # Initialize history lists undo_stack = [] redo_stack = [] # Function to add current state to history def add_to_history(): global undo_stack, redo_stack world_data_copy = [row[:] for row in world_data] sprite_data_copy = {k: v.copy() for k, v in sprite_data.items()} undo_stack.append((world_data_copy, sprite_data_copy, spawn_point)) redo_stack.clear() # Function to handle undo def handle_undo(): global world_data, sprite_data, undo_stack, redo_stack, spawn_point if undo_stack: # Add the current state to redo stack before undoing redo_stack.append(([row[:] for row in world_data], {k: v.copy() for k, v in sprite_data.items()}, spawn_point)) world_data, sprite_data, spawn_point = undo_stack.pop() print("Undo successful.") else: print("Nothing to undo.") # Function to handle redo def handle_redo(): global world_data, sprite_data, undo_stack, redo_stack, spawn_point if redo_stack: # Add the current state to undo stack before redoing undo_stack.append(([row[:] for row in world_data], {k: v.copy() for k, v in sprite_data.items()}, spawn_point)) world_data, sprite_data, spawn_point = redo_stack.pop() print("Redo successful.") else: print("Nothing to redo.") # Function to handle reset and add it to history def handle_reset(): add_to_history() for i in range(rows): for j in range(cols): world_data[i][j] = 0 sprite_data.clear() spawn_point = None add_to_history() # Function to draw a button with hover effect and centered text def draw_button(img_default, img_hover, rect, pos, text, font, text_col): if rect.collidepoint(pos): screen.blit(img_hover, rect.topleft) else: screen.blit(img_default, rect.topleft) text_surf = font.render(text, True, text_col) text_rect = text_surf.get_rect(center=rect.center) screen.blit(text_surf, text_rect.topleft) # Function to display help overlay def display_help(): help_text_lines = [ "Level Editor Help", "________________________________________________________", "SAVE: Save the current level.", "LOAD: Load previous level data.", "UNDO: Undo the last action.", "REDO: Redo the last undone action.", "RESET: Reset the current level.", "HELP: Display this help screen.", "Click on tiles/sprites in the inventory to select them.", "Use the inventory to place tiles.", "Use the sprite inventory to place sprites.", "Click on the grid to place the selected tile/sprite.", "--------------------------------------------------------", "ESC" ] overlay = pygame.Surface((screen_width, screen_height)) overlay.set_alpha(200) overlay.fill((0, 0, 0)) screen.blit(overlay, (0, 0)) y_offset = 100 line_spacing = 40 for line in help_text_lines: draw_text(line, gameplay_font, white, 100, y_offset) y_offset += line_spacing # Function to draw the inventory with the new selection outline def draw_inventory(): for i, (item_img, item_id) in enumerate(inventory): row = i // 5 col = i % 5 x = 1221 + (col * 60) y = 350 + (row * 60) screen.blit(item_img, (x, y)) if selected_tile_id == item_id: screen.blit(selection_outline_img, (x - 3, y - 3)) # Function to draw the sprite inventory with the new selection outline def draw_sprite_inventory(): for i, (item_img, item_id) in enumerate(sprite_inventory): row = i // 5 col = i % 5 x = 1221 + (col * 60) y = 650 + (row * 60) screen.blit(item_img, (x + (tile_size - coin_size) // 2 if item_id == 105 else x, y + (tile_size - coin_size) // 2 if item_id == 105 else y)) if selected_tile_id == item_id: screen.blit(selection_outline_img, (x - 3, y - 3)) # Load click sound click_sound = pygame.mixer.Sound('Gilbert/Audio/Click.wav') # Function to play the click sound def play_click_sound(): click_sound.play() # Main game loop run = True clicked = False customizing_sprite = False selected_sprite = None selecting_waypoints = False waypoints = [] # Rectangles for customization menu speed_input_rect = pygame.Rect(1310, 750, 50, 30) delay_input_rect = pygame.Rect(1310, 800, 50, 30) waypoint_button_rect = pygame.Rect(1210, 850, 100, 30) back_button_rect = pygame.Rect(1210, 900, 100, 30) exit_waypoints_button_rect = pygame.Rect(1400, 850, 100, 30) pygame.display.update() # Inventory inventory = [ (crossout_img, 0), (block_img, 1), (block2_img, 2), (block3_img, 3), (block4_img, 4), (block5_img, 5), (block6_img, 6), (block7_img, 7), (block8_img, 8), (block9_img, 9), (block10_img, 10), (block11_img, 11), (block12_img, 12), (block13_img, 13), (block14_img, 14), (block15_img, 15) ] # Sprite Inventory sprite_inventory = [ (Acid_img, 101), (Acid2_img, 102), (zombie_img, 103), (moving_tile_img, 104), (gold_coin_img, 105), (key_img, 106), (door_inv_img, 107), (spawn_point_img, 108) ] selected_tile_id = 0 while run: clock.tick(60) screen.fill(white) screen.blit(background_img, (0, 0)) draw_grid() draw_world() # Draw editor menu background screen.blit(button_bg_img, (1201, 0)) draw_text("MENU", font, white, 1210, 20) pos = pygame.mouse.get_pos() # Draw buttons with hover effect save_button_rect = pygame.Rect(1221, 80, button_width, button_height) draw_button(silver_button_flipped_img, gold_button_flipped_img, save_button_rect, pos, "SAVE", font, white) load_button_rect = pygame.Rect(1372, 80, button_width, button_height) draw_button(silver_button_flipped_img, gold_button_flipped_img, load_button_rect, pos, "LOAD", font, white) undo_button_rect = pygame.Rect(1221, 150, button_width, button_height) draw_button(silver_middle_button_img, gold_middle_button_img, undo_button_rect, pos, "UNDO", font, white) redo_button_rect = pygame.Rect(1372, 150, button_width, button_height) draw_button(silver_middle_button_img, gold_middle_button_img, redo_button_rect, pos, "REDO", font, white) reset_button_rect = pygame.Rect(1221, 220, reset_button_width, button_height) draw_button(silver_button_reset_img, gold_button_reset_img, reset_button_rect, pos, "RESET", font, white) help_button_rect = pygame.Rect(1372, 220, button_width, button_height) draw_button(silver_button_img, gold_button_img, help_button_rect, pos, "HELP", font, white) draw_text("INVENTORY", font, white, 1210, 300) # Draw inventory draw_inventory() draw_text("SPRITE INVENTORY", font, white, 1210, 600) # Draw sprite inventory draw_sprite_inventory() def draw_customization_menu(): if customizing_sprite: pygame.draw.rect(screen, black, (1201, 700, 329, 350)) draw_text("SPRITE CUSTOMIZATION MENU", font, white, 1210, 710) draw_text("Speed:", font, white, 1210, 750) pygame.draw.rect(screen, white, speed_input_rect) draw_text(speed_input + ("|" if active_input == "speed" else ""), font, black, speed_input_rect.x + 5, speed_input_rect.y + 5) draw_text("Delay:", font, white, 1210, 800) pygame.draw.rect(screen, white, delay_input_rect) draw_text(delay_input + ("|" if active_input == "delay" else ""), font, black, delay_input_rect.x + 5, delay_input_rect.y + 5) draw_text("Waypoints", font, white, 1210, 850) draw_text("Back", font, white, 1210, 900) if selecting_waypoints: draw_text("Exit", font, white, 1400, 850) # Draw waypoints if selecting_waypoints: overlay = pygame.Surface((editor_width, screen_height)) overlay.set_alpha(100) overlay.fill((128, 128, 128)) screen.blit(overlay, (0, 0)) for wp in waypoints: pygame.draw.circle(screen, red, (wp[1] * tile_size + tile_size // 2, wp[0] * tile_size + tile_size // 2), 5) if waypoints: for i in range(len(waypoints) - 1): pygame.draw.line(screen, red, (waypoints[i][1] * tile_size + tile_size // 2, waypoints[i][0] * tile_size + tile_size // 2), (waypoints[i+1][1] * tile_size + tile_size // 2, waypoints[i+1][0] * tile_size + tile_size // 2), 2) pygame.draw.line(screen, red, (waypoints[-1][1] * tile_size + tile_size // 2, waypoints[-1][0] * tile_size + tile_size // 2), (selected_sprite[1] * tile_size + tile_size // 2, selected_sprite[0] * tile_size + tile_size // 2), 2) # Draw customization menu draw_customization_menu() # Event handler for event in pygame.event.get(): if event.type == pygame.QUIT: run = False if event.type == pygame.MOUSEBUTTONDOWN: pos = pygame.mouse.get_pos() if event.button == 1: # Left click if customizing_sprite: if waypoint_button_rect.collidepoint(pos): selecting_waypoints = True elif back_button_rect.collidepoint(pos): customizing_sprite = False selecting_waypoints = False if selected_sprite: sprite_data[selected_sprite] = { "speed": speed_input, "delay": delay_input, "waypoints": waypoints } selected_sprite = None elif exit_waypoints_button_rect.collidepoint(pos): selecting_waypoints = False elif speed_input_rect.collidepoint(pos): active_input = "speed" elif delay_input_rect.collidepoint(pos): active_input = "delay" else: active_input = None else: if save_button_rect.collidepoint(pos): play_click_sound() handle_button_click("save") elif load_button_rect.collidepoint(pos): play_click_sound() handle_button_click("load") elif undo_button_rect.collidepoint(pos): play_click_sound() handle_undo() elif redo_button_rect.collidepoint(pos): play_click_sound() handle_redo() elif reset_button_rect.collidepoint(pos): play_click_sound() handle_reset() elif help_button_rect.collidepoint(pos): play_click_sound() show_help = True else: add_to_history() # Record the state before any changes are made for i, (item_img, item_id) in enumerate(inventory): row = i // 5 col = i % 5 x = 1221 + (col * 60) y = 350 + (row * 60) item_rect = pygame.Rect(x, y, tile_size, tile_size) if item_rect.collidepoint(pos): selected_tile_id = item_id break else: for i, (item_img, item_id) in enumerate(sprite_inventory): row = i // 5 col = i % 5 x = 1221 + (col * 60) y = 650 + (row * 60) item_rect = pygame.Rect(x, y, tile_size, tile_size) if item_rect.collidepoint(pos): selected_tile_id = item_id break else: for row in range(rows): for col in range(cols): if pygame.Rect(col * tile_size, row * tile_size, tile_size, tile_size).collidepoint(pos): if selected_tile_id == 108: # If placing a new spawn point, remove the old one first spawn_point = (row, col) else: world_data[row][col] = selected_tile_id if selected_tile_id in [103, 104]: customizing_sprite = True selected_sprite = (row, col) speed_input = sprite_data.get(selected_sprite, {}).get("speed", "") delay_input = sprite_data.get(selected_sprite, {}).get("delay", "") waypoints = sprite_data.get(selected_sprite, {}).get("waypoints", []) break elif event.button == 3: # Right click for row in range(rows): for col in range(cols): if pygame.Rect(col * tile_size, row * tile_size, tile_size, tile_size).collidepoint(pos): if world_data[row][col] in [103, 104]: # Right-clicked on a customizable sprite customizing_sprite = True selected_sprite = (row, col) speed_input = sprite_data.get(selected_sprite, {}).get("speed", "") delay_input = sprite_data.get(selected_sprite, {}).get("delay", "") waypoints = sprite_data.get(selected_sprite, {}).get("waypoints", []) break if event.type == pygame.MOUSEBUTTONUP and event.button == 1: clicked = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_u: handle_undo() if event.key == pygame.K_r: handle_redo() if event.key == pygame.K_e: handle_reset() if event.key == pygame.K_h: show_help = not show_help if event.key == pygame.K_ESCAPE and show_help: show_help = False if selecting_waypoints: if event.key == pygame.K_ESCAPE: if waypoints: waypoints.pop() if event.key == pygame.K_RETURN: selecting_waypoints = False if active_input is not None: if event.key == pygame.K_BACKSPACE: if active_input == "speed": speed_input = speed_input[:-1] elif active_input == "delay": delay_input = delay_input[:-1] elif event.key == pygame.K_RETURN: active_input = None elif event.unicode.isdigit(): if active_input == "speed": speed_input += event.unicode elif active_input == "delay": delay_input += event.unicode if pygame.mouse.get_pressed()[0]: pos = pygame.mouse.get_pos() if selecting_waypoints: for row in range(rows): for col in range(cols): if pygame.Rect(col * tile_size, row * tile_size, tile_size, tile_size).collidepoint(pos): if (row, col) not in waypoints: waypoints.append((row, col)) break else: for row in range(rows): for col in range(cols): if pygame.Rect(col * tile_size, row * tile_size, tile_size, tile_size).collidepoint(pos): if selected_tile_id == 108: spawn_point = (row, col) else: world_data[row][col] = selected_tile_id if show_help: display_help() pygame.display.update() pygame.quit()