Key extractors by stable planet identity
This commit is contained in:
parent
14081ad230
commit
e2d4297ee5
11 changed files with 71 additions and 16 deletions
|
|
@ -563,7 +563,7 @@ func _show_building_info(type: String) -> void:
|
||||||
if planet == null:
|
if planet == null:
|
||||||
lines.append("No planet in reach - fly over one.")
|
lines.append("No planet in reach - fly over one.")
|
||||||
can_build = false
|
can_build = false
|
||||||
elif GameState.has_extractor_at(planet.planet_name):
|
elif GameState.has_extractor_at(planet.planet_id):
|
||||||
lines.append("%s already has an extractor." % planet.planet_name)
|
lines.append("%s already has an extractor." % planet.planet_name)
|
||||||
can_build = false
|
can_build = false
|
||||||
else:
|
else:
|
||||||
|
|
@ -606,13 +606,13 @@ func _confirm_build() -> void:
|
||||||
|
|
||||||
if definition.placement_mode == "planet":
|
if definition.placement_mode == "planet":
|
||||||
var planet: Node2D = _planet_under_fleet()
|
var planet: Node2D = _planet_under_fleet()
|
||||||
if planet == null or GameState.has_extractor_at(planet.planet_name):
|
if planet == null or GameState.has_extractor_at(planet.planet_id):
|
||||||
build_status.text = "No planet in reach any more"
|
build_status.text = "No planet in reach any more"
|
||||||
return
|
return
|
||||||
if not GameState.spend(definition.cost):
|
if not GameState.spend(definition.cost):
|
||||||
build_status.text = "Not enough rocks (need %d, have %d)" % [definition.cost, GameState.rocks]
|
build_status.text = "Not enough rocks (need %d, have %d)" % [definition.cost, GameState.rocks]
|
||||||
return
|
return
|
||||||
GameState.add_extractor(planet.planet_name, planet.position, planet.resource_type)
|
GameState.add_extractor(planet.planet_name, planet.position, planet.resource_type, planet.planet_id)
|
||||||
build_menu.visible = false
|
build_menu.visible = false
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
15
GameState.gd
15
GameState.gd
|
|
@ -18,6 +18,7 @@ var return_world_position: Vector2 = Vector2.ZERO
|
||||||
var return_camera_zoom: float = 1.0
|
var return_camera_zoom: float = 1.0
|
||||||
|
|
||||||
var planet_name: String = ""
|
var planet_name: String = ""
|
||||||
|
var planet_id: String = ""
|
||||||
var planet_definition_id: String = "rocky"
|
var planet_definition_id: String = "rocky"
|
||||||
|
|
||||||
# Explored-chunk history for the world map's fog-of-war reveal. Lives
|
# Explored-chunk history for the world map's fog-of-war reveal. Lives
|
||||||
|
|
@ -881,15 +882,16 @@ func rotate_formation(radians: float) -> void:
|
||||||
# Extractors mine into their OWN local stock. They do not own a magical
|
# Extractors mine into their OWN local stock. They do not own a magical
|
||||||
# hauler: ore becomes spendable only when a hub-stationed transporter picks
|
# hauler: ore becomes spendable only when a hub-stationed transporter picks
|
||||||
# up a full load and physically delivers it to the mothership. Entries are
|
# up a full load and physically delivers it to the mothership. Entries are
|
||||||
# {"position", "planet_name", "resource_type", "inventory", "mining_progress"}.
|
# {"position", "planet_id", "planet_name", "resource_type", "inventory", "mining_progress"}.
|
||||||
const EXTRACTOR_RATE: float = 2.0 # ore per second
|
const EXTRACTOR_RATE: float = 2.0 # ore per second
|
||||||
|
|
||||||
var extractors: Array = []
|
var extractors: Array = []
|
||||||
|
|
||||||
|
|
||||||
func has_extractor_at(planet_name: String) -> bool:
|
func has_extractor_at(planet_key: String) -> bool:
|
||||||
for e in extractors:
|
for e in extractors:
|
||||||
if e.planet_name == planet_name:
|
# Old saves have no planet_id; their name remains a migration fallback.
|
||||||
|
if String(e.get("planet_id", e.get("planet_name", ""))) == planet_key:
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
@ -897,9 +899,12 @@ func has_extractor_at(planet_name: String) -> bool:
|
||||||
signal extractor_added(planet_name: String)
|
signal extractor_added(planet_name: String)
|
||||||
|
|
||||||
|
|
||||||
func add_extractor(planet_name: String, at_position: Vector2, resource_type: String = "ore") -> void:
|
func add_extractor(planet_name: String, at_position: Vector2, resource_type: String = "ore",
|
||||||
|
planet_key: String = "") -> void:
|
||||||
|
var stable_id: String = planet_key if planet_key != "" else planet_name
|
||||||
extractors.append({
|
extractors.append({
|
||||||
"position": at_position,
|
"position": at_position,
|
||||||
|
"planet_id": stable_id,
|
||||||
"planet_name": planet_name,
|
"planet_name": planet_name,
|
||||||
"resource_type": resource_type,
|
"resource_type": resource_type,
|
||||||
"inventory": INVENTORY.new(),
|
"inventory": INVENTORY.new(),
|
||||||
|
|
@ -909,7 +914,7 @@ func add_extractor(planet_name: String, at_position: Vector2, resource_type: Str
|
||||||
# so the Planet node is already in the tree with its _ready() long since
|
# so the Planet node is already in the tree with its _ready() long since
|
||||||
# run. Without this it kept showing no derrick until it happened to
|
# run. Without this it kept showing no derrick until it happened to
|
||||||
# despawn and respawn, which reads as the build having done nothing.
|
# despawn and respawn, which reads as the build having done nothing.
|
||||||
extractor_added.emit(planet_name)
|
extractor_added.emit(stable_id)
|
||||||
|
|
||||||
|
|
||||||
func extractor_stock(index: int) -> int:
|
func extractor_stock(index: int) -> int:
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ var progress: float = 0.0
|
||||||
var emission_phase: float = 0.0
|
var emission_phase: float = 0.0
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
var rng := RandomNumberGenerator.new()
|
var rng := RandomNumberGenerator.new()
|
||||||
rng.seed = hash(GameState.planet_name)
|
rng.seed = hash(GameState.planet_id if GameState.planet_id != "" else GameState.planet_name)
|
||||||
for i in range(6): vents.append(Vector2(rng.randf_range(-800,800), rng.randf_range(-650,650)))
|
for i in range(6): vents.append(Vector2(rng.randf_range(-800,800), rng.randf_range(-650,650)))
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ const PLANET_DEFINITIONS = preload("res://PlanetDefinitions.gd")
|
||||||
# while the fleet as a whole was clearly sitting on the planet.
|
# while the fleet as a whole was clearly sitting on the planet.
|
||||||
|
|
||||||
@export var planet_name: String = ""
|
@export var planet_name: String = ""
|
||||||
|
@export var planet_id: String = ""
|
||||||
@export var definition_id: String = PLANET_DEFINITIONS.DEFAULT_DEFINITION_ID
|
@export var definition_id: String = PLANET_DEFINITIONS.DEFAULT_DEFINITION_ID
|
||||||
@export var landing_radius: float = 140.0
|
@export var landing_radius: float = 140.0
|
||||||
# Set by whoever spawns this planet (PlanetField, seeded from the chunk's
|
# Set by whoever spawns this planet (PlanetField, seeded from the chunk's
|
||||||
|
|
@ -67,7 +68,9 @@ func _ready() -> void:
|
||||||
visual = definition.visual_scene.instantiate()
|
visual = definition.visual_scene.instantiate()
|
||||||
visual.configure(shape_seed)
|
visual.configure(shape_seed)
|
||||||
get_node("VisualHost").add_child(visual)
|
get_node("VisualHost").add_child(visual)
|
||||||
shows_extractor = GameState.has_extractor_at(planet_name)
|
if planet_id == "":
|
||||||
|
planet_id = planet_name
|
||||||
|
shows_extractor = GameState.has_extractor_at(planet_id)
|
||||||
GameState.extractor_added.connect(_on_extractor_added)
|
GameState.extractor_added.connect(_on_extractor_added)
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
||||||
|
|
@ -75,7 +78,7 @@ func _ready() -> void:
|
||||||
func _on_extractor_added(built_on: String) -> void:
|
func _on_extractor_added(built_on: String) -> void:
|
||||||
# Only the planet it was actually built on, and only a redraw when the
|
# Only the planet it was actually built on, and only a redraw when the
|
||||||
# answer really changed - every other planet in range gets this too.
|
# answer really changed - every other planet in range gets this too.
|
||||||
if built_on != planet_name or shows_extractor:
|
if built_on != planet_id or shows_extractor:
|
||||||
return
|
return
|
||||||
shows_extractor = true
|
shows_extractor = true
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ func _on_enter_pressed() -> void:
|
||||||
GameState.return_world_position = current_planet.position
|
GameState.return_world_position = current_planet.position
|
||||||
GameState.return_camera_zoom = camera.zoom.x
|
GameState.return_camera_zoom = camera.zoom.x
|
||||||
GameState.planet_name = current_planet.planet_name
|
GameState.planet_name = current_planet.planet_name
|
||||||
|
GameState.planet_id = current_planet.planet_id
|
||||||
GameState.planet_definition_id = current_planet.definition_id
|
GameState.planet_definition_id = current_planet.definition_id
|
||||||
|
|
||||||
var tween: Tween = create_tween()
|
var tween: Tween = create_tween()
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ func _spawn_chunk_planet(chunk: Vector2i) -> void:
|
||||||
var planet: Node2D = planet_scene.instantiate()
|
var planet: Node2D = planet_scene.instantiate()
|
||||||
planet.position = origin + data.local_pos
|
planet.position = origin + data.local_pos
|
||||||
planet.planet_name = data.name
|
planet.planet_name = data.name
|
||||||
|
planet.planet_id = data.planet_id
|
||||||
planet.shape_seed = data.shape_seed
|
planet.shape_seed = data.shape_seed
|
||||||
planet.definition_id = String(data.get("definition_id", "rocky"))
|
planet.definition_id = String(data.get("definition_id", "rocky"))
|
||||||
# Deferred: update_chunks() can run synchronously from inside a
|
# Deferred: update_chunks() can run synchronously from inside a
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,8 @@ static func planet_data_at_chunk(chunk: Vector2i) -> Dictionary:
|
||||||
var definition: Resource = PLANET_DEFINITIONS.get_definition(definition_id)
|
var definition: Resource = PLANET_DEFINITIONS.get_definition(definition_id)
|
||||||
var names: PackedStringArray = definition.names
|
var names: PackedStringArray = definition.names
|
||||||
var planet_name: String = names[rng.randi() % names.size()]
|
var planet_name: String = names[rng.randi() % names.size()]
|
||||||
return {"local_pos": local_pos, "name": planet_name, "shape_seed": shape_seed,
|
var planet_id: String = "chunk:%d:%d" % [chunk.x, chunk.y]
|
||||||
|
return {"planet_id": planet_id, "local_pos": local_pos, "name": planet_name, "shape_seed": shape_seed,
|
||||||
"definition_id": definition_id}
|
"definition_id": definition_id}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ func building_markers() -> Array:
|
||||||
func chunk_has_extractor_badge(chunk: Vector2i) -> bool:
|
func chunk_has_extractor_badge(chunk: Vector2i) -> bool:
|
||||||
if not WorldGen.has_planet_at_chunk(chunk):
|
if not WorldGen.has_planet_at_chunk(chunk):
|
||||||
return false
|
return false
|
||||||
return GameState.has_extractor_at(WorldGen.planet_data_at_chunk(chunk).name)
|
return GameState.has_extractor_at(WorldGen.planet_data_at_chunk(chunk).planet_id)
|
||||||
|
|
||||||
|
|
||||||
func _regular_polygon(center: Vector2, radius: float, sides: int, phase: float = 0.0) -> PackedVector2Array:
|
func _regular_polygon(center: Vector2, radius: float, sides: int, phase: float = 0.0) -> PackedVector2Array:
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ func _process(delta: float) -> bool:
|
||||||
return true
|
return true
|
||||||
controller._confirm_build()
|
controller._confirm_build()
|
||||||
|
|
||||||
var built: bool = game_state.has_extractor_at(planet2.planet_name)
|
var built: bool = game_state.has_extractor_at(planet2.planet_id)
|
||||||
var charged: bool = game_state.rocks == rocks_before - GameState.EXTRACTOR_COST
|
var charged: bool = game_state.rocks == rocks_before - GameState.EXTRACTOR_COST
|
||||||
print("over planet '%s': enabled=%s built=%s rocks %d->%d charged=%s" % [
|
print("over planet '%s': enabled=%s built=%s rocks %d->%d charged=%s" % [
|
||||||
planet2.planet_name, button.enabled, built, rocks_before, game_state.rocks, charged
|
planet2.planet_name, button.enabled, built, rocks_before, game_state.rocks, charged
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ var scene: Node
|
||||||
var map_content: Node2D
|
var map_content: Node2D
|
||||||
var planet_chunk: Vector2i
|
var planet_chunk: Vector2i
|
||||||
var planet_name: String = ""
|
var planet_name: String = ""
|
||||||
|
var planet_id: String = ""
|
||||||
var stage: int = 0
|
var stage: int = 0
|
||||||
var already_spawned: Node2D
|
var already_spawned: Node2D
|
||||||
|
|
||||||
|
|
@ -43,7 +44,9 @@ func _initialize() -> void:
|
||||||
var chunk: Vector2i = Vector2i(x, y)
|
var chunk: Vector2i = Vector2i(x, y)
|
||||||
if WorldGen.has_planet_at_chunk(chunk):
|
if WorldGen.has_planet_at_chunk(chunk):
|
||||||
planet_chunk = chunk
|
planet_chunk = chunk
|
||||||
planet_name = WorldGen.planet_data_at_chunk(chunk).name
|
var data: Dictionary = WorldGen.planet_data_at_chunk(chunk)
|
||||||
|
planet_name = data.name
|
||||||
|
planet_id = data.planet_id
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
if found:
|
if found:
|
||||||
|
|
@ -61,6 +64,7 @@ func _initialize() -> void:
|
||||||
func _spawn_planet() -> Node2D:
|
func _spawn_planet() -> Node2D:
|
||||||
var planet: Node2D = load("res://Planet.tscn").instantiate()
|
var planet: Node2D = load("res://Planet.tscn").instantiate()
|
||||||
planet.planet_name = planet_name
|
planet.planet_name = planet_name
|
||||||
|
planet.planet_id = planet_id
|
||||||
planet.shape_seed = 12345
|
planet.shape_seed = 12345
|
||||||
scene.add_child(planet)
|
scene.add_child(planet)
|
||||||
return planet
|
return planet
|
||||||
|
|
@ -90,7 +94,7 @@ func _process(_delta: float) -> bool:
|
||||||
# having done nothing at all. Spawning a fresh planet afterwards
|
# having done nothing at all. Spawning a fresh planet afterwards
|
||||||
# (as the checks below do) cannot catch that.
|
# (as the checks below do) cannot catch that.
|
||||||
already_spawned = clean
|
already_spawned = clean
|
||||||
game_state.add_extractor(planet_name, WorldGen.chunk_origin(planet_chunk))
|
game_state.add_extractor(planet_name, WorldGen.chunk_origin(planet_chunk), "ore", planet_id)
|
||||||
stage = 1
|
stage = 1
|
||||||
return false
|
return false
|
||||||
1:
|
1:
|
||||||
|
|
@ -141,6 +145,7 @@ func _process(_delta: float) -> bool:
|
||||||
# by name, and getting that wrong would light up every planet.
|
# by name, and getting that wrong would light up every planet.
|
||||||
var other: Node2D = load("res://Planet.tscn").instantiate()
|
var other: Node2D = load("res://Planet.tscn").instantiate()
|
||||||
other.planet_name = planet_name + " II"
|
other.planet_name = planet_name + " II"
|
||||||
|
other.planet_id = planet_id + ":other"
|
||||||
other.shape_seed = 999
|
other.shape_seed = 999
|
||||||
scene.add_child(other)
|
scene.add_child(other)
|
||||||
if other.shows_extractor:
|
if other.shows_extractor:
|
||||||
|
|
|
||||||
39
tests/test_planet_unique_identity.gd
Normal file
39
tests/test_planet_unique_identity.gd
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
var state: Node = root.get_node("GameState")
|
||||||
|
state.extractors.clear()
|
||||||
|
var first_by_name: Dictionary = {}
|
||||||
|
var first: Dictionary = {}
|
||||||
|
var second: Dictionary = {}
|
||||||
|
for x in range(-60, 61):
|
||||||
|
for y in range(-60, 61):
|
||||||
|
var chunk := Vector2i(x, y)
|
||||||
|
if not WorldGen.has_planet_at_chunk(chunk):
|
||||||
|
continue
|
||||||
|
var data: Dictionary = WorldGen.planet_data_at_chunk(chunk)
|
||||||
|
if first_by_name.has(data.name):
|
||||||
|
first = first_by_name[data.name]
|
||||||
|
second = data
|
||||||
|
break
|
||||||
|
first_by_name[data.name] = data
|
||||||
|
if not second.is_empty():
|
||||||
|
break
|
||||||
|
if second.is_empty():
|
||||||
|
return _fail("could not find two generated planets sharing a display name")
|
||||||
|
if first.planet_id == second.planet_id:
|
||||||
|
return _fail("duplicate display names also shared a stable id")
|
||||||
|
state.add_extractor(first.name, Vector2.ZERO, "ore", first.planet_id)
|
||||||
|
if not state.has_extractor_at(first.planet_id):
|
||||||
|
return _fail("extractor was not attached to its planet id")
|
||||||
|
if state.has_extractor_at(second.planet_id):
|
||||||
|
return _fail("'%s' inherited an extractor from its namesake" % second.name)
|
||||||
|
print("same name '%s', distinct ids %s and %s" % [first.name, first.planet_id, second.planet_id])
|
||||||
|
print("TEST_PLANET_UNIQUE_IDENTITY: PASS")
|
||||||
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
|
func _fail(message: String) -> void:
|
||||||
|
print("TEST_PLANET_UNIQUE_IDENTITY: FAIL (%s)" % message)
|
||||||
|
quit(1)
|
||||||
Loading…
Reference in a new issue