tesitng maps
This commit is contained in:
parent
025a690b48
commit
e1178d5f5c
12 changed files with 1573 additions and 3 deletions
|
|
@ -58,8 +58,8 @@ func _on_deck_button_pressed():
|
||||||
|
|
||||||
func _on_map_button_pressed():
|
func _on_map_button_pressed():
|
||||||
print("Map button pressed")
|
print("Map button pressed")
|
||||||
# Emit signal with null options
|
|
||||||
emit_signal("map_open_requested", null)
|
emit_signal("map_open_requested", null)
|
||||||
|
visible = false
|
||||||
|
|
||||||
func _on_start_button_pressed():
|
func _on_start_button_pressed():
|
||||||
print("Start button pressed")
|
print("Start button pressed")
|
||||||
|
|
|
||||||
109
Systems/Game/Map/DotPatternGenerator.gd
Normal file
109
Systems/Game/Map/DotPatternGenerator.gd
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
extends Control
|
||||||
|
class_name DotPatternGenerator
|
||||||
|
# Dot pattern settings
|
||||||
|
const DOT_SPACING = 30
|
||||||
|
const DOT_SIZE = 2
|
||||||
|
const DOT_COLOR = Color(0.3, 0.3, 0.3, 0.5)
|
||||||
|
const PATH_DOT_COLOR = Color(0.4, 0.4, 0.6, 0.8)
|
||||||
|
const PATH_DOT_SIZE = 3
|
||||||
|
const HIGHLIGHT_RADIUS = 150
|
||||||
|
|
||||||
|
# For path highlights
|
||||||
|
var map_screen = null
|
||||||
|
var highlight_dots = false
|
||||||
|
|
||||||
|
func _init(highlight: bool = false):
|
||||||
|
highlight_dots = highlight
|
||||||
|
# Don't process input
|
||||||
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
# Apply the pattern
|
||||||
|
generate_dots()
|
||||||
|
|
||||||
|
# Find map screen if we're highlighting paths
|
||||||
|
if highlight_dots:
|
||||||
|
map_screen = find_map_screen()
|
||||||
|
|
||||||
|
func find_map_screen():
|
||||||
|
var parent = get_parent()
|
||||||
|
while parent != null:
|
||||||
|
if parent is MapScreen:
|
||||||
|
return parent
|
||||||
|
parent = parent.get_parent()
|
||||||
|
return null
|
||||||
|
|
||||||
|
func generate_dots():
|
||||||
|
# Clear existing dots
|
||||||
|
for child in get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
# Get the size of this control
|
||||||
|
var control_size = size
|
||||||
|
|
||||||
|
# Calculate how many dots we need
|
||||||
|
var cols = int(control_size.x / DOT_SPACING) + 1
|
||||||
|
var rows = int(control_size.y / DOT_SPACING) + 1
|
||||||
|
|
||||||
|
# Create dots
|
||||||
|
for i in range(rows):
|
||||||
|
for j in range(cols):
|
||||||
|
var dot_position = Vector2(j * DOT_SPACING, i * DOT_SPACING)
|
||||||
|
var on_path = false
|
||||||
|
|
||||||
|
# If highlighting, check if dot is near a path
|
||||||
|
if highlight_dots and map_screen != null:
|
||||||
|
on_path = is_near_path(dot_position)
|
||||||
|
|
||||||
|
# Create the dot with appropriate style
|
||||||
|
var dot = ColorRect.new()
|
||||||
|
dot.size = Vector2(DOT_SIZE, DOT_SIZE) if not on_path else Vector2(PATH_DOT_SIZE, PATH_DOT_SIZE)
|
||||||
|
dot.color = DOT_COLOR if not on_path else PATH_DOT_COLOR
|
||||||
|
dot.position = dot_position - (dot.size / 2)
|
||||||
|
add_child(dot)
|
||||||
|
|
||||||
|
func is_near_path(position: Vector2) -> bool:
|
||||||
|
# If no map screen, can't check paths
|
||||||
|
if map_screen == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Get connections from map screen
|
||||||
|
for line in map_screen.connection_lines:
|
||||||
|
if line.get_point_count() < 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check distance to line segment
|
||||||
|
var start = line.get_point_position(0)
|
||||||
|
var end = line.get_point_position(1)
|
||||||
|
|
||||||
|
# Adjust for potential offset in the map container
|
||||||
|
start += map_screen.map_container.position
|
||||||
|
end += map_screen.map_container.position
|
||||||
|
|
||||||
|
# Check if point is near the line
|
||||||
|
var distance = distance_to_line_segment(position, start, end)
|
||||||
|
if distance < HIGHLIGHT_RADIUS:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Calculate distance from point to line segment
|
||||||
|
func distance_to_line_segment(point: Vector2, line_start: Vector2, line_end: Vector2) -> float:
|
||||||
|
var line_vec = line_end - line_start
|
||||||
|
var point_vec = point - line_start
|
||||||
|
|
||||||
|
var line_length_squared = line_vec.length_squared()
|
||||||
|
if line_length_squared == 0:
|
||||||
|
return point_vec.length() # Line segment is a point
|
||||||
|
|
||||||
|
# Calculate projection of point onto line
|
||||||
|
var t = max(0, min(1, point_vec.dot(line_vec) / line_length_squared))
|
||||||
|
var projection = line_start + t * line_vec
|
||||||
|
|
||||||
|
# Return distance from point to projection
|
||||||
|
return (point - projection).length()
|
||||||
|
|
||||||
|
# Call this when the map changes to update dot highlights
|
||||||
|
func update_dots():
|
||||||
|
if highlight_dots and map_screen != null:
|
||||||
|
generate_dots()
|
||||||
1
Systems/Game/Map/DotPatternGenerator.gd.uid
Normal file
1
Systems/Game/Map/DotPatternGenerator.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://6cmhvsug8nbv
|
||||||
630
Systems/Game/Map/MapGenerator.gd
Normal file
630
Systems/Game/Map/MapGenerator.gd
Normal file
|
|
@ -0,0 +1,630 @@
|
||||||
|
extends RefCounted
|
||||||
|
class_name ChessMapGenerator
|
||||||
|
|
||||||
|
# Room type enum
|
||||||
|
enum RoomType {
|
||||||
|
STARTING,
|
||||||
|
NORMAL,
|
||||||
|
BOSS,
|
||||||
|
FINAL,
|
||||||
|
SHOP,
|
||||||
|
EVENT
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
var min_levels = 6
|
||||||
|
var max_levels = 12
|
||||||
|
var min_nodes_per_level = 2
|
||||||
|
var max_nodes_per_level = 4
|
||||||
|
var positions_per_level = 7 # How many horizontal positions are available (0-6)
|
||||||
|
var starting_elo = 1000
|
||||||
|
var final_elo = 2100
|
||||||
|
|
||||||
|
# Internal variables
|
||||||
|
var _rng = RandomNumberGenerator.new()
|
||||||
|
var _next_id = 0
|
||||||
|
|
||||||
|
func _init(seed_value = null):
|
||||||
|
# Set seed for reproducible maps if needed
|
||||||
|
if seed_value != null:
|
||||||
|
_rng.seed = seed_value
|
||||||
|
else:
|
||||||
|
_rng.randomize()
|
||||||
|
|
||||||
|
# Main function to generate the map
|
||||||
|
func generate_map():
|
||||||
|
var nodes = []
|
||||||
|
var connections = []
|
||||||
|
_next_id = 0
|
||||||
|
|
||||||
|
# Determine the number of levels
|
||||||
|
var num_levels = _rng.randi_range(min_levels, max_levels)
|
||||||
|
|
||||||
|
# Calculate ELO for each level
|
||||||
|
var elo_step = float(final_elo - starting_elo) / (num_levels - 1)
|
||||||
|
|
||||||
|
# Create starting node (always at position 3, level 0)
|
||||||
|
var start_node = {
|
||||||
|
"id": _get_next_id(),
|
||||||
|
"type": RoomType.STARTING,
|
||||||
|
"level": 0,
|
||||||
|
"position": Vector2(3, 0),
|
||||||
|
"elo": starting_elo
|
||||||
|
}
|
||||||
|
nodes.append(start_node)
|
||||||
|
|
||||||
|
# Create final boss node (always at position 3, highest level)
|
||||||
|
var final_node = {
|
||||||
|
"id": _get_next_id(),
|
||||||
|
"type": RoomType.FINAL,
|
||||||
|
"level": num_levels - 1,
|
||||||
|
"position": Vector2(3, num_levels - 1),
|
||||||
|
"elo": final_elo
|
||||||
|
}
|
||||||
|
nodes.append(final_node)
|
||||||
|
|
||||||
|
# Generate intermediate levels
|
||||||
|
var levels_nodes = {0: [start_node], (num_levels - 1): [final_node]}
|
||||||
|
|
||||||
|
for level in range(1, num_levels - 1):
|
||||||
|
var level_nodes = []
|
||||||
|
|
||||||
|
# Calculate ELO for this level
|
||||||
|
var level_elo = starting_elo + (elo_step * level)
|
||||||
|
|
||||||
|
# Determine number of nodes for this level
|
||||||
|
var num_nodes = _rng.randi_range(min_nodes_per_level, max_nodes_per_level)
|
||||||
|
|
||||||
|
# Generate available positions and shuffle them
|
||||||
|
var available_positions = []
|
||||||
|
for pos in range(positions_per_level):
|
||||||
|
available_positions.append(pos)
|
||||||
|
available_positions.shuffle()
|
||||||
|
|
||||||
|
# Create nodes for this level
|
||||||
|
for i in range(num_nodes):
|
||||||
|
var node_type = _get_random_room_type(level, num_levels)
|
||||||
|
var node = {
|
||||||
|
"id": _get_next_id(),
|
||||||
|
"type": node_type,
|
||||||
|
"level": level,
|
||||||
|
"position": Vector2(available_positions[i], level),
|
||||||
|
"elo": level_elo
|
||||||
|
}
|
||||||
|
nodes.append(node)
|
||||||
|
level_nodes.append(node)
|
||||||
|
|
||||||
|
levels_nodes[level] = level_nodes
|
||||||
|
|
||||||
|
# Connect nodes between levels
|
||||||
|
# First connect starting node to level 1
|
||||||
|
if levels_nodes.has(1) and levels_nodes[1].size() > 0:
|
||||||
|
var num_connections = min(_rng.randi_range(2, 3), levels_nodes[1].size())
|
||||||
|
var targets = levels_nodes[1].duplicate()
|
||||||
|
targets.shuffle()
|
||||||
|
|
||||||
|
for i in range(num_connections):
|
||||||
|
connections.append({
|
||||||
|
"from": start_node.id,
|
||||||
|
"to": targets[i].id
|
||||||
|
})
|
||||||
|
|
||||||
|
# Keep track of which nodes are connected
|
||||||
|
var connected_nodes = [start_node.id]
|
||||||
|
for level in range(1, num_levels - 1):
|
||||||
|
if not levels_nodes.has(level) or not levels_nodes.has(level + 1):
|
||||||
|
continue
|
||||||
|
|
||||||
|
var current_level_nodes = levels_nodes[level]
|
||||||
|
var next_level_nodes = levels_nodes[level + 1].duplicate()
|
||||||
|
next_level_nodes.shuffle()
|
||||||
|
|
||||||
|
# For each node in current level that is connected from previous level
|
||||||
|
for node in current_level_nodes:
|
||||||
|
if _is_node_connected_to(node.id, connections):
|
||||||
|
# Add to connected nodes
|
||||||
|
connected_nodes.append(node.id)
|
||||||
|
|
||||||
|
# Connect to 1-2 nodes in next level if not the final level
|
||||||
|
if level < num_levels - 2:
|
||||||
|
var num_next_connections = _rng.randi_range(1, 2)
|
||||||
|
num_next_connections = min(num_next_connections, next_level_nodes.size())
|
||||||
|
|
||||||
|
for i in range(num_next_connections):
|
||||||
|
if i < next_level_nodes.size():
|
||||||
|
connections.append({
|
||||||
|
"from": node.id,
|
||||||
|
"to": next_level_nodes[i].id
|
||||||
|
})
|
||||||
|
|
||||||
|
# Remove the selected nodes so they aren't picked again
|
||||||
|
for i in range(num_next_connections):
|
||||||
|
if next_level_nodes.size() > 0:
|
||||||
|
next_level_nodes.pop_front()
|
||||||
|
|
||||||
|
# Connect to final boss if at the level before
|
||||||
|
elif level == num_levels - 2:
|
||||||
|
connections.append({
|
||||||
|
"from": node.id,
|
||||||
|
"to": final_node.id
|
||||||
|
})
|
||||||
|
|
||||||
|
# Remove any nodes that aren't connected
|
||||||
|
var valid_nodes = []
|
||||||
|
for node in nodes:
|
||||||
|
if connected_nodes.has(node.id) or node.id == final_node.id:
|
||||||
|
valid_nodes.append(node)
|
||||||
|
|
||||||
|
# Clean up connections to removed nodes
|
||||||
|
var valid_connections = []
|
||||||
|
for conn in connections:
|
||||||
|
var from_valid = false
|
||||||
|
var to_valid = false
|
||||||
|
|
||||||
|
for node in valid_nodes:
|
||||||
|
if node.id == conn.from:
|
||||||
|
from_valid = true
|
||||||
|
if node.id == conn.to:
|
||||||
|
to_valid = true
|
||||||
|
|
||||||
|
if from_valid and to_valid:
|
||||||
|
valid_connections.append(conn)
|
||||||
|
valid_connections = ensure_path_exists(valid_nodes, valid_connections)
|
||||||
|
|
||||||
|
# Then filter out overlapping connections while maintaining connectivity
|
||||||
|
valid_connections = filter_overlapping_connections(valid_nodes, valid_connections)
|
||||||
|
|
||||||
|
# Perform a final check to make sure all nodes are connected
|
||||||
|
var reachable_nodes = {}
|
||||||
|
reachable_nodes[0] = true # Start node id is always 0
|
||||||
|
|
||||||
|
# Build the graph again
|
||||||
|
var graph = {}
|
||||||
|
for node in valid_nodes:
|
||||||
|
graph[node.id] = []
|
||||||
|
|
||||||
|
for conn in valid_connections:
|
||||||
|
if not graph.has(conn.from):
|
||||||
|
graph[conn.from] = []
|
||||||
|
graph[conn.from].append(conn.to)
|
||||||
|
|
||||||
|
# Do a BFS from the start node
|
||||||
|
var queue = [0] # Starting node ID
|
||||||
|
while queue.size() > 0:
|
||||||
|
var current = queue.pop_front()
|
||||||
|
|
||||||
|
if graph.has(current):
|
||||||
|
for neighbor in graph[current]:
|
||||||
|
if not reachable_nodes.has(neighbor):
|
||||||
|
reachable_nodes[neighbor] = true
|
||||||
|
queue.append(neighbor)
|
||||||
|
|
||||||
|
# Check if all nodes are reachable
|
||||||
|
var all_reachable = true
|
||||||
|
for node in valid_nodes:
|
||||||
|
if not reachable_nodes.has(node.id):
|
||||||
|
all_reachable = false
|
||||||
|
print("Node not reachable: ", node.id, " - Type: ", node.type)
|
||||||
|
break
|
||||||
|
|
||||||
|
# If not all nodes are reachable, only keep the reachable ones
|
||||||
|
if not all_reachable:
|
||||||
|
var truly_valid_nodes = []
|
||||||
|
for node in valid_nodes:
|
||||||
|
if reachable_nodes.has(node.id):
|
||||||
|
truly_valid_nodes.append(node)
|
||||||
|
|
||||||
|
# Update valid_nodes to only include reachable nodes
|
||||||
|
valid_nodes = truly_valid_nodes
|
||||||
|
|
||||||
|
# Also update connections to only include those between reachable nodes
|
||||||
|
var truly_valid_connections = []
|
||||||
|
for conn in valid_connections:
|
||||||
|
if reachable_nodes.has(conn.from) and reachable_nodes.has(conn.to):
|
||||||
|
truly_valid_connections.append(conn)
|
||||||
|
|
||||||
|
valid_connections = truly_valid_connections
|
||||||
|
|
||||||
|
space_nodes_horizontally(valid_nodes, valid_connections)
|
||||||
|
# Return the generated map
|
||||||
|
return {
|
||||||
|
"nodes": valid_nodes,
|
||||||
|
"connections": valid_connections,
|
||||||
|
"levels": num_levels,
|
||||||
|
"seed": _rng.seed
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get a node type based on level - more shops and events in middle levels
|
||||||
|
func _get_random_room_type(level, total_levels):
|
||||||
|
# Chance of special rooms based on level
|
||||||
|
var boss_chance = 0.1 + (level / float(total_levels) * 0.1) # Higher chance in later levels
|
||||||
|
var shop_chance = 0.1 + (level / float(total_levels) * 0.05) # Higher chance in later levels
|
||||||
|
var event_chance = 0.05
|
||||||
|
|
||||||
|
# Last non-final level should have more bosses
|
||||||
|
if level == total_levels - 2:
|
||||||
|
boss_chance += 0.3
|
||||||
|
|
||||||
|
var roll = _rng.randf()
|
||||||
|
print("_get_random_room_type ", roll, " ", boss_chance, " ", shop_chance, " ", event_chance)
|
||||||
|
if roll < boss_chance:
|
||||||
|
return RoomType.BOSS
|
||||||
|
elif roll < boss_chance + shop_chance:
|
||||||
|
return RoomType.SHOP
|
||||||
|
elif roll < boss_chance + shop_chance + event_chance:
|
||||||
|
return RoomType.EVENT
|
||||||
|
else:
|
||||||
|
return RoomType.NORMAL
|
||||||
|
|
||||||
|
# Check if a node is connected as a destination in any connection
|
||||||
|
func _is_node_connected_to(node_id, connections):
|
||||||
|
for conn in connections:
|
||||||
|
if conn.to == node_id:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Get a unique ID for a new node
|
||||||
|
func _get_next_id():
|
||||||
|
var id = _next_id
|
||||||
|
_next_id += 1
|
||||||
|
return id
|
||||||
|
|
||||||
|
|
||||||
|
func filter_overlapping_connections(nodes, connections):
|
||||||
|
var valid_connections = []
|
||||||
|
var node_positions = {}
|
||||||
|
|
||||||
|
# Create a lookup for node positions
|
||||||
|
for node in nodes:
|
||||||
|
node_positions[node.id] = Vector2(node.position.x, node.position.y)
|
||||||
|
|
||||||
|
# Create a graph representation to check connectivity
|
||||||
|
var graph = {}
|
||||||
|
for node in nodes:
|
||||||
|
graph[node.id] = []
|
||||||
|
|
||||||
|
for connection in connections:
|
||||||
|
if not graph.has(connection.from):
|
||||||
|
graph[connection.from] = []
|
||||||
|
if not graph.has(connection.to):
|
||||||
|
graph[connection.to] = []
|
||||||
|
graph[connection.from].append(connection.to)
|
||||||
|
|
||||||
|
# Find start and end nodes
|
||||||
|
var start_node_id = -1
|
||||||
|
var end_node_id = -1
|
||||||
|
for node in nodes:
|
||||||
|
if node.type == RoomType.STARTING:
|
||||||
|
start_node_id = node.id
|
||||||
|
elif node.type == RoomType.FINAL:
|
||||||
|
end_node_id = node.id
|
||||||
|
|
||||||
|
# Sort connections by priority, giving preference to:
|
||||||
|
# 1. Critical path connections
|
||||||
|
# 2. More vertical connections (to reduce crossing)
|
||||||
|
# 3. Shorter connections
|
||||||
|
var sorted_connections = connections.duplicate()
|
||||||
|
sorted_connections.sort_custom(func(a, b):
|
||||||
|
var a_from_pos = node_positions[a.from]
|
||||||
|
var a_to_pos = node_positions[a.to]
|
||||||
|
var b_from_pos = node_positions[b.from]
|
||||||
|
var b_to_pos = node_positions[b.to]
|
||||||
|
|
||||||
|
# Critical path connections (ones needed to reach the final boss) get highest priority
|
||||||
|
var a_critical = is_on_critical_path(a.from, a.to, start_node_id, end_node_id, graph)
|
||||||
|
var b_critical = is_on_critical_path(b.from, b.to, start_node_id, end_node_id, graph)
|
||||||
|
|
||||||
|
if a_critical and not b_critical:
|
||||||
|
return true
|
||||||
|
elif not a_critical and b_critical:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Connections from the same node should be spread out by x-position
|
||||||
|
if a.from == b.from:
|
||||||
|
var a_x_diff = abs(a_from_pos.x - a_to_pos.x)
|
||||||
|
var b_x_diff = abs(b_from_pos.x - b_to_pos.x)
|
||||||
|
# Prefer more aligned connections (less horizontal distance)
|
||||||
|
return a_x_diff < b_x_diff
|
||||||
|
|
||||||
|
# Calculate how vertical each connection is
|
||||||
|
var a_dx = abs(a_to_pos.x - a_from_pos.x)
|
||||||
|
var a_dy = abs(a_to_pos.y - a_from_pos.y)
|
||||||
|
var b_dx = abs(b_to_pos.x - b_from_pos.x)
|
||||||
|
var b_dy = abs(b_to_pos.y - b_from_pos.y)
|
||||||
|
|
||||||
|
var a_vertical_ratio = a_dy / max(a_dx, 0.1) # Avoid division by zero
|
||||||
|
var b_vertical_ratio = b_dy / max(b_dx, 0.1)
|
||||||
|
|
||||||
|
# Prefer more vertical connections
|
||||||
|
if a_vertical_ratio > b_vertical_ratio * 1.5: # Threshold to make the difference significant
|
||||||
|
return true
|
||||||
|
elif b_vertical_ratio > a_vertical_ratio * 1.5:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Then prioritize by level progression (forward progress)
|
||||||
|
var a_forward = a_from_pos.y < a_to_pos.y
|
||||||
|
var b_forward = b_from_pos.y < b_to_pos.y
|
||||||
|
|
||||||
|
if a_forward and not b_forward:
|
||||||
|
return true
|
||||||
|
elif not a_forward and b_forward:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Finally by length
|
||||||
|
var a_dist = (a_to_pos - a_from_pos).length_squared()
|
||||||
|
var b_dist = (b_to_pos - b_from_pos).length_squared()
|
||||||
|
return a_dist < b_dist
|
||||||
|
)
|
||||||
|
|
||||||
|
# First pass: Add all critical path connections
|
||||||
|
for connection in sorted_connections:
|
||||||
|
if is_on_critical_path(connection.from, connection.to, start_node_id, end_node_id, graph):
|
||||||
|
valid_connections.append(connection)
|
||||||
|
|
||||||
|
# Create a set of connected nodes from the critical path
|
||||||
|
var connected_nodes = {}
|
||||||
|
connected_nodes[start_node_id] = true
|
||||||
|
|
||||||
|
for connection in valid_connections:
|
||||||
|
connected_nodes[connection.from] = true
|
||||||
|
connected_nodes[connection.to] = true
|
||||||
|
|
||||||
|
# Second pass: Add remaining connections, avoiding overlaps
|
||||||
|
for connection in sorted_connections:
|
||||||
|
# Skip if already added
|
||||||
|
if connection in valid_connections:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Only consider connections that link to already connected nodes
|
||||||
|
if not connected_nodes.has(connection.from) and not connected_nodes.has(connection.to):
|
||||||
|
continue
|
||||||
|
|
||||||
|
var from_pos = node_positions[connection.from]
|
||||||
|
var to_pos = node_positions[connection.to]
|
||||||
|
|
||||||
|
# Check if this connection would intersect with any valid connection
|
||||||
|
var has_intersection = false
|
||||||
|
for valid_conn in valid_connections:
|
||||||
|
var valid_from_pos = node_positions[valid_conn.from]
|
||||||
|
var valid_to_pos = node_positions[valid_conn.to]
|
||||||
|
|
||||||
|
# Skip if they share a node
|
||||||
|
if connection.from == valid_conn.from or connection.from == valid_conn.to or \
|
||||||
|
connection.to == valid_conn.from or connection.to == valid_conn.to:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check intersection
|
||||||
|
if do_lines_intersect(from_pos, to_pos, valid_from_pos, valid_to_pos):
|
||||||
|
has_intersection = true
|
||||||
|
break
|
||||||
|
|
||||||
|
# If no intersections, add to valid connections
|
||||||
|
if not has_intersection:
|
||||||
|
valid_connections.append(connection)
|
||||||
|
connected_nodes[connection.from] = true
|
||||||
|
connected_nodes[connection.to] = true
|
||||||
|
|
||||||
|
# Final pass: Ensure all nodes are reachable
|
||||||
|
# This part can remain the same as your current implementation
|
||||||
|
|
||||||
|
return valid_connections
|
||||||
|
# Helper function to check if a connection is on a critical path
|
||||||
|
func is_on_critical_path(from_id, to_id, start_id, end_id, graph):
|
||||||
|
# Skip the check if we're directly connecting start to end
|
||||||
|
if (from_id == start_id and to_id == end_id):
|
||||||
|
return true
|
||||||
|
|
||||||
|
# Use breadth-first search to find paths
|
||||||
|
var visited = {from_id: true}
|
||||||
|
var queue = [from_id]
|
||||||
|
|
||||||
|
# First check if we can reach end_id from to_id
|
||||||
|
while queue.size() > 0:
|
||||||
|
var current = queue.pop_front()
|
||||||
|
|
||||||
|
if current == end_id:
|
||||||
|
return true
|
||||||
|
|
||||||
|
for neighbor in graph[current]:
|
||||||
|
if not visited.has(neighbor):
|
||||||
|
visited[neighbor] = true
|
||||||
|
queue.append(neighbor)
|
||||||
|
|
||||||
|
# If we're not on a path to the end, this isn't critical
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Helper function to ensure at least one valid path exists from start to end
|
||||||
|
func ensure_path_exists(nodes, connections):
|
||||||
|
# Find start and end node IDs
|
||||||
|
var start_id = -1
|
||||||
|
var end_id = -1
|
||||||
|
for node in nodes:
|
||||||
|
if node.type == RoomType.STARTING:
|
||||||
|
start_id = node.id
|
||||||
|
elif node.type == RoomType.FINAL:
|
||||||
|
end_id = node.id
|
||||||
|
|
||||||
|
# Build a graph representation
|
||||||
|
var graph = {}
|
||||||
|
for node in nodes:
|
||||||
|
graph[node.id] = []
|
||||||
|
|
||||||
|
for conn in connections:
|
||||||
|
if not graph.has(conn.from):
|
||||||
|
graph[conn.from] = []
|
||||||
|
graph[conn.from].append(conn.to)
|
||||||
|
|
||||||
|
# Use BFS to check if end is reachable from start
|
||||||
|
var visited = {start_id: true}
|
||||||
|
var queue = [start_id]
|
||||||
|
var path_exists = false
|
||||||
|
|
||||||
|
while queue.size() > 0:
|
||||||
|
var current = queue.pop_front()
|
||||||
|
|
||||||
|
if current == end_id:
|
||||||
|
path_exists = true # Path exists
|
||||||
|
break
|
||||||
|
|
||||||
|
for neighbor in graph[current]:
|
||||||
|
if not visited.has(neighbor):
|
||||||
|
visited[neighbor] = true
|
||||||
|
queue.append(neighbor)
|
||||||
|
|
||||||
|
# If path exists, return the existing connections
|
||||||
|
if path_exists:
|
||||||
|
return connections # Return the unmodified connections array
|
||||||
|
|
||||||
|
# No path exists, need to add one
|
||||||
|
# Find nodes by level
|
||||||
|
var nodes_by_level = {}
|
||||||
|
for node in nodes:
|
||||||
|
if not nodes_by_level.has(node.level):
|
||||||
|
nodes_by_level[node.level] = []
|
||||||
|
nodes_by_level[node.level].append(node)
|
||||||
|
|
||||||
|
# Create a direct path from start to end through each level
|
||||||
|
var current_id = start_id
|
||||||
|
var levels = nodes_by_level.keys()
|
||||||
|
levels.sort()
|
||||||
|
|
||||||
|
# Copy connections array to avoid modifying the original
|
||||||
|
var new_connections = connections.duplicate()
|
||||||
|
|
||||||
|
for i in range(1, levels.size()):
|
||||||
|
var level = levels[i]
|
||||||
|
if nodes_by_level.has(level) and nodes_by_level[level].size() > 0:
|
||||||
|
# Pick the middle node in this level
|
||||||
|
var target_node = nodes_by_level[level][nodes_by_level[level].size() / 2]
|
||||||
|
|
||||||
|
# Add a connection from current to this node
|
||||||
|
new_connections.append({
|
||||||
|
"from": current_id,
|
||||||
|
"to": target_node.id
|
||||||
|
})
|
||||||
|
|
||||||
|
current_id = target_node.id
|
||||||
|
|
||||||
|
# If we've reached the final level, connect to end
|
||||||
|
if level == levels[-1]:
|
||||||
|
new_connections.append({
|
||||||
|
"from": current_id,
|
||||||
|
"to": end_id
|
||||||
|
})
|
||||||
|
|
||||||
|
return new_connections
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func do_lines_intersect(a_start: Vector2, a_end: Vector2, b_start: Vector2, b_end: Vector2) -> bool:
|
||||||
|
# Line segment intersection using the parametric equation approach
|
||||||
|
var s1_x = a_end.x - a_start.x
|
||||||
|
var s1_y = a_end.y - a_start.y
|
||||||
|
var s2_x = b_end.x - b_start.x
|
||||||
|
var s2_y = b_end.y - b_start.y
|
||||||
|
|
||||||
|
var denominator = (-s2_x * s1_y + s1_x * s2_y)
|
||||||
|
if denominator == 0:
|
||||||
|
return false # Collinear or parallel lines
|
||||||
|
|
||||||
|
var s = (-s1_y * (a_start.x - b_start.x) + s1_x * (a_start.y - b_start.y)) / denominator
|
||||||
|
var t = (s2_x * (a_start.y - b_start.y) - s2_y * (a_start.x - b_start.x)) / denominator
|
||||||
|
|
||||||
|
# If s and t are between 0-1, the lines intersect
|
||||||
|
return (s >= 0 && s <= 1 && t >= 0 && t <= 1)
|
||||||
|
|
||||||
|
|
||||||
|
# Fixed version of space_nodes_horizontally with proper connection tracking
|
||||||
|
func space_nodes_horizontally(nodes, connections):
|
||||||
|
# Group nodes by level
|
||||||
|
var nodes_by_level = {}
|
||||||
|
for node in nodes:
|
||||||
|
if not nodes_by_level.has(node.level):
|
||||||
|
nodes_by_level[node.level] = []
|
||||||
|
nodes_by_level[node.level].append(node)
|
||||||
|
|
||||||
|
# Process each level to spread out nodes
|
||||||
|
for level in nodes_by_level.keys():
|
||||||
|
var level_nodes = nodes_by_level[level]
|
||||||
|
|
||||||
|
# Skip levels with just one node
|
||||||
|
if level_nodes.size() <= 1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Sort nodes by their current horizontal position
|
||||||
|
level_nodes.sort_custom(func(a, b): return a.position.x < b.position.x)
|
||||||
|
|
||||||
|
# First pass: calculate connections for each node
|
||||||
|
var node_connections = {}
|
||||||
|
for node in level_nodes:
|
||||||
|
node_connections[node.id] = {
|
||||||
|
"above": [],
|
||||||
|
"below": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find connections to this node
|
||||||
|
for conn in connections:
|
||||||
|
# Connection from above
|
||||||
|
if conn.to == node.id:
|
||||||
|
var from_node = null
|
||||||
|
for n in nodes:
|
||||||
|
if n.id == conn.from and n.level < node.level:
|
||||||
|
from_node = n
|
||||||
|
break
|
||||||
|
if from_node:
|
||||||
|
node_connections[node.id].above.append(from_node)
|
||||||
|
|
||||||
|
# Connection to below
|
||||||
|
elif conn.from == node.id:
|
||||||
|
var to_node = null
|
||||||
|
for n in nodes:
|
||||||
|
if n.id == conn.to and n.level > node.level:
|
||||||
|
to_node = n
|
||||||
|
break
|
||||||
|
if to_node:
|
||||||
|
node_connections[node.id].below.append(to_node)
|
||||||
|
|
||||||
|
# Second pass: adjust node positions based on connections
|
||||||
|
for node in level_nodes:
|
||||||
|
# Get this node's connections
|
||||||
|
var connections_above = node_connections[node.id].above
|
||||||
|
var connections_below = node_connections[node.id].below
|
||||||
|
|
||||||
|
# Calculate "pull" forces from connected nodes
|
||||||
|
var pull_x = 0.0
|
||||||
|
var total_pull = 0.0
|
||||||
|
|
||||||
|
# Look at connections to nodes above
|
||||||
|
for conn_node in connections_above:
|
||||||
|
pull_x += conn_node.position.x
|
||||||
|
total_pull += 1.0
|
||||||
|
|
||||||
|
# Look at connections to nodes below
|
||||||
|
for conn_node in connections_below:
|
||||||
|
pull_x += conn_node.position.x
|
||||||
|
total_pull += 1.0
|
||||||
|
|
||||||
|
# Adjust position based on pull if there are connections
|
||||||
|
if total_pull > 0:
|
||||||
|
# Calculate ideal x position (average of connected nodes)
|
||||||
|
var ideal_x = pull_x / total_pull
|
||||||
|
|
||||||
|
# Move slightly toward ideal position (don't move fully to maintain ordering)
|
||||||
|
node.position.x = node.position.x * 0.7 + ideal_x * 0.3
|
||||||
|
|
||||||
|
# Ensure minimum spacing between nodes
|
||||||
|
var min_spacing = 1.0 # Minimum horizontal distance between nodes
|
||||||
|
|
||||||
|
for i in range(1, level_nodes.size()):
|
||||||
|
var prev_node = level_nodes[i-1]
|
||||||
|
var curr_node = level_nodes[i]
|
||||||
|
|
||||||
|
# Check if nodes are too close
|
||||||
|
if curr_node.position.x - prev_node.position.x < min_spacing:
|
||||||
|
# Shift this node and all subsequent nodes to the right
|
||||||
|
var shift = min_spacing - (curr_node.position.x - prev_node.position.x)
|
||||||
|
|
||||||
|
for j in range(i, level_nodes.size()):
|
||||||
|
level_nodes[j].position.x += shift
|
||||||
|
|
||||||
|
return nodes
|
||||||
1
Systems/Game/Map/MapGenerator.gd.uid
Normal file
1
Systems/Game/Map/MapGenerator.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bs0u5jjxm2c18
|
||||||
537
Systems/Game/Map/MapScreen.gd
Normal file
537
Systems/Game/Map/MapScreen.gd
Normal file
|
|
@ -0,0 +1,537 @@
|
||||||
|
extends Control
|
||||||
|
class_name MapScreen
|
||||||
|
|
||||||
|
signal back_pressed
|
||||||
|
signal node_selected(node_data)
|
||||||
|
|
||||||
|
const MapGenerator = preload("res://Systems/Game/Map/MapGenerator.gd")
|
||||||
|
# Room type constants
|
||||||
|
enum RoomType {
|
||||||
|
STARTING,
|
||||||
|
NORMAL,
|
||||||
|
BOSS,
|
||||||
|
FINAL,
|
||||||
|
SHOP,
|
||||||
|
EVENT
|
||||||
|
}
|
||||||
|
|
||||||
|
# Node config
|
||||||
|
const NODE_SIZE = Vector2(60, 60)
|
||||||
|
const NODE_SPACING_X = 150
|
||||||
|
const NODE_SPACING_Y = 120
|
||||||
|
const LINE_WIDTH = 3
|
||||||
|
const LINE_COLOR = Color(0.2, 0.2, 0.2)
|
||||||
|
const LINE_COLOR_CRIT = Color(0.9, 0.8, 0.2, 0.8)
|
||||||
|
const LINE_COLOR_SELECTED = Color(0.2, 0.6, 0.2)
|
||||||
|
const LINE_COLOR_ACCESSIBLE = Color(0.6, 0.6, 0.2)
|
||||||
|
|
||||||
|
# Node symbols and colors
|
||||||
|
const NODE_SYMBOLS = {
|
||||||
|
RoomType.STARTING: "O", # Circle
|
||||||
|
RoomType.NORMAL: "■", # Square
|
||||||
|
RoomType.BOSS: "★", # Star
|
||||||
|
RoomType.FINAL: "✱", # Burst
|
||||||
|
RoomType.SHOP: "₵", # Star
|
||||||
|
RoomType.EVENT: "?" # Burst
|
||||||
|
}
|
||||||
|
|
||||||
|
const NODE_COLORS = {
|
||||||
|
RoomType.STARTING: Color(1, 1, 1), # White
|
||||||
|
RoomType.NORMAL: Color(0.2, 0.2, 0.2), # Dark gray
|
||||||
|
RoomType.BOSS: Color(0.9, 0.2, 0.2), # Red
|
||||||
|
RoomType.FINAL: Color(0.9, 0.9, 0.2), # Yellow
|
||||||
|
RoomType.SHOP: Color(0.2, 0.7, 0.9), # Yellow
|
||||||
|
RoomType.EVENT: Color(0.8, 0.4, 0.9) # Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
var map_nodes = []
|
||||||
|
var map_connections = []
|
||||||
|
var node_buttons = {}
|
||||||
|
var connection_lines = []
|
||||||
|
var traversed_map = []
|
||||||
|
var current_node = null
|
||||||
|
var levels = 5
|
||||||
|
|
||||||
|
const SCROLL_PADDING_TOP = 80
|
||||||
|
const SCROLL_PADDING_BOTTOM = 80
|
||||||
|
const SCROLL_PADDING_LEFT = 60
|
||||||
|
const SCROLL_PADDING_RIGHT = 60
|
||||||
|
|
||||||
|
|
||||||
|
@onready var map_container = $MapScrollContainer/MapContainer
|
||||||
|
@onready var back_button = $BackButton
|
||||||
|
@onready var title_label = $TitleLabel
|
||||||
|
@onready var legend_container = $LegendContainer
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
# Connect back button
|
||||||
|
if back_button:
|
||||||
|
back_button.connect("pressed", Callable(self, "_on_back_button_pressed"))
|
||||||
|
|
||||||
|
# Create legend
|
||||||
|
create_legend()
|
||||||
|
|
||||||
|
# Generate default map
|
||||||
|
generate_map()
|
||||||
|
|
||||||
|
# Display the map
|
||||||
|
display_map()
|
||||||
|
|
||||||
|
func create_legend():
|
||||||
|
# Create legend for room types
|
||||||
|
var legend_items = [
|
||||||
|
{"type": RoomType.BOSS, "text": "Boss"},
|
||||||
|
{"type": RoomType.STARTING, "text": "Starting Room"},
|
||||||
|
{"type": RoomType.FINAL, "text": "Final Boss"},
|
||||||
|
{"type": RoomType.NORMAL, "text": "Normal Room"}
|
||||||
|
]
|
||||||
|
|
||||||
|
for item in legend_items:
|
||||||
|
var hbox = HBoxContainer.new()
|
||||||
|
hbox.add_theme_constant_override("separation", 10)
|
||||||
|
|
||||||
|
var symbol = Label.new()
|
||||||
|
symbol.text = NODE_SYMBOLS[item.type]
|
||||||
|
symbol.add_theme_color_override("font_color", NODE_COLORS[item.type])
|
||||||
|
symbol.add_theme_font_size_override("font_size", 24)
|
||||||
|
|
||||||
|
var text = Label.new()
|
||||||
|
text.text = item.text
|
||||||
|
|
||||||
|
hbox.add_child(symbol)
|
||||||
|
hbox.add_child(text)
|
||||||
|
legend_container.add_child(hbox)
|
||||||
|
|
||||||
|
func generate_map():
|
||||||
|
# Clear existing map
|
||||||
|
map_nodes.clear()
|
||||||
|
map_connections.clear()
|
||||||
|
var mapGen = MapGenerator.new().generate_map()
|
||||||
|
print("MapGen ", mapGen)
|
||||||
|
# Create starting node
|
||||||
|
# var start_node = {
|
||||||
|
# "id": 0,
|
||||||
|
# "type": RoomType.STARTING,
|
||||||
|
# "level": 0,
|
||||||
|
# "position": Vector2(3, 4) # Column, Row
|
||||||
|
# }
|
||||||
|
# map_nodes.append(start_node)
|
||||||
|
# current_node = start_node
|
||||||
|
|
||||||
|
# # Create final boss node
|
||||||
|
# var final_node = {
|
||||||
|
# "id": 1,
|
||||||
|
# "type": RoomType.FINAL,
|
||||||
|
# "level": levels - 1,
|
||||||
|
# "position": Vector2(3, 0) # Column, Row
|
||||||
|
# }
|
||||||
|
# map_nodes.append(final_node)
|
||||||
|
for node_data in mapGen.nodes:
|
||||||
|
add_node(
|
||||||
|
node_data.id,
|
||||||
|
node_data.type,
|
||||||
|
node_data.level,
|
||||||
|
node_data.position,
|
||||||
|
node_data.elo
|
||||||
|
)
|
||||||
|
|
||||||
|
# Store elo rating if needed
|
||||||
|
map_nodes[map_nodes.size() - 1].elo = node_data.elo
|
||||||
|
|
||||||
|
# Set current node to starting node (id 0)
|
||||||
|
if node_data.id == 0:
|
||||||
|
current_node = map_nodes[map_nodes.size() - 1]
|
||||||
|
|
||||||
|
# Process connections
|
||||||
|
for connection in mapGen.connections:
|
||||||
|
add_connection(connection.from, connection.to)
|
||||||
|
|
||||||
|
|
||||||
|
func add_node(id, type, level, position, elo):
|
||||||
|
map_nodes.append({
|
||||||
|
"id": id,
|
||||||
|
"type": type,
|
||||||
|
"level": level,
|
||||||
|
"position": position,
|
||||||
|
"elo": elo
|
||||||
|
})
|
||||||
|
|
||||||
|
func add_connection(from_id, to_id):
|
||||||
|
map_connections.append({
|
||||||
|
"from": from_id,
|
||||||
|
"to": to_id
|
||||||
|
})
|
||||||
|
|
||||||
|
func display_map():
|
||||||
|
# Clear previous display
|
||||||
|
for child in map_container.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
node_buttons.clear()
|
||||||
|
connection_lines = []
|
||||||
|
|
||||||
|
# Calculate map dimensions for proper padding
|
||||||
|
var min_y = 9999
|
||||||
|
var max_y = 0
|
||||||
|
var min_x = 9999
|
||||||
|
var max_x = 0
|
||||||
|
|
||||||
|
# Find boundaries of the map
|
||||||
|
for node_data in map_nodes:
|
||||||
|
var x_pos = node_data.position.x * NODE_SPACING_X
|
||||||
|
var y_pos = node_data.position.y * NODE_SPACING_Y
|
||||||
|
min_x = min(min_x, x_pos)
|
||||||
|
max_x = max(max_x, x_pos)
|
||||||
|
min_y = min(min_y, y_pos)
|
||||||
|
max_y = max(max_y, y_pos)
|
||||||
|
|
||||||
|
# Add padding to container size
|
||||||
|
var map_width = max_x - min_x + NODE_SIZE.x * 2 + SCROLL_PADDING_LEFT + SCROLL_PADDING_RIGHT
|
||||||
|
var map_height = max_y - min_y + NODE_SIZE.y * 2 + SCROLL_PADDING_TOP + SCROLL_PADDING_BOTTOM
|
||||||
|
map_container.custom_minimum_size = Vector2(map_width, map_height)
|
||||||
|
|
||||||
|
# Create padding nodes
|
||||||
|
# Create a padding node at the top
|
||||||
|
var top_padding = Control.new()
|
||||||
|
top_padding.custom_minimum_size = Vector2(map_width, SCROLL_PADDING_TOP)
|
||||||
|
top_padding.position = Vector2(0, 0)
|
||||||
|
map_container.add_child(top_padding)
|
||||||
|
|
||||||
|
# Create a padding node at the bottom
|
||||||
|
var bottom_padding = Control.new()
|
||||||
|
bottom_padding.custom_minimum_size = Vector2(map_width, SCROLL_PADDING_BOTTOM)
|
||||||
|
bottom_padding.position = Vector2(0, map_height - SCROLL_PADDING_BOTTOM)
|
||||||
|
map_container.add_child(bottom_padding)
|
||||||
|
|
||||||
|
# Create padding nodes for left and right
|
||||||
|
var left_padding = Control.new()
|
||||||
|
left_padding.custom_minimum_size = Vector2(SCROLL_PADDING_LEFT, map_height)
|
||||||
|
left_padding.position = Vector2(0, 0)
|
||||||
|
map_container.add_child(left_padding)
|
||||||
|
|
||||||
|
var right_padding = Control.new()
|
||||||
|
right_padding.custom_minimum_size = Vector2(SCROLL_PADDING_RIGHT, map_height)
|
||||||
|
right_padding.position = Vector2(map_width - SCROLL_PADDING_RIGHT, 0)
|
||||||
|
map_container.add_child(right_padding)
|
||||||
|
|
||||||
|
# Find the optimal path from start to end for highlighting
|
||||||
|
var critical_path = find_unique_path_to_end()
|
||||||
|
var critical_connections = []
|
||||||
|
|
||||||
|
# Convert path nodes to connections
|
||||||
|
if critical_path.size() > 1:
|
||||||
|
for i in range(critical_path.size() - 1):
|
||||||
|
critical_connections.append({
|
||||||
|
"from": critical_path[i],
|
||||||
|
"to": critical_path[i + 1]
|
||||||
|
})
|
||||||
|
|
||||||
|
# First draw non-critical connections (so critical ones are on top)
|
||||||
|
for connection in map_connections:
|
||||||
|
var is_critical = false
|
||||||
|
for crit_conn in critical_connections:
|
||||||
|
if connection.from == crit_conn.from and connection.to == crit_conn.to:
|
||||||
|
is_critical = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not is_critical:
|
||||||
|
var from_node = get_node_by_id(connection.from)
|
||||||
|
var to_node = get_node_by_id(connection.to)
|
||||||
|
|
||||||
|
if from_node and to_node:
|
||||||
|
draw_curved_connection(from_node, to_node, false)
|
||||||
|
|
||||||
|
# Then draw critical connections on top
|
||||||
|
for crit_conn in critical_connections:
|
||||||
|
var from_node = get_node_by_id(crit_conn.from)
|
||||||
|
var to_node = get_node_by_id(crit_conn.to)
|
||||||
|
|
||||||
|
if from_node and to_node:
|
||||||
|
draw_curved_connection(from_node, to_node, true)
|
||||||
|
|
||||||
|
|
||||||
|
# Draw nodes
|
||||||
|
for node_data in map_nodes:
|
||||||
|
draw_node(node_data)
|
||||||
|
|
||||||
|
func get_node_by_id(id):
|
||||||
|
for node in map_nodes:
|
||||||
|
if node.id == id:
|
||||||
|
return node
|
||||||
|
return null
|
||||||
|
|
||||||
|
func draw_node(node_data):
|
||||||
|
var button = Button.new()
|
||||||
|
button.text = NODE_SYMBOLS[node_data.type]
|
||||||
|
button.custom_minimum_size = NODE_SIZE
|
||||||
|
button.flat = true
|
||||||
|
|
||||||
|
# Add some styling
|
||||||
|
var font = FontFile.new()
|
||||||
|
var style = StyleBoxFlat.new()
|
||||||
|
style.bg_color = Color(0.1, 0.1, 0.1, 0.5)
|
||||||
|
style.corner_radius_top_left = 30
|
||||||
|
style.corner_radius_top_right = 30
|
||||||
|
style.corner_radius_bottom_left = 30
|
||||||
|
style.corner_radius_bottom_right = 30
|
||||||
|
style.border_width_left = 2
|
||||||
|
style.border_width_top = 2
|
||||||
|
style.border_width_right = 2
|
||||||
|
style.border_width_bottom = 2
|
||||||
|
style.border_color = NODE_COLORS[node_data.type]
|
||||||
|
|
||||||
|
button.add_theme_font_size_override("font_size", 28)
|
||||||
|
button.add_theme_color_override("font_color", NODE_COLORS[node_data.type])
|
||||||
|
button.add_theme_stylebox_override("normal", style)
|
||||||
|
|
||||||
|
# Position the node with top and left padding adjustments
|
||||||
|
var x_pos = node_data.position.x * NODE_SPACING_X + SCROLL_PADDING_LEFT
|
||||||
|
var y_pos = node_data.position.y * NODE_SPACING_Y + SCROLL_PADDING_TOP
|
||||||
|
button.position = Vector2(x_pos, y_pos) - NODE_SIZE/2
|
||||||
|
|
||||||
|
# Store data
|
||||||
|
button.set_meta("node_data", node_data)
|
||||||
|
|
||||||
|
# Connect signal
|
||||||
|
button.pressed.connect(func(): _on_node_pressed(node_data))
|
||||||
|
|
||||||
|
# Add to map
|
||||||
|
map_container.add_child(button)
|
||||||
|
node_buttons[node_data.id] = button
|
||||||
|
|
||||||
|
# Highlight current node
|
||||||
|
if current_node and node_data.id == current_node.id:
|
||||||
|
highlight_current_node(button)
|
||||||
|
|
||||||
|
func draw_connection(from_node, to_node):
|
||||||
|
var line = Line2D.new()
|
||||||
|
line.width = LINE_WIDTH
|
||||||
|
line.default_color = LINE_COLOR
|
||||||
|
if from_node.id == current_node.id:
|
||||||
|
line.default_color = LINE_COLOR_ACCESSIBLE
|
||||||
|
elif traversed_map.has(to_node.id) and (traversed_map.has(from_node.id) || from_node.id == 0):
|
||||||
|
line.default_color = LINE_COLOR_SELECTED
|
||||||
|
|
||||||
|
# Calculate start and end positions with top and left padding adjustments
|
||||||
|
var start_pos = from_node.position * Vector2(NODE_SPACING_X, NODE_SPACING_Y) + Vector2(SCROLL_PADDING_LEFT, SCROLL_PADDING_TOP)
|
||||||
|
var end_pos = to_node.position * Vector2(NODE_SPACING_X, NODE_SPACING_Y) + Vector2(SCROLL_PADDING_LEFT, SCROLL_PADDING_TOP)
|
||||||
|
|
||||||
|
# Add points
|
||||||
|
line.add_point(start_pos)
|
||||||
|
line.add_point(end_pos)
|
||||||
|
|
||||||
|
# Add to map
|
||||||
|
map_container.add_child(line)
|
||||||
|
connection_lines.append(line)
|
||||||
|
|
||||||
|
func highlight_current_node(button):
|
||||||
|
var style = button.get_theme_stylebox("normal").duplicate()
|
||||||
|
style.bg_color = Color(0.3, 0.3, 0.3, 0.7)
|
||||||
|
style.border_width_left = 4
|
||||||
|
style.border_width_top = 4
|
||||||
|
style.border_width_right = 4
|
||||||
|
style.border_width_bottom = 4
|
||||||
|
button.add_theme_stylebox_override("normal", style)
|
||||||
|
|
||||||
|
func _on_node_pressed(node_data):
|
||||||
|
# Check if the node is accessible from current node
|
||||||
|
if is_node_accessible(node_data):
|
||||||
|
# Update current node
|
||||||
|
current_node = node_data
|
||||||
|
traversed_node(node_data)
|
||||||
|
# Refresh display to update highlights
|
||||||
|
display_map()
|
||||||
|
|
||||||
|
# Emit signal with selected node data
|
||||||
|
emit_signal("node_selected", node_data)
|
||||||
|
else:
|
||||||
|
print("Node not accessible from current position", node_data)
|
||||||
|
|
||||||
|
func traversed_node(node_data):
|
||||||
|
traversed_map.append(node_data.id)
|
||||||
|
|
||||||
|
func is_node_accessible(node_data):
|
||||||
|
# If no current node, only starting node is accessible
|
||||||
|
if current_node == null:
|
||||||
|
return node_data.type == RoomType.STARTING
|
||||||
|
|
||||||
|
# Check if there's a direct connection from current node to target node
|
||||||
|
for connection in map_connections:
|
||||||
|
if connection.from == current_node.id and connection.to == node_data.id:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _on_back_button_pressed():
|
||||||
|
emit_signal("back_pressed")
|
||||||
|
visible = false
|
||||||
|
|
||||||
|
func draw_curved_connection(from_node, to_node, isCritPath):
|
||||||
|
var line = Line2D.new()
|
||||||
|
line.width = LINE_WIDTH
|
||||||
|
line.default_color = LINE_COLOR
|
||||||
|
# if isCritPath:
|
||||||
|
# line.default_color = LINE_COLOR_CRIT
|
||||||
|
if from_node.id == current_node.id:
|
||||||
|
line.default_color = LINE_COLOR_ACCESSIBLE
|
||||||
|
elif traversed_map.has(to_node.id) and (traversed_map.has(from_node.id) || from_node.id == 0):
|
||||||
|
line.default_color = LINE_COLOR_SELECTED
|
||||||
|
|
||||||
|
# Calculate start and end positions with padding adjustments
|
||||||
|
var start_pos = from_node.position * Vector2(NODE_SPACING_X, NODE_SPACING_Y) + Vector2(SCROLL_PADDING_LEFT, SCROLL_PADDING_TOP)
|
||||||
|
var end_pos = to_node.position * Vector2(NODE_SPACING_X, NODE_SPACING_Y) + Vector2(SCROLL_PADDING_LEFT, SCROLL_PADDING_TOP)
|
||||||
|
|
||||||
|
# Find all connections from the same starting node to help with path separation
|
||||||
|
var sibling_connections = []
|
||||||
|
for conn in map_connections:
|
||||||
|
if conn.from == from_node.id and conn.to != to_node.id:
|
||||||
|
var other_node = get_node_by_id(conn.to)
|
||||||
|
if other_node:
|
||||||
|
sibling_connections.append(other_node)
|
||||||
|
|
||||||
|
# Count how many connections this source node has
|
||||||
|
var connection_count = sibling_connections.size() + 1 # +1 for this connection
|
||||||
|
|
||||||
|
# Determine position of this connection among siblings (for consistent curve offsets)
|
||||||
|
var connection_index = 0
|
||||||
|
for i in range(sibling_connections.size()):
|
||||||
|
if sibling_connections[i].id < to_node.id:
|
||||||
|
connection_index += 1
|
||||||
|
|
||||||
|
# Create a bezier curve path
|
||||||
|
var points = []
|
||||||
|
|
||||||
|
# Calculate direction vector between nodes
|
||||||
|
var direction = (end_pos - start_pos).normalized()
|
||||||
|
var perp_direction = Vector2(-direction.y, direction.x) # Perpendicular to direction
|
||||||
|
|
||||||
|
# Different curve handling based on node positions
|
||||||
|
if from_node.position.y != to_node.position.y:
|
||||||
|
# Vertical/diagonal connection between different levels
|
||||||
|
|
||||||
|
# Calculate curve offset based on connection count and index
|
||||||
|
var curve_offset = 0.0
|
||||||
|
if connection_count > 1:
|
||||||
|
# Calculate spacing between -0.5 and 0.5
|
||||||
|
var spacing = 1.0 / (connection_count + 1)
|
||||||
|
curve_offset = (connection_index + 1) * spacing - 0.5
|
||||||
|
curve_offset *= NODE_SPACING_X * 0.8 # Scale by node spacing
|
||||||
|
|
||||||
|
# Generate control points with horizontal offset
|
||||||
|
var dist = (end_pos - start_pos).length()
|
||||||
|
var mid_point = start_pos + (end_pos - start_pos) * 0.5
|
||||||
|
|
||||||
|
# Apply the offset perpendicular to the path direction
|
||||||
|
mid_point += perp_direction * curve_offset
|
||||||
|
|
||||||
|
# Calculate control points with vertical offsets
|
||||||
|
var control1 = start_pos + direction * (dist * 0.25) + perp_direction * curve_offset * 1.5
|
||||||
|
var control2 = end_pos - direction * (dist * 0.25) + perp_direction * curve_offset * 1.5
|
||||||
|
|
||||||
|
# Generate points for a cubic bezier curve
|
||||||
|
for i in range(21): # More points for a smoother curve
|
||||||
|
var t = i / 20.0
|
||||||
|
var point = cubic_bezier(start_pos, control1, control2, end_pos, t)
|
||||||
|
points.append(point)
|
||||||
|
else:
|
||||||
|
# Horizontal connection (nodes at same level)
|
||||||
|
var curve_height = NODE_SPACING_Y * 0.4 # Base curve height
|
||||||
|
|
||||||
|
# Adjust curve height based on connection position
|
||||||
|
if connection_count > 1:
|
||||||
|
curve_height *= (1.0 + 0.3 * connection_index)
|
||||||
|
|
||||||
|
# Determine curve direction (up or down)
|
||||||
|
var curve_up = true
|
||||||
|
|
||||||
|
# If nodes are further apart horizontally, make a higher curve
|
||||||
|
var x_distance = abs(from_node.position.x - to_node.position.x)
|
||||||
|
if x_distance > 1:
|
||||||
|
curve_height *= (1.0 + 0.2 * x_distance)
|
||||||
|
|
||||||
|
# Calculate mid point with vertical offset
|
||||||
|
var mid_x = (start_pos.x + end_pos.x) / 2
|
||||||
|
var mid_y = null
|
||||||
|
if curve_up:
|
||||||
|
mid_y = start_pos - curve_height
|
||||||
|
else :
|
||||||
|
mid_y = start_pos - (-curve_height)
|
||||||
|
var mid_point = Vector2(mid_x, mid_y)
|
||||||
|
|
||||||
|
# Generate points for a quadratic bezier curve
|
||||||
|
for i in range(21):
|
||||||
|
var t = i / 20.0
|
||||||
|
var point = quadratic_bezier(start_pos, mid_point, end_pos, t)
|
||||||
|
points.append(point)
|
||||||
|
|
||||||
|
# Apply anti-aliasing
|
||||||
|
line.antialiased = true
|
||||||
|
|
||||||
|
# Add the points to the line
|
||||||
|
for point in points:
|
||||||
|
line.add_point(point)
|
||||||
|
|
||||||
|
# Add to map
|
||||||
|
map_container.add_child(line)
|
||||||
|
connection_lines.append(line)
|
||||||
|
|
||||||
|
func find_unique_path_to_end():
|
||||||
|
# Find start and end nodes
|
||||||
|
var start_node = null
|
||||||
|
var end_node = null
|
||||||
|
|
||||||
|
for node in map_nodes:
|
||||||
|
if node.type == RoomType.STARTING:
|
||||||
|
start_node = node
|
||||||
|
elif node.type == RoomType.FINAL:
|
||||||
|
end_node = node
|
||||||
|
|
||||||
|
if not start_node or not end_node:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Build a graph representation
|
||||||
|
var graph = {}
|
||||||
|
for node in map_nodes:
|
||||||
|
graph[node.id] = []
|
||||||
|
|
||||||
|
for conn in map_connections:
|
||||||
|
if graph.has(conn.from):
|
||||||
|
graph[conn.from].append(conn.to)
|
||||||
|
|
||||||
|
# Use BFS to find the shortest path
|
||||||
|
var queue = [[start_node.id]] # Queue of paths
|
||||||
|
var visited = {start_node.id: true}
|
||||||
|
|
||||||
|
while queue.size() > 0:
|
||||||
|
var path = queue.pop_front()
|
||||||
|
var node_id = path[path.size() - 1]
|
||||||
|
|
||||||
|
if node_id == end_node.id:
|
||||||
|
return path # Return the full path
|
||||||
|
|
||||||
|
if graph.has(node_id):
|
||||||
|
for neighbor in graph[node_id]:
|
||||||
|
if not visited.has(neighbor):
|
||||||
|
visited[neighbor] = true
|
||||||
|
var new_path = path.duplicate()
|
||||||
|
new_path.append(neighbor)
|
||||||
|
queue.append(new_path)
|
||||||
|
|
||||||
|
return [] # No path found
|
||||||
|
|
||||||
|
# Helper function for cubic bezier curve calculation
|
||||||
|
func cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float) -> Vector2:
|
||||||
|
var q0 = p0.lerp(p1, t)
|
||||||
|
var q1 = p1.lerp(p2, t)
|
||||||
|
var q2 = p2.lerp(p3, t)
|
||||||
|
|
||||||
|
var r0 = q0.lerp(q1, t)
|
||||||
|
var r1 = q1.lerp(q2, t)
|
||||||
|
|
||||||
|
return r0.lerp(r1, t)
|
||||||
|
|
||||||
|
# Helper function for quadratic bezier curve calculation
|
||||||
|
func quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float) -> Vector2:
|
||||||
|
var q0 = p0.lerp(p1, t)
|
||||||
|
var q1 = p1.lerp(p2, t)
|
||||||
|
|
||||||
|
return q0.lerp(q1, t)
|
||||||
1
Systems/Game/Map/MapScreen.gd.uid
Normal file
1
Systems/Game/Map/MapScreen.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://ckpv3snpg6g34
|
||||||
147
Systems/Game/Map/NodeLayoutHelper.gd
Normal file
147
Systems/Game/Map/NodeLayoutHelper.gd
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
extends RefCounted
|
||||||
|
class_name NodeLayoutHelper
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
var map_width: int = 6 # Available horizontal positions (0-5)
|
||||||
|
var level_height: float = 1.0 # Vertical spacing between levels
|
||||||
|
var node_spread: float = 0.7 # How much to spread nodes horizontally (0-1)
|
||||||
|
var path_smoothness: float = 0.3 # How much to align nodes with their connections (0-1)
|
||||||
|
|
||||||
|
# Internal
|
||||||
|
var _rng = RandomNumberGenerator.new()
|
||||||
|
|
||||||
|
func _init(seed_value: int = 0):
|
||||||
|
if seed_value != 0:
|
||||||
|
_rng.seed = seed_value
|
||||||
|
else:
|
||||||
|
_rng.randomize()
|
||||||
|
|
||||||
|
# Organize node positions for better visual layout
|
||||||
|
func organize_layout(nodes: Array, connections: Array) -> Array:
|
||||||
|
# Clone the nodes so we don't modify the original array
|
||||||
|
var updated_nodes = []
|
||||||
|
for node in nodes:
|
||||||
|
updated_nodes.append(node.duplicate())
|
||||||
|
|
||||||
|
# Group nodes by level
|
||||||
|
var nodes_by_level = {}
|
||||||
|
|
||||||
|
for node in updated_nodes:
|
||||||
|
var level = node.level
|
||||||
|
if not nodes_by_level.has(level):
|
||||||
|
nodes_by_level[level] = []
|
||||||
|
nodes_by_level[level].append(node)
|
||||||
|
|
||||||
|
# Process each level
|
||||||
|
for level in nodes_by_level:
|
||||||
|
var level_nodes = nodes_by_level[level]
|
||||||
|
|
||||||
|
# Skip levels with fixed positions
|
||||||
|
if level == 0 or level == nodes_by_level.size() - 1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Calculate optimal positions based on connections
|
||||||
|
for node in level_nodes:
|
||||||
|
_adjust_node_position(node, updated_nodes, connections, nodes_by_level)
|
||||||
|
|
||||||
|
# Resolve overlaps after initial placement
|
||||||
|
for level in nodes_by_level:
|
||||||
|
var level_nodes = nodes_by_level[level]
|
||||||
|
if level_nodes.size() > 1:
|
||||||
|
_resolve_horizontal_overlaps(level_nodes)
|
||||||
|
|
||||||
|
return updated_nodes
|
||||||
|
|
||||||
|
# Adjust node position based on its connections
|
||||||
|
func _adjust_node_position(node, all_nodes, connections, nodes_by_level):
|
||||||
|
# Find all connections to/from this node
|
||||||
|
var connected_from = [] # Nodes in the previous level connecting to this node
|
||||||
|
var connected_to = [] # Nodes in the next level this node connects to
|
||||||
|
|
||||||
|
for conn in connections:
|
||||||
|
if conn.to == node.id:
|
||||||
|
var from_node = _find_node_by_id(all_nodes, conn.from)
|
||||||
|
if from_node and from_node.level == node.level - 1:
|
||||||
|
connected_from.append(from_node)
|
||||||
|
|
||||||
|
if conn.from == node.id:
|
||||||
|
var to_node = _find_node_by_id(all_nodes, conn.to)
|
||||||
|
if to_node and to_node.level == node.level + 1:
|
||||||
|
connected_to.append(to_node)
|
||||||
|
|
||||||
|
# Calculate optimal x position based on connected nodes
|
||||||
|
var optimal_x = node.position.x # Start with current position
|
||||||
|
|
||||||
|
if connected_from.size() > 0 or connected_to.size() > 0:
|
||||||
|
var avg_x = 0.0
|
||||||
|
var total_connected = 0
|
||||||
|
|
||||||
|
# Consider positions of connected nodes
|
||||||
|
for from_node in connected_from:
|
||||||
|
avg_x += from_node.position.x
|
||||||
|
total_connected += 1
|
||||||
|
|
||||||
|
for to_node in connected_to:
|
||||||
|
avg_x += to_node.position.x
|
||||||
|
total_connected += 1
|
||||||
|
|
||||||
|
if total_connected > 0:
|
||||||
|
avg_x /= total_connected
|
||||||
|
|
||||||
|
# Blend between current position and optimal position
|
||||||
|
optimal_x = lerp(node.position.x, avg_x, path_smoothness)
|
||||||
|
|
||||||
|
# Add some random variation to prevent everything aligning too perfectly
|
||||||
|
optimal_x += (_rng.randf() - 0.5) * node_spread
|
||||||
|
|
||||||
|
# Keep within bounds
|
||||||
|
optimal_x = clamp(optimal_x, 0, map_width)
|
||||||
|
|
||||||
|
# Update node position
|
||||||
|
node.position.x = optimal_x
|
||||||
|
|
||||||
|
# Y position is determined by level
|
||||||
|
node.position.y = node.level * level_height
|
||||||
|
|
||||||
|
# Resolve horizontal overlaps between nodes on the same level
|
||||||
|
func _resolve_horizontal_overlaps(level_nodes):
|
||||||
|
# Sort nodes by x position
|
||||||
|
level_nodes.sort_custom(func(a, b): return a.position.x < b.position.x)
|
||||||
|
|
||||||
|
# Minimum distance between nodes
|
||||||
|
var min_distance = 1.0
|
||||||
|
|
||||||
|
# Check for overlaps and adjust
|
||||||
|
for i in range(1, level_nodes.size()):
|
||||||
|
var prev_node = level_nodes[i-1]
|
||||||
|
var curr_node = level_nodes[i]
|
||||||
|
|
||||||
|
if curr_node.position.x - prev_node.position.x < min_distance:
|
||||||
|
# If too close, move them apart
|
||||||
|
var midpoint = (prev_node.position.x + curr_node.position.x) / 2
|
||||||
|
var half_distance = min_distance / 2
|
||||||
|
|
||||||
|
# Try to maintain relative positions
|
||||||
|
prev_node.position.x = midpoint - half_distance
|
||||||
|
curr_node.position.x = midpoint + half_distance
|
||||||
|
|
||||||
|
# Ensure we stay within bounds
|
||||||
|
if prev_node.position.x < 0:
|
||||||
|
var shift = -prev_node.position.x
|
||||||
|
prev_node.position.x += shift
|
||||||
|
curr_node.position.x += shift
|
||||||
|
|
||||||
|
if curr_node.position.x > map_width:
|
||||||
|
var shift = curr_node.position.x - map_width
|
||||||
|
prev_node.position.x -= shift
|
||||||
|
curr_node.position.x -= shift
|
||||||
|
|
||||||
|
# Final boundary check for previous node
|
||||||
|
prev_node.position.x = max(0, prev_node.position.x)
|
||||||
|
|
||||||
|
# Find a node by its ID
|
||||||
|
func _find_node_by_id(nodes, id):
|
||||||
|
for node in nodes:
|
||||||
|
if node.id == id:
|
||||||
|
return node
|
||||||
|
return null
|
||||||
1
Systems/Game/Map/NodeLayoutHelper.gd.uid
Normal file
1
Systems/Game/Map/NodeLayoutHelper.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bmi14xk8tn7xc
|
||||||
|
|
@ -16,7 +16,7 @@ signal map_open_requested(options)
|
||||||
@onready var developerText = $HBoxContainer/VBoxContainer/DeveloperText
|
@onready var developerText = $HBoxContainer/VBoxContainer/DeveloperText
|
||||||
@onready var gameMenuScreen = get_node("/root/Board/GameMenuScreen")
|
@onready var gameMenuScreen = get_node("/root/Board/GameMenuScreen")
|
||||||
@onready var deckManagerScreen = get_node("/root/Board/DeckManagerScreen")
|
@onready var deckManagerScreen = get_node("/root/Board/DeckManagerScreen")
|
||||||
|
@onready var mapScreen = get_node("/root/Board/MapScreen")
|
||||||
@onready var stateMachine = get_node("/root/Board/StateMachine")
|
@onready var stateMachine = get_node("/root/Board/StateMachine")
|
||||||
func _ready():
|
func _ready():
|
||||||
# Connect menu option signals
|
# Connect menu option signals
|
||||||
|
|
@ -34,6 +34,11 @@ func _ready():
|
||||||
if deckManagerScreen:
|
if deckManagerScreen:
|
||||||
deckManagerScreen.connect("back_pressed", Callable(self, "_on_deck_manager_back_pressed"))
|
deckManagerScreen.connect("back_pressed", Callable(self, "_on_deck_manager_back_pressed"))
|
||||||
deckManagerScreen.visible = false
|
deckManagerScreen.visible = false
|
||||||
|
|
||||||
|
if mapScreen:
|
||||||
|
mapScreen.connect("back_pressed", Callable(self, "_on_map_back_pressed"))
|
||||||
|
mapScreen.connect("node_selected", Callable(self, "_on_map_node_selected"))
|
||||||
|
mapScreen.visible = false
|
||||||
load_version()
|
load_version()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -124,6 +129,9 @@ func _on_deck_manager_back_pressed():
|
||||||
|
|
||||||
func _on_map_open_requested(options):
|
func _on_map_open_requested(options):
|
||||||
print("Map requested with options:", options)
|
print("Map requested with options:", options)
|
||||||
|
gameMenuScreen.visible = false
|
||||||
|
if mapScreen:
|
||||||
|
mapScreen.visible = true
|
||||||
emit_signal("map_open_requested", options)
|
emit_signal("map_open_requested", options)
|
||||||
|
|
||||||
func _on_start_game_pressed(options):
|
func _on_start_game_pressed(options):
|
||||||
|
|
@ -131,8 +139,22 @@ func _on_start_game_pressed(options):
|
||||||
|
|
||||||
emit_signal("new_game_requested", options)
|
emit_signal("new_game_requested", options)
|
||||||
|
|
||||||
|
func _on_map_back_pressed():
|
||||||
|
mapScreen.visible = false
|
||||||
|
gameMenuScreen.visible = true
|
||||||
|
|
||||||
|
# Map node selection
|
||||||
|
func _on_map_node_selected(node_data):
|
||||||
|
print("Selected map node: ", node_data)
|
||||||
|
# Implement logic for map node selection
|
||||||
|
# For example, start a battle based on the node type
|
||||||
|
|
||||||
# Public method to show the menu
|
# Public method to show the menu
|
||||||
func show_menu():
|
func show_menu():
|
||||||
self.visible = true
|
self.visible = true
|
||||||
if gameMenuScreen:
|
if gameMenuScreen:
|
||||||
gameMenuScreen.visible = false
|
gameMenuScreen.visible = false
|
||||||
|
if deckManagerScreen:
|
||||||
|
deckManagerScreen.visible = false
|
||||||
|
if mapScreen:
|
||||||
|
mapScreen.visible = false
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=29 format=3 uid="uid://d0qyk6v20uief"]
|
[gd_scene load_steps=30 format=3 uid="uid://d0qyk6v20uief"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cbcu68o863pfp" path="res://Systems/Game/ChessGame.gd" id="1_fkb2r"]
|
[ext_resource type="Script" uid="uid://cbcu68o863pfp" path="res://Systems/Game/ChessGame.gd" id="1_fkb2r"]
|
||||||
[ext_resource type="Script" uid="uid://d2bfw6edgkhfa" path="res://Systems/StateMachine/GameStates/WhiteTurn.gd" id="3_276ip"]
|
[ext_resource type="Script" uid="uid://d2bfw6edgkhfa" path="res://Systems/StateMachine/GameStates/WhiteTurn.gd" id="3_276ip"]
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
[ext_resource type="Script" uid="uid://j0m4rwr86oi6" path="res://Systems/Game/GameMenuScreen.gd" id="26_pb4ja"]
|
[ext_resource type="Script" uid="uid://j0m4rwr86oi6" path="res://Systems/Game/GameMenuScreen.gd" id="26_pb4ja"]
|
||||||
[ext_resource type="Script" uid="uid://bfjmon81nckns" path="res://Systems/Game/GameMenuButton.gd" id="26_t2e38"]
|
[ext_resource type="Script" uid="uid://bfjmon81nckns" path="res://Systems/Game/GameMenuButton.gd" id="26_t2e38"]
|
||||||
[ext_resource type="PackedScene" uid="uid://c7uqbcxdjoais" path="res://deck_manager_screen.tscn" id="28_4nyv8"]
|
[ext_resource type="PackedScene" uid="uid://c7uqbcxdjoais" path="res://deck_manager_screen.tscn" id="28_4nyv8"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dxiw67f3rrwue" path="res://map_screen.tscn" id="29_y7cv2"]
|
||||||
|
|
||||||
[node name="Board" type="Control"]
|
[node name="Board" type="Control"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
|
|
@ -388,3 +389,7 @@ texture = ExtResource("23_vmvai")
|
||||||
[node name="DeckManagerScreen" parent="." instance=ExtResource("28_4nyv8")]
|
[node name="DeckManagerScreen" parent="." instance=ExtResource("28_4nyv8")]
|
||||||
visible = false
|
visible = false
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
|
|
||||||
|
[node name="MapScreen" parent="." instance=ExtResource("29_y7cv2")]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 1
|
||||||
|
|
|
||||||
116
map_screen.tscn
Normal file
116
map_screen.tscn
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
[gd_scene load_steps=4 format=3 uid="uid://dxiw67f3rrwue"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://6cmhvsug8nbv" path="res://Systems/Game/Map/DotPatternGenerator.gd" id="3_wcttb"]
|
||||||
|
[ext_resource type="Script" uid="uid://bfjmon81nckns" path="res://Systems/Game/GameMenuButton.gd" id="bfjmon81nckns"]
|
||||||
|
[ext_resource type="Script" uid="uid://ckpv3snpg6g34" path="res://Systems/Game/Map/MapScreen.gd" id="ckpv3snpg6g34"]
|
||||||
|
|
||||||
|
[node name="MapScreen" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("ckpv3snpg6g34")
|
||||||
|
|
||||||
|
[node name="Background" type="ColorRect" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
color = Color(0.08, 0.08, 0.12, 1)
|
||||||
|
|
||||||
|
[node name="DotPattern" type="Control" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="TitleLabel" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_top = 20.0
|
||||||
|
offset_bottom = 72.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 36
|
||||||
|
text = "MAP"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="MapScrollContainer" type="ScrollContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = -1
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -404.0
|
||||||
|
offset_top = -250.0
|
||||||
|
offset_right = 404.0
|
||||||
|
offset_bottom = 242.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
vertical_scroll_mode = 2
|
||||||
|
|
||||||
|
[node name="MapContainer" type="Control" parent="MapScrollContainer"]
|
||||||
|
clip_contents = true
|
||||||
|
custom_minimum_size = Vector2(800, 600)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LegendContainer" type="VBoxContainer" parent="."]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -200.0
|
||||||
|
offset_top = 80.0
|
||||||
|
offset_right = -20.0
|
||||||
|
offset_bottom = 250.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
|
[node name="BackButton" type="RichTextLabel" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 20.0
|
||||||
|
offset_top = -50.0
|
||||||
|
offset_right = 155.0
|
||||||
|
offset_bottom = -20.0
|
||||||
|
grow_vertical = 0
|
||||||
|
text = "BACK"
|
||||||
|
script = ExtResource("bfjmon81nckns")
|
||||||
|
|
||||||
|
[node name="MapInfo" type="Label" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 7
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -377.0
|
||||||
|
offset_top = -100.0
|
||||||
|
offset_right = 377.0
|
||||||
|
offset_bottom = -24.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
text = "After a boss the player selects 1 found card to keep"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="DotPatternGenerator" type="Control" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("3_wcttb")
|
||||||
Loading…
Reference in a new issue