79 lines
3.2 KiB
GDScript
79 lines
3.2 KiB
GDScript
extends RefCounted
|
|
|
|
const POI := preload("res://scripts/levels/exploration_poi.gd")
|
|
|
|
static func spawn(game: Node2D, data: Dictionary, player: CharacterBody2D) -> void:
|
|
# Snapshot once per system. Learning a rank later must not add wrecks on reload.
|
|
if not GameState.exploration_wreck_bonuses.has(GameState.system_seed):
|
|
GameState.exploration_wreck_bonuses[GameState.system_seed] = roundi(GameState.captain.get_skill_bonus("explorer"))
|
|
var wreck_bonus := int(GameState.exploration_wreck_bonuses[GameState.system_seed])
|
|
var rng := RandomNumberGenerator.new()
|
|
rng.seed = GameState.system_seed ^ 0x504f49
|
|
var occupied: Array[Vector2] = []
|
|
var wreck_count := rng.randi_range(2, 4)
|
|
for index in wreck_count + rng.randi_range(6, 10):
|
|
var kind := "wreck" if index < wreck_count else "asteroid"
|
|
var candidate := Vector2.ZERO
|
|
var found := false
|
|
for attempt in 200:
|
|
candidate = Vector2(rng.randf_range(-2300, 2300), rng.randf_range(-1500, 1500))
|
|
if candidate.length() < 650 or candidate.distance_to(player.position) < 450:
|
|
continue
|
|
var clear := true
|
|
for planet in data["planets"]:
|
|
if candidate.distance_to(planet["position"]) < float(planet["radius"]) + 220:
|
|
clear = false
|
|
for point in occupied:
|
|
if candidate.distance_to(point) < 180:
|
|
clear = false
|
|
if clear:
|
|
found = true
|
|
break
|
|
if found:
|
|
occupied.append(candidate)
|
|
_add(game, player, kind, candidate, index, rng.randi())
|
|
# Rare per system, at most one ruin on an uninhabited planet.
|
|
if rng.randf() < 0.08:
|
|
var candidates: Array[int] = []
|
|
for index in data["planets"].size():
|
|
if not data["planets"][index]["protected"]:
|
|
candidates.append(index)
|
|
if not candidates.is_empty():
|
|
var index := candidates[rng.randi_range(0, candidates.size() - 1)]
|
|
var planet := game.get_node("CelestialBodies/Planet%d" % (index + 1))
|
|
_add(game, player, "ruin", planet.position, 100, rng.randi(), float(planet.get("radius")) + 100.0)
|
|
|
|
# Separate stream and IDs preserve the base POIs, asteroid count and ruin chance.
|
|
var bonus_rng := RandomNumberGenerator.new()
|
|
bonus_rng.seed = GameState.system_seed ^ 0x4558504c
|
|
for index in wreck_bonus:
|
|
for attempt in 1000:
|
|
var candidate := Vector2(bonus_rng.randf_range(-2300, 2300), bonus_rng.randf_range(-1500, 1500))
|
|
if candidate.length() < 650 or candidate.distance_to(player.position) < 450:
|
|
continue
|
|
var clear := true
|
|
for planet in data["planets"]:
|
|
if candidate.distance_to(planet["position"]) < float(planet["radius"]) + 220:
|
|
clear = false
|
|
for point in occupied:
|
|
if candidate.distance_to(point) < 180:
|
|
clear = false
|
|
if clear:
|
|
occupied.append(candidate)
|
|
_add(game, player, "wreck", candidate, 1000 + index, bonus_rng.randi())
|
|
break
|
|
|
|
static func _add(game: Node2D, player: CharacterBody2D, kind: String, position: Vector2, index: int, reward_seed: int, approach_radius: float = 115.0) -> void:
|
|
var id := "%d:%d" % [GameState.system_seed, index]
|
|
if GameState.explored_pois.has(id):
|
|
return
|
|
var poi := StaticBody2D.new()
|
|
poi.set_script(POI)
|
|
poi.set("kind", kind)
|
|
poi.set("poi_id", id)
|
|
poi.set("reward_seed", reward_seed)
|
|
poi.set("player", player)
|
|
poi.set("approach_radius", approach_radius)
|
|
poi.position = position
|
|
game.add_child(poi)
|