401 lines
14 KiB
GDScript
401 lines
14 KiB
GDScript
extends Resource
|
|
class_name IsometricMapData
|
|
|
|
var GRID_SIZE_WIDTH = 50 # play area size x
|
|
var GRID_SIZE_LENGTH = 30 # play area size y
|
|
var GRID_SIZE_HEIGHT = 30 # play area size z
|
|
var OFFSET = 0
|
|
var MAX_ARRAY_SIZE = 0
|
|
|
|
# The actual map data array
|
|
var map_data = []
|
|
|
|
func _init(width = 50, length = 30, height = 30, offset = 0):
|
|
GRID_SIZE_WIDTH = width
|
|
GRID_SIZE_LENGTH = length
|
|
GRID_SIZE_HEIGHT = height
|
|
OFFSET = offset
|
|
_set_max_array_size()
|
|
initialize_map_data()
|
|
|
|
func _set_max_array_size():
|
|
MAX_ARRAY_SIZE = 0
|
|
|
|
if MAX_ARRAY_SIZE < GRID_SIZE_WIDTH:
|
|
MAX_ARRAY_SIZE = GRID_SIZE_WIDTH
|
|
if MAX_ARRAY_SIZE < GRID_SIZE_LENGTH:
|
|
MAX_ARRAY_SIZE = GRID_SIZE_LENGTH
|
|
if MAX_ARRAY_SIZE < GRID_SIZE_HEIGHT:
|
|
MAX_ARRAY_SIZE = GRID_SIZE_HEIGHT
|
|
|
|
|
|
func is_array_tile_valid(array_pos: Vector3i):
|
|
if 0 < array_pos.x and array_pos.x < MAX_ARRAY_SIZE and 0 < array_pos.y and array_pos.y < MAX_ARRAY_SIZE and 0 < array_pos.z and array_pos.z < MAX_ARRAY_SIZE:
|
|
return true
|
|
return false
|
|
|
|
func get_array_neighbors(array_pos: Vector3i, movement_range: int) -> Array:
|
|
var neighbors = []
|
|
var visited = {array_pos: true} # Using a dictionary for faster lookups
|
|
var directions = [
|
|
Vector3i(0, 0, 1), # up
|
|
Vector3i(0, 0, -1), # down
|
|
Vector3i(0, 1, 0), # left
|
|
Vector3i(0, -1, 0), # right
|
|
Vector3i(1, 0, 0), # forward
|
|
Vector3i(-1, 0, 0), # backward
|
|
]
|
|
|
|
# Use a breadth-first search approach
|
|
var current_level = [array_pos]
|
|
|
|
for distance in range(1, movement_range + 1):
|
|
var next_level = []
|
|
|
|
for current_pos in current_level:
|
|
for dir in directions:
|
|
var neighbor_pos = current_pos + dir
|
|
|
|
# Check if we've already processed this position or if it's invalid
|
|
if neighbor_pos in visited or not is_array_tile_valid(neighbor_pos):
|
|
continue
|
|
|
|
# Mark as visited and add to results
|
|
visited[neighbor_pos] = true
|
|
neighbors.append(neighbor_pos)
|
|
next_level.append(neighbor_pos)
|
|
|
|
# Move to the next level of neighbors
|
|
current_level = next_level
|
|
|
|
# If no more positions to check at this level, we can stop early
|
|
if current_level.is_empty():
|
|
break
|
|
|
|
return neighbors
|
|
|
|
|
|
func initialize_map_data():
|
|
# Initialize empty map structure
|
|
map_data = []
|
|
|
|
for x in MAX_ARRAY_SIZE:
|
|
var y_array = []
|
|
for y in MAX_ARRAY_SIZE:
|
|
var z_array = []
|
|
for z in MAX_ARRAY_SIZE:
|
|
z_array.append(null)
|
|
y_array.append(z_array)
|
|
map_data.append(y_array)
|
|
|
|
# Get a tile at specific coordinates, return null if out of bounds
|
|
func get_tile(x, y, z):
|
|
if x < 0 or x >= MAX_ARRAY_SIZE or y < 0 or y >= MAX_ARRAY_SIZE or z < 0 or z >= MAX_ARRAY_SIZE:
|
|
return null
|
|
return map_data[x][y][z]
|
|
|
|
# Set a tile at specific coordinates
|
|
func set_tile(x, y, z, tile_data):
|
|
if x < 0 or x >= MAX_ARRAY_SIZE or y < 0 or y >= MAX_ARRAY_SIZE or z < 0 or z >= MAX_ARRAY_SIZE:
|
|
return
|
|
map_data[x][y][z] = tile_data
|
|
|
|
# Generate the isometric coordinates for a tile
|
|
func generate_iso_coords(x, y, z):
|
|
var coord_x = x + (-1 * z) + OFFSET
|
|
var coord_y = y + (-1 * z) - OFFSET
|
|
return Vector2i(coord_x, coord_y)
|
|
|
|
# Create a map with default configuration
|
|
func create_default_map():
|
|
# First initialize all tiles with null values
|
|
for z in GRID_SIZE_HEIGHT:
|
|
for y in GRID_SIZE_LENGTH:
|
|
for x in GRID_SIZE_WIDTH:
|
|
var iso_coords = generate_iso_coords(x, y, z)
|
|
|
|
var tile_data = IsometricMapSystem.create_tile_data(
|
|
iso_coords.x,
|
|
iso_coords.y,
|
|
z,
|
|
# Give all tiles valid default atlas positions to avoid null errors
|
|
IsometricMapSystem.BLUE_ISOMETRICTILE_ATLAS_POSITION,
|
|
IsometricMapSystem.WHITE_ISOMETRICTILE_ATLAS_POSITION
|
|
)
|
|
# By default, tiles are not visible
|
|
tile_data["visibility"] = false
|
|
map_data[x][y][z] = tile_data
|
|
|
|
# Set ground floor visible with blue tiles
|
|
for y in GRID_SIZE_LENGTH:
|
|
for x in GRID_SIZE_WIDTH:
|
|
var tile = map_data[x][y][0]
|
|
tile["atlas_base_position"] = IsometricMapSystem.BLUE_ISOMETRICTILE_ATLAS_POSITION
|
|
tile["atlas_highlight_position"] = IsometricMapSystem.WHITE_ISOMETRICTILE_ATLAS_POSITION
|
|
tile["visibility"] = true
|
|
|
|
# Add a vertical column of red tiles for demonstration
|
|
# Make sure coords are within bounds
|
|
if 5 < GRID_SIZE_WIDTH and 5 < GRID_SIZE_LENGTH:
|
|
for i in range(1, min(10, GRID_SIZE_HEIGHT)):
|
|
var tile = map_data[5][5][i]
|
|
if tile != null:
|
|
tile["atlas_base_position"] = IsometricMapSystem.RED_ISOMETRICTILE_ATLAS_POSITION
|
|
tile["atlas_highlight_position"] = IsometricMapSystem.WHITE_ISOMETRICTILE_ATLAS_POSITION
|
|
tile["visibility"] = true
|
|
IsometricMapSystem.debug_print("Created red tile at 5,5," + str(i))
|
|
#var tile = map_data[10][10][1]
|
|
#if tile != null:
|
|
# tile["atlas_base_position"] = IsometricMapSystem.BLACK_ISOMETRICTILE_ATLAS_POSITION
|
|
# tile["atlas_highlight_position"] = IsometricMapSystem.GREEN_ISOMETRICTILE_ATLAS_POSITION
|
|
# tile["visibility"] = true
|
|
# IsometricMapSystem.debug_print("Created black tile at 10,10," + str(1))
|
|
|
|
# Rotate map around X axis
|
|
func rotate_around_x_axis(rotation_steps = 1):
|
|
rotation_steps = rotation_steps % 4
|
|
|
|
if rotation_steps == 0:
|
|
return
|
|
|
|
var temp_map = []
|
|
# Initialize temp_map with same structure
|
|
for x in MAX_ARRAY_SIZE:
|
|
var y_array = []
|
|
for y in MAX_ARRAY_SIZE:
|
|
var z_array = []
|
|
for z in MAX_ARRAY_SIZE:
|
|
z_array.append(null)
|
|
y_array.append(z_array)
|
|
temp_map.append(y_array)
|
|
|
|
# Create a new map with adjusted dimensions
|
|
var new_height = int(GRID_SIZE_LENGTH) if rotation_steps % 2 == 1 else int(GRID_SIZE_HEIGHT)
|
|
var new_length = int(GRID_SIZE_HEIGHT) if rotation_steps % 2 == 1 else int(GRID_SIZE_LENGTH)
|
|
|
|
# Store the original dimensions
|
|
var original_height = GRID_SIZE_HEIGHT
|
|
var original_length = GRID_SIZE_LENGTH
|
|
|
|
# Temporarily adjust grid dimensions for coordinate calculation
|
|
GRID_SIZE_HEIGHT = new_height
|
|
GRID_SIZE_LENGTH = new_length
|
|
|
|
for x in GRID_SIZE_WIDTH:
|
|
for y in original_length:
|
|
for z in original_height:
|
|
if map_data[x][y][z] == null:
|
|
continue
|
|
|
|
var new_y = 0
|
|
var new_z = 0
|
|
|
|
match rotation_steps:
|
|
1: # 90 degrees
|
|
new_y = z
|
|
new_z = original_length - 1 - y
|
|
2: # 180 degrees
|
|
new_y = original_length - 1 - y
|
|
new_z = original_height - 1 - z
|
|
3: # 270 degrees
|
|
new_y = original_height - 1 - z
|
|
new_z = y
|
|
|
|
if new_y >= 0 and new_y < new_length and new_z >= 0 and new_z < new_height:
|
|
temp_map[x][new_y][new_z] = map_data[x][y][z].duplicate()
|
|
|
|
var new_coord_x = x + (-1 * new_z) + OFFSET
|
|
var new_coord_y = new_y + (-1 * new_z) - OFFSET
|
|
|
|
temp_map[x][new_y][new_z]["x"] = new_coord_x
|
|
temp_map[x][new_y][new_z]["y"] = new_coord_y
|
|
temp_map[x][new_y][new_z]["z"] = new_z
|
|
temp_map[x][new_y][new_z]["highlighted"] = false
|
|
|
|
# Update the grid dimensions
|
|
GRID_SIZE_HEIGHT = new_height
|
|
GRID_SIZE_LENGTH = new_length
|
|
|
|
map_data = temp_map
|
|
|
|
# Rotate map around Y axis
|
|
func rotate_around_y_axis(rotation_steps = 1):
|
|
rotation_steps = rotation_steps % 4
|
|
|
|
if rotation_steps == 0:
|
|
return
|
|
|
|
var temp_map = []
|
|
# Initialize temp_map with same structure
|
|
for x in MAX_ARRAY_SIZE:
|
|
var y_array = []
|
|
for y in MAX_ARRAY_SIZE:
|
|
var z_array = []
|
|
for z in MAX_ARRAY_SIZE:
|
|
z_array.append(null)
|
|
y_array.append(z_array)
|
|
temp_map.append(y_array)
|
|
|
|
# Create a new map with adjusted dimensions
|
|
var new_width = int(GRID_SIZE_HEIGHT) if rotation_steps % 2 == 1 else int(GRID_SIZE_WIDTH)
|
|
var new_height = int(GRID_SIZE_WIDTH) if rotation_steps % 2 == 1 else int(GRID_SIZE_HEIGHT)
|
|
|
|
# Store the original dimensions
|
|
var original_width = GRID_SIZE_WIDTH
|
|
var original_height = GRID_SIZE_HEIGHT
|
|
|
|
# Temporarily adjust grid dimensions for coordinate calculation
|
|
GRID_SIZE_WIDTH = new_width
|
|
GRID_SIZE_HEIGHT = new_height
|
|
|
|
for x in original_width:
|
|
for y in GRID_SIZE_LENGTH:
|
|
for z in original_height:
|
|
if map_data[x][y][z] == null:
|
|
continue
|
|
|
|
var new_x = 0
|
|
var new_z = 0
|
|
|
|
match rotation_steps:
|
|
1: # 90 degrees clockwise around Y axis
|
|
new_x = original_height - 1 - z
|
|
new_z = x
|
|
2: # 180 degrees
|
|
new_x = original_width - 1 - x
|
|
new_z = original_height - 1 - z
|
|
3: # 270 degrees
|
|
new_x = z
|
|
new_z = original_width - 1 - x
|
|
|
|
if new_x >= 0 and new_x < new_width and new_z >= 0 and new_z < new_height:
|
|
temp_map[new_x][y][new_z] = map_data[x][y][z].duplicate()
|
|
|
|
var new_coord_x = new_x + (-1 * new_z) + OFFSET
|
|
var new_coord_y = y + (-1 * new_z) - OFFSET
|
|
|
|
temp_map[new_x][y][new_z]["x"] = new_coord_x
|
|
temp_map[new_x][y][new_z]["y"] = new_coord_y
|
|
temp_map[new_x][y][new_z]["z"] = new_z
|
|
temp_map[new_x][y][new_z]["highlighted"] = false
|
|
|
|
# Update the grid dimensions
|
|
GRID_SIZE_WIDTH = new_width
|
|
GRID_SIZE_HEIGHT = new_height
|
|
|
|
map_data = temp_map
|
|
|
|
# Rotate map around Z axis
|
|
func rotate_around_z_axis(rotation_steps = 1):
|
|
rotation_steps = rotation_steps % 4
|
|
|
|
if rotation_steps == 0:
|
|
return
|
|
|
|
var temp_map = []
|
|
# Initialize temp_map with same structure
|
|
for x in MAX_ARRAY_SIZE:
|
|
var y_array = []
|
|
for y in MAX_ARRAY_SIZE:
|
|
var z_array = []
|
|
for z in MAX_ARRAY_SIZE:
|
|
z_array.append(null)
|
|
y_array.append(z_array)
|
|
temp_map.append(y_array)
|
|
|
|
# Create a new map with adjusted dimensions based on rotation
|
|
var new_width = int(GRID_SIZE_LENGTH) if rotation_steps % 2 == 1 else int(GRID_SIZE_WIDTH)
|
|
var new_length = int(GRID_SIZE_WIDTH) if rotation_steps % 2 == 1 else int(GRID_SIZE_LENGTH)
|
|
|
|
# Store the original dimensions
|
|
var original_width = GRID_SIZE_WIDTH
|
|
var original_length = GRID_SIZE_LENGTH
|
|
|
|
# Temporarily adjust grid dimensions for coordinate calculation
|
|
GRID_SIZE_WIDTH = new_width
|
|
GRID_SIZE_LENGTH = new_length
|
|
|
|
for x in original_width:
|
|
for y in original_length:
|
|
for z in GRID_SIZE_HEIGHT:
|
|
if map_data[x][y][z] == null:
|
|
continue
|
|
|
|
var new_x = 0
|
|
var new_y = 0
|
|
|
|
match rotation_steps:
|
|
1: # 90 degrees
|
|
new_x = y
|
|
new_y = original_width - 1 - x
|
|
2: # 180 degrees
|
|
new_x = original_width - 1 - x
|
|
new_y = original_length - 1 - y
|
|
3: # 270 degrees
|
|
new_x = original_length - 1 - y
|
|
new_y = x
|
|
|
|
if new_x >= 0 and new_x < new_width and new_y >= 0 and new_y < new_length:
|
|
temp_map[new_x][new_y][z] = map_data[x][y][z].duplicate()
|
|
|
|
var new_coord_x = new_x + (-1 * z) + OFFSET
|
|
var new_coord_y = new_y + (-1 * z) - OFFSET
|
|
|
|
temp_map[new_x][new_y][z]["x"] = new_coord_x
|
|
temp_map[new_x][new_y][z]["y"] = new_coord_y
|
|
temp_map[new_x][new_y][z]["z"] = z
|
|
temp_map[new_x][new_y][z]["highlighted"] = false
|
|
|
|
# Update the grid dimensions
|
|
GRID_SIZE_WIDTH = new_width
|
|
GRID_SIZE_LENGTH = new_length
|
|
|
|
map_data = temp_map
|
|
|
|
# Find a tile containing a specific unit
|
|
func find_unit_tile(unit_type = null):
|
|
for z in GRID_SIZE_HEIGHT:
|
|
for x in GRID_SIZE_WIDTH:
|
|
for y in GRID_SIZE_LENGTH:
|
|
var tile = map_data[x][y][z]
|
|
if tile and tile["unit"] != null:
|
|
if unit_type == null or tile["unit_type"] == unit_type:
|
|
return {"tile": tile, "x": x, "y": y, "z": z}
|
|
return null
|
|
|
|
# Move a unit from one position to another
|
|
func move_unit(from_x, from_y, from_z, to_x, to_y, to_z):
|
|
# Get the source and destination tiles safely
|
|
var from_tile = get_tile(from_x, from_y, from_z)
|
|
var to_tile = get_tile(to_x, to_y, to_z)
|
|
|
|
# Debug information
|
|
print("Moving unit from [", from_x, ",", from_y, ",", from_z, "] to [", to_x, ",", to_y, ",", to_z, "]")
|
|
|
|
# Validate that both tiles exist and source has a unit
|
|
if from_tile == null:
|
|
print("Source tile doesn't exist")
|
|
return false
|
|
|
|
if to_tile == null:
|
|
print("Destination tile doesn't exist")
|
|
return false
|
|
|
|
if from_tile["unit"] == null:
|
|
print("No unit at source tile")
|
|
return false
|
|
|
|
# Valid move - transfer the unit
|
|
to_tile["unit"] = from_tile["unit"]
|
|
to_tile["unit_type"] = from_tile["unit_type"]
|
|
to_tile["visibility"] = true
|
|
|
|
# Clear the source tile
|
|
from_tile["unit"] = null
|
|
from_tile["unit_type"] = null
|
|
# Keep source tile visible
|
|
from_tile["visibility"] = true
|
|
|
|
print("Unit moved successfully")
|
|
return true
|