itrpg/scripts/isometric_map_data.gd

398 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
# 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
# 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
# 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