import time import tkinter as tk import glob import os import json import Units from Effects.order_click import OrderClick from tkinter import Menu from collections import deque import Units.hound import Units.zombie class IsometricGame: def __init__(self, width, height, data): self.width = width self.height = height self.view_offset_x = 800 self.view_offset_y = 0 self.cell_width = 134 self.cell_height = 68 self.cell_selected = (0, 0) self.last_trigger_time = 0 self.min_interval = 0.5 # Minimum interval in seconds self.effects = [] self.editor_mode = False self.input_lock = False self.window = tk.Tk() files = sorted(glob.glob("Tiles/**/*.png", recursive=True)) self.tiles = {os.path.splitext(os.path.basename(file))[0]: tk.PhotoImage(file=file) for file in files} # self.battlefield = [[Cell(walkable = True, tile=self.tiles["landscapeTiles_067"] ) for x in range(self.width)] for y in range(self.height)] # use data to create the battlefield self.battlefield = [[Cell(walkable = not data[y][x], tile=self.tiles["landscapeTiles_067"] if not data[y][x] else self.tiles["landscapeTiles_066"] ) for x in range(self.width)] for y in range(self.height)] self.window.title("Campo di Battaglia RTS Isometrico") self.canvas = tk.Canvas(self.window, width=1920, height=1080, bg='black') self.canvas.pack() self.menu = Menu(self.canvas, tearoff=0) self.window.bind('', self.on_key_press) self.canvas.bind('', self.on_canvas_click) self.canvas.bind('', self.on_canvas_rclick) self.canvas.bind('', self.calculate_coordinates) self.knight = Units.zombie.Unit(self, position=(1, 1)) self.draw_battlefield() def calculate_coordinates(self, event): if self.input_lock: return # Get the mouse coordinates mouse_x = event.x mouse_y = event.y iso_x, iso_y = self.inv_iso_transform(mouse_x, mouse_y) self.cell_selected = (iso_x, iso_y) def get_direction(self, initial_coord, surrounding_coord): # Calcola la differenza tra le coordinate delta_x = surrounding_coord[0] - initial_coord[0] delta_y = surrounding_coord[1] - initial_coord[1] # Determina la direzione basandosi sulla differenza if delta_x == -1 and delta_y == 1: return 2 # Sud-Ovest elif delta_x == -1 and delta_y == 0: return 3 # Ovest elif delta_x == -1 and delta_y == -1: return 4 # Nord-Ovest elif delta_x == 0 and delta_y == -1: return 5 # Nord elif delta_x == 1 and delta_y == -1: return 6 # Nord-Est elif delta_x == 1 and delta_y == 0: return 7 # Est elif delta_x == 1 and delta_y == 1: return 8 # Sud-Est elif delta_x == 0 and delta_y == 1: return 1 # Sud else: return 1 def find_neighbors(self, coordinates): """ Find and return the cells adjacent to the cell at position (coordinates). Adjacent cells are those directly above, below, left, right, and all four diagonally adjacent cells. """ neighbors = [] x_coord, y_coord = coordinates # Check all eight possible directions for delta_x in [-1, 0, 1]: for delta_y in [-1, 0, 1]: new_x, new_y = x_coord + delta_x, y_coord + delta_y # Skip the cell itself if delta_x == 0 and delta_y == 0: continue # Check if the new coordinates are within the battlefield if 0 <= new_x < self.width and 0 <= new_y < self.height: neighbors.append((new_x, new_y)) return neighbors def get_closest_neighbor(self, neighbors_list, target_position): """ Find and return the neighbor cell in the neighbors_list closest to the target position. """ # If there are no neighbors, return None if not neighbors_list: return None # Calculate the distance between the target position and each neighbor distances = [abs(((neighbor[0] - target_position[0])**2 + (neighbor[1] - target_position[1])**2)**0.5) for neighbor in neighbors_list] # Find the index of the smallest distance min_distance_index = distances.index(min(distances)) # Return the neighbor corresponding to the smallest distance return neighbors_list[min_distance_index] def iso_transform(self, x, y): screen_x = (x - y) * self.cell_width / 2 + self.view_offset_x screen_y = (x + y) * self.cell_height / 2 + self.view_offset_y return screen_x, screen_y def inv_iso_transform(self, screen_x, screen_y): x = ((screen_x - self.view_offset_x) / (self.cell_width / 2) + (screen_y - self.view_offset_y) / (self.cell_height / 2)) / 2 y = ((screen_y - self.view_offset_y) / (self.cell_height / 2) - (screen_x - self.view_offset_x) / (self.cell_width / 2)) / 2 return int(x), int(y) def get_distance(self, position1, position2): x1, y1 = position1 x2, y2 = position2 return abs(((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5) def get_distance_components(self, position1, position2): x1, y1 = position1 x2, y2 = position2 return abs(x2 - x1), abs(y2 - y1) #function to check if a cell is near a wall def near_wall(self, position): x, y = position neighbors = self.find_neighbors(position) for neighbor in neighbors: if not self.battlefield[neighbor[1]][neighbor[0]].walkable: return True def min_steps_to_target(self, start, target): grid = [[0 for _ in range(self.width)] for _ in range(self.height)] rows, cols = len(grid), len(grid[0]) dx = [0, 1, 0, -1] dy = [1, 0, -1, 0] queue = deque([start]) visited = set([start]) steps = 0 while queue: for _ in range(len(queue)): x, y = queue.popleft() if (x, y) == target: return steps for i in range(4): nx, ny = x + dx[i], y + dy[i] if 0 <= nx < rows and 0 <= ny < cols and (nx, ny) not in visited: queue.append((nx, ny)) visited.add((nx, ny)) steps += 1 return -1 def draw_battlefield(self): self.canvas.delete("cell") # Pulisce le celle precedenti prima di ridisegnare self.canvas.delete("cell-label") for y, row in enumerate(self.battlefield): for x, cell in enumerate(row): screen_x, screen_y = self.iso_transform(x, y) offset = (99 - cell.tile.height()) / 2 self.canvas.create_image(screen_x, screen_y+45+offset, image=cell.tile, tags="cell") if self.editor_mode: self.canvas.create_text(screen_x, screen_y+45, text=str(cell.elevation), tags="cell_label") self.canvas.create_text(screen_x+10, screen_y+45, text=str(int(cell.walkable)), tags="cell_label", fill="white" if cell.walkable else "red") self.canvas.create_text(screen_x+10, screen_y+35, text=str(int(cell.tile.height())), tags="cell_label", fill="white" if cell.walkable else "red") def draw_units(self): self.canvas.delete("unit") # Pulisce le unità precedenti prima di ridisegnare gif, screen_x, screen_y = self.knight.ai() # Calcola la posizione e l'animazione del cavaliere self.canvas.create_image(screen_x, screen_y+35, image=gif.next_frame(), tags="unit") # Disegna il cavaliere def draw_effects(self): # Pulisce gli effetti precedenti prima di ridisegnare self.canvas.delete("effect") self.canvas.delete("sel-cell") # Disegna gli effetti for effect in self.effects: if not effect.next_frame(self.canvas): self.effects.remove(effect) # Disegna il rettangolo di selezione cell_width = self.cell_width - 8 cell_height = self.cell_height - 4 x, y = self.iso_transform(*self.cell_selected) def draw_line(self, start, end): self.canvas.create_line(start[0], start[1], end[0], end[1], fill='red', tags="sel-cell", width=2) draw_line(self, (x, y), (x+cell_width/2, y+cell_height/2)) draw_line(self, (x+cell_width/2, y+cell_height/2), (x, y+cell_height)) draw_line(self, (x, y+cell_height), (x-cell_width/2, y+cell_height/2)) draw_line(self, (x-cell_width/2, y+cell_height/2), (x, y)) if self.editor_mode: self.canvas.create_text(55,15, text="EDITOR MODE", tags="effect", fill="blue") def on_key_press(self, event): current_time = time.time() step = 30 # Distanza dello spostamento ad ogni pressione dei tasti if current_time - self.last_trigger_time >= self.min_interval: # Check if the last trigger time is greater than the minimum interval if event.keysym == 'Up': self.view_offset_y += step elif event.keysym == 'Down': self.view_offset_y -= step elif event.keysym == 'Left': self.view_offset_x += step elif event.keysym == 'Right': self.view_offset_x -= step elif event.keysym == 'q': cell_x, cell_y = self.inv_iso_transform(event.x, event.y) self.battlefield[cell_y][cell_x].elevation -= 1 elif event.keysym == 'e': cell_x, cell_y = self.inv_iso_transform(event.x, event.y) self.battlefield[cell_y][cell_x].elevation += 1 elif event.keysym == '1': tiles_list = self.tiles.keys() self.input_lock = True for key, tile in enumerate(tiles_list): self.menu.add_command(label=tile, command=lambda vars=(tile, key) : self.set_tile(*vars)) self.menu.post(event.x_root, event.y_root) elif event.keysym == 'w': # Se si preme il tasto 'w' si cambia la walkability della cella cell_x, cell_y = self.inv_iso_transform(event.x, event.y) self.battlefield[cell_y][cell_x].walkable ^= True elif event.keysym == 'a': # Se si preme il tasto 'a' si cambia l'offset della cella cell_x, cell_y = self.inv_iso_transform(event.x, event.y) self.battlefield[cell_y][cell_x].offset += 9 elif event.keysym == 'd': # Se si preme il tasto 'd' si cambia l'offset della cella cell_x, cell_y = self.inv_iso_transform(event.x, event.y) self.battlefield[cell_y][cell_x].offset -= 11 elif event.keysym == '5': # Handle '5' key press pass elif event.keysym == 'Tab': # Handle tab key press self.editor_mode ^= True self.draw_battlefield() self.draw_units() self.draw_effects() self.last_trigger_time = current_time def on_canvas_click(self, event): # calculate the clicked cell's coordinates cell_x, cell_y = self.inv_iso_transform(event.x, event.y) self.battlefield[cell_y][cell_x].walkable ^= True self.battlefield[cell_y][cell_x].tile = self.tiles["landscapeTiles_067"] if self.battlefield[cell_y][cell_x].walkable else self.tiles["landscapeTiles_066"] self.draw_battlefield() self.draw_units() def set_tile(self, name, pos): # Handle option 1 selection self.input_lock = False print(f'You selected option {name}') cell_x, cell_y = self.cell_selected if 0 <= cell_x < self.width and 0 <= cell_y < self.height: self.battlefield[cell_y][cell_x].tile = self.tiles[name] self.draw_battlefield() def on_canvas_rclick(self, event): # calculate the clicked cell's coordinates cell_x, cell_y = self.inv_iso_transform(event.x, event.y) print(f'Cell clicked: ({cell_x}, {cell_y})') x, y = self.iso_transform(cell_x, cell_y) self.effects.append(OrderClick((x, y), color="light blue")) if self.knight.target == (cell_x, cell_y): return if 0 <= cell_x < self.width and 0 <= cell_y < self.height: self.knight.move_to((cell_x,cell_y)) def run(self): self.draw_battlefield() self.update() self.window.mainloop() def update(self): #self.draw_battlefield() self.draw_units() self.draw_effects() self.window.after(75, self.update) # Call again after 100 ms class Cell: def __init__(self, walkable=False, tile=None, elevation=0, offset=0): self.tile = tile self.walkable = walkable self.elevation = elevation if __name__ == "__main__": with open('maze.json', 'r') as file: data = json.load(file) # Creare una matrice 10x10 piena di False room = [[True for _ in range(10)] for _ in range(10)] # Cambiare i valori interni della matrice in True for i in range(1, 9): for j in range(1, 8): room[i][j] = False # Creare un'apertura di un'unità verso l'esterno room[0][4] = True # Cambia questo valore per spostare l'apertura data =room # data is a boolean matrix, find dmensions width = len(data[0]) height = len(data) game = IsometricGame(width=width, height=height, data=data) game.run()