You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
343 lines
14 KiB
343 lines
14 KiB
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('<KeyRelease>', self.on_key_press) |
|
self.canvas.bind('<Button-1>', self.on_canvas_click) |
|
self.canvas.bind('<Button-3>', self.on_canvas_rclick) |
|
self.canvas.bind('<Motion>', 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()
|
|
|