214 lines
6.9 KiB
GDScript
214 lines
6.9 KiB
GDScript
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)
|
|
|
|
# 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
|