Files
ScrapFrontier/game/generators/area_generator.gd
T

275 lines
8.0 KiB
GDScript

class_name AreaGenerator
extends Node
const STAGE_COUNT = 9
const EXTRA_PASSAGE_CHANCE = 33
const PASSAGE_TYPES = {
"1 1 0": PassageData.PassageType.ZeroGrad,
"1 2 0": PassageData.PassageType.Minus26Grad,
"1 2 -1": PassageData.PassageType.Plus26Grad,
"1 3 0": PassageData.PassageType.Plus45Grad,
"1 3 -1": PassageData.PassageType.ZeroGrad,
"1 3 -2": PassageData.PassageType.Minus45Grad,
"2 1 1": PassageData.PassageType.Minus26Grad,
"2 1 0": PassageData.PassageType.Plus26Grad,
"2 2 1": PassageData.PassageType.Minus45Grad,
"2 2 0": PassageData.PassageType.ZeroGrad,
"2 2 -1": PassageData.PassageType.Plus45Grad,
"2 3 0": PassageData.PassageType.Minus26Grad,
"2 3 -1": PassageData.PassageType.Plus26Grad,
"3 1 2": PassageData.PassageType.Minus45Grad,
"3 1 1": PassageData.PassageType.ZeroGrad,
"3 1 0": PassageData.PassageType.Plus45Grad,
"3 2 1": PassageData.PassageType.Minus26Grad,
"3 2 0": PassageData.PassageType.Plus26Grad,
"3 3 1": PassageData.PassageType.Minus45Grad,
"3 3 0": PassageData.PassageType.ZeroGrad,
"3 3 -1": PassageData.PassageType.Plus45Grad,
}
var local_seed_rng : RandomNumberGenerator = RandomNumberGenerator.new()
var stage_seed_rng : RandomNumberGenerator = RandomNumberGenerator.new()
var passage_seed_rng : RandomNumberGenerator = RandomNumberGenerator.new()
var passage_chance_rng : RandomNumberGenerator = RandomNumberGenerator.new()
var passage_direction_rng : RandomNumberGenerator = RandomNumberGenerator.new()
@onready var stage_generator : StageGenerator = $StageGenerator
@onready var passage_generator : PassageGenerator = $PassageGenerator
func generate(seed_value: int) -> AreaData:
local_seed_rng.seed = seed_value
stage_seed_rng.seed = local_seed_rng.randi()
passage_seed_rng.seed = local_seed_rng.randi()
passage_chance_rng.seed = local_seed_rng.randi()
passage_direction_rng.seed = local_seed_rng.randi()
var data : AreaData = AreaData.new()
data.seed_value = seed_value
_fill_stages(data)
_fill_passages(data)
return data
func _fill_stages(data : AreaData) -> void:
for i in range(STAGE_COUNT):
var stage_type := _get_stage_type(i)
var seed_value := stage_seed_rng.randi()
var stage := stage_generator.generate(seed_value, stage_type)
data.stages.append(stage)
_update_neighbors(data)
func _get_stage_type(stage_index: int) -> StageGenerator.StageType:
match stage_index:
0:
return StageGenerator.StageType.Start
STAGE_COUNT - 1:
return StageGenerator.StageType.Boss
_:
return StageGenerator.StageType.Inner
func _fill_passages(data : AreaData) -> void:
for i in range(data.stages.size() - 1):
var first_stage := data.stages[i]
var second_stage := data.stages[i + 1]
_fill_passages_for_pair(data, first_stage, second_stage)
func _fill_passages_for_pair(
data : AreaData,
first_stage: StageData,
second_stage: StageData
) -> void:
var first_size := first_stage.sectors.size()
var second_size := second_stage.sectors.size()
if first_size < second_size:
_fill_passages_for_unequal_pair(
data, first_stage.sectors, second_stage.sectors, _connect_sectors
)
elif first_size > second_size:
_fill_passages_for_unequal_pair(
data, second_stage.sectors, first_stage.sectors, _connect_sectors_flipped
)
else:
_fill_passages_for_equal_pair(data, first_stage.sectors, second_stage.sectors)
func _fill_passages_for_equal_pair(
data : AreaData,
first_sectors: Array[SectorData],
second_sectors: Array[SectorData]
) -> void:
var size := first_sectors.size()
for i in range(size):
_connect_sectors(data, first_sectors, i, second_sectors, i)
for i in range(size - 1):
if _extra_passage_needed():
var is_passage_fliped := _is_extra_passage_flipped()
var from := i if is_passage_fliped else i + 1
var to := i + 1 if is_passage_fliped else i
_connect_sectors(data, first_sectors, from, second_sectors, to)
func _fill_passages_for_unequal_pair(
data : AreaData,
lesser_sectors: Array[SectorData],
greater_sectors: Array[SectorData],
connect_method: Callable
) -> void:
var lesser_size := lesser_sectors.size()
match lesser_size:
1:
_fill_passages_for_unequal_pair_1_to_2_3(
data, lesser_sectors, greater_sectors, connect_method
)
2:
_fill_passages_for_unequal_pair_2_to_3(
data, lesser_sectors, greater_sectors, connect_method
)
func _fill_passages_for_unequal_pair_1_to_2_3(
data : AreaData,
lesser_sectors: Array[SectorData],
greater_sectors: Array[SectorData],
connect_method: Callable
) -> void:
var greater_size := greater_sectors.size()
for i in range(greater_size):
connect_method.call(data, lesser_sectors, 0, greater_sectors, i)
func _fill_passages_for_unequal_pair_2_to_3(
data : AreaData,
lesser_sectors: Array[SectorData],
greater_sectors: Array[SectorData],
connect_method: Callable
) -> void:
connect_method.call(data, lesser_sectors, 0, greater_sectors, 0)
connect_method.call(data, lesser_sectors, 1, greater_sectors, 2)
if _extra_passage_needed():
connect_method.call(data, lesser_sectors, 0, greater_sectors, 1)
connect_method.call(data, lesser_sectors, 1, greater_sectors, 1)
else:
var from := 0 if _is_extra_passage_flipped() else 1
connect_method.call(data, lesser_sectors, from, greater_sectors, 1)
func _extra_passage_needed() -> bool:
return passage_chance_rng.randi_range(1, 100) <= EXTRA_PASSAGE_CHANCE
func _is_extra_passage_flipped() -> bool:
return passage_direction_rng.randi_range(0, 1) == 0
func _connect_sectors(
data : AreaData,
first_sectors: Array[SectorData],
first_index: int,
second_sectors: Array[SectorData],
second_index: int
) -> void:
var seed_value := passage_seed_rng.randi()
var passage := passage_generator.generate(seed_value)
passage.type = _get_passage_type(
first_sectors.size(), first_index,
second_sectors.size(), second_index,
)
var first_sector := first_sectors[first_index]
var second_sector := second_sectors[second_index]
passage.previous_sector = first_sector
passage.next_sector = second_sector
first_sector.next_passages.append(passage)
second_sector.previous_passages.append(passage)
data.passages.append(passage)
func _connect_sectors_flipped(
data : AreaData,
first_sectors: Array[SectorData],
first_index: int,
second_sectors: Array[SectorData],
second_index: int
) -> void:
_connect_sectors(data, second_sectors, second_index, first_sectors, first_index)
func _get_passage_type(
from_size: int, from_index: int, to_size: int, to_index: int
) -> PassageData.PassageType:
var diff := from_index - to_index
var key := "%d %d %d" % [from_size, to_size, diff]
return PASSAGE_TYPES.get(key, PassageData.PassageType.ZeroGrad)
func _update_neighbors(data : AreaData) -> void:
for stage_index in range(data.stages.size()):
var stage := data.stages[stage_index]
for sector_index in range(stage.sectors.size()):
var sector := stage.sectors[sector_index]
sector.sector_to_left = _get_left_neighbor(data, stage_index, sector_index)
sector.sector_to_right = _get_right_neighbor(data, stage_index, sector_index)
func _get_left_neighbor(data : AreaData, stage_index: int, sector_index: int) -> SectorData:
if stage_index == 0: return null
var current_stage := data.stages[stage_index]
var left_stage := data.stages[stage_index-1]
return _get_side_neighbor(sector_index, current_stage, left_stage)
func _get_right_neighbor(data : AreaData, stage_index: int, sector_index: int) -> SectorData:
if stage_index == data.stages.size() - 1: return null
var current_stage := data.stages[stage_index]
var right_stage := data.stages[stage_index+1]
return _get_side_neighbor(sector_index, current_stage, right_stage)
func _get_side_neighbor(
sector_index: int, current_stage: StageData, side_stage: StageData
) -> SectorData:
var current_size := current_stage.sectors.size()
var side_size := side_stage.sectors.size()
if side_size == 0:
return null
if current_size == 1:
return side_stage.sectors[floori(side_size / 2.0)]
var normalized_position := float(sector_index) / float(current_size - 1)
var side_index := floori(normalized_position * (side_size - 1))
side_index = clampi(side_index, 0, side_size - 1)
return side_stage.sectors[side_index]