Add research lab planets and research points (#63)
This commit is contained in:
parent
651f8b787e
commit
3f3a22227d
43 changed files with 459 additions and 30 deletions
|
|
@ -8,7 +8,13 @@ extends Node2D
|
|||
# ... and again at 720x960 to check a shorter screen format.
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var configured: bool = false
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if configured:
|
||||
return
|
||||
configured = true
|
||||
GameState.buildings.clear()
|
||||
GameState.extractors.clear()
|
||||
GameState.transporters.clear()
|
||||
|
|
@ -16,9 +22,12 @@ func _ready() -> void:
|
|||
GameState.gas = 36
|
||||
GameState.fleet_cargo = 24
|
||||
GameState.fleet_gas_cargo = 12
|
||||
GameState.unlocked = ["shipyards", "logistics", "freight"]
|
||||
GameState.research_points = 0
|
||||
GameState.unlocked = ["shipyards"]
|
||||
var main: Node = load("res://Main.tscn").instantiate()
|
||||
add_child(main)
|
||||
main.get_node("EnemySpawner").set_process(false)
|
||||
main.get_node("AsteroidField").set_process(false)
|
||||
main.get_node("BuildingField").place(GameState.BUILDING_MOTHERSHIP, Vector2(400.0, 0.0))
|
||||
var mothership_id: int = int(GameState.buildings[-1].id)
|
||||
var bc: Node = main.get_node("BuildController")
|
||||
|
|
@ -27,5 +36,5 @@ func _ready() -> void:
|
|||
main.get_node("UI/SkillMenu").visible = true
|
||||
# Select a node so the description, price and status all have text - an
|
||||
# empty description cannot collide with anything.
|
||||
bc.skill_tree.selected_id = "freight"
|
||||
bc._refresh_skill_menu("freight")
|
||||
bc.skill_tree.selected_id = "coordination"
|
||||
bc._refresh_skill_menu("coordination")
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ func _initialize() -> void:
|
|||
game_state.reset_progression()
|
||||
game_state.build_time_scale = 0.02
|
||||
game_state.rocks = 500
|
||||
game_state.research_points = 1
|
||||
scene = load("res://Main.tscn").instantiate()
|
||||
root.add_child(scene)
|
||||
fleet = scene.get_node("Fleet")
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ func _initialize() -> void:
|
|||
game_state.extractors.clear()
|
||||
game_state.transporters.clear()
|
||||
game_state.reset_progression()
|
||||
game_state.research_points = 1
|
||||
game_state.add_extractor("Test Planet", EXTRACTOR_AT)
|
||||
extractor_index = 0
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ func _initialize() -> void:
|
|||
game_state.fleet_cargo = 0
|
||||
game_state.fleet_gas_cargo = 0
|
||||
game_state.build_time_scale = 0.01
|
||||
game_state.research_points = 1
|
||||
# #60: after research/crafting resets the fleet hold below, top it up
|
||||
# with fleet-carried gas for the shot itself (ammunition, not the ore/gas
|
||||
# spent researching or crafting the bomber).
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ func _initialize() -> void:
|
|||
game.vessel_levels.clear()
|
||||
game.build_time_scale = 0.02
|
||||
game.rocks = 500
|
||||
game.research_points = 1
|
||||
lab_id = game.add_building(GameState.BUILDING_SHIP_LAB, Vector2.ZERO)
|
||||
if not game.queue_lab_research(lab_id, "int_damage"):
|
||||
_fail("could not queue research")
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ func _initialize() -> void:
|
|||
state.transporters.clear()
|
||||
state.reset_progression()
|
||||
state.rocks = 100
|
||||
state.research_points = 1
|
||||
state.build_time_scale = 0.02
|
||||
state.add_building(state.BUILDING_MOTHERSHIP, Vector2.ZERO)
|
||||
state.add_building(state.BUILDING_TRANSPORT_HUB, Vector2(50, 0))
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ func _initialize() -> void:
|
|||
state.reset_progression()
|
||||
state.rocks = 0
|
||||
state.gas = 10
|
||||
state.research_points = 1
|
||||
state.build_time_scale = 0.01
|
||||
|
||||
state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO)
|
||||
|
|
|
|||
143
tests/test_research_points.gd
Normal file
143
tests/test_research_points.gd
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
extends SceneTree
|
||||
|
||||
# Headless gameplay test: run with
|
||||
# godot --headless -s res://tests/test_research_points.gd
|
||||
# Forgejo #63: a rare enemy planet type that yields no resources but grants
|
||||
# one research point when conquered - a required currency for mid/late
|
||||
# skill-tree nodes and vessel/lab research, gated alongside their existing
|
||||
# ore/resource costs.
|
||||
#
|
||||
# Covers:
|
||||
# A. research labs generate deterministically and are rare
|
||||
# (structural) the definition yields no resources and its surface has no
|
||||
# resource provider at all
|
||||
# F. a fresh game can still reach a ship crafter and an extractor with
|
||||
# zero research points
|
||||
# D. a research-point-gated skill node is refused while unaffordable, and
|
||||
# explains why
|
||||
# E. conquering a research_lab awards exactly one point, once (guarded
|
||||
# even against a redundant re-mark), emits the signal once, and a
|
||||
# non-research_lab planet's conquest never awards one
|
||||
# D continued: unlocking spends the points and the balance updates
|
||||
# G. the ship-lab research path gates and spends research points the
|
||||
# same way, independent of the skill tree
|
||||
|
||||
var game_state: Node
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
game_state = root.get_node("GameState")
|
||||
|
||||
# --- A. deterministic and rare -------------------------------------------
|
||||
var lab_count: int = 0
|
||||
var total_planets: int = 0
|
||||
for x in range(-40, 40):
|
||||
for y in range(-40, 40):
|
||||
var chunk: Vector2i = Vector2i(x, y)
|
||||
if not WorldGen.has_planet_at_chunk(chunk):
|
||||
continue
|
||||
total_planets += 1
|
||||
var first: Dictionary = WorldGen.planet_data_at_chunk(chunk)
|
||||
var second: Dictionary = WorldGen.planet_data_at_chunk(chunk)
|
||||
if first.definition_id != second.definition_id or first.local_pos != second.local_pos:
|
||||
return _fail("planet_data_at_chunk(%s) is not deterministic" % chunk)
|
||||
if first.definition_id == "research_lab":
|
||||
lab_count += 1
|
||||
print("scanned %d planet chunks: %d research labs" % [total_planets, lab_count])
|
||||
if lab_count == 0:
|
||||
return _fail("no research labs generated across a wide scan - the rare outcome never fires")
|
||||
if lab_count * 3 >= total_planets:
|
||||
return _fail("research labs (%d of %d) are not rare" % [lab_count, total_planets])
|
||||
print("research labs are deterministic and rare (%d of %d planet chunks)" % [lab_count, total_planets])
|
||||
|
||||
# --- structural: yields nothing at all ------------------------------------
|
||||
var lab_def: Resource = load("res://PlanetDefinitions.gd").get_definition("research_lab")
|
||||
if String(lab_def.resource_id) != "":
|
||||
return _fail("research_lab planet's resource_id is '%s', expected empty" % lab_def.resource_id)
|
||||
var profile: Resource = load("res://PlanetSurfaceProfiles.gd").get_profile("research_lab")
|
||||
if profile.features_scene != null:
|
||||
return _fail("research_lab surface profile has a features_scene - it must yield nothing")
|
||||
print("research_lab yields no resource id and no surface feature provider")
|
||||
|
||||
# --- F. a fresh game still reaches a crafter and an extractor -------------
|
||||
game_state.reset_new_game()
|
||||
if not game_state.can_unlock("shipyards"):
|
||||
return _fail("shipyards is not reachable with 0 research points")
|
||||
game_state.unlock("shipyards")
|
||||
if not game_state.can_unlock("logistics"):
|
||||
return _fail("logistics is not reachable with 0 research points after shipyards")
|
||||
game_state.unlock("logistics")
|
||||
print("fresh game reaches a ship crafter (shipyards) and an extractor (logistics) at 0 research points")
|
||||
|
||||
# --- D. a points-gated node is refused and explains why -------------------
|
||||
if game_state.can_unlock("coordination"):
|
||||
return _fail("coordination (costs research points) was unlockable with 0 research points")
|
||||
print("a research-point-gated node (coordination) correctly refused at 0 points")
|
||||
|
||||
# --- E. earning exactly one point, once, with the signal ------------------
|
||||
var totals_seen: Array = []
|
||||
game_state.research_points_changed.connect(func(total): totals_seen.append(total))
|
||||
game_state.planet_capture.clear()
|
||||
game_state.register_capture_points("test_lab_planet", 2, "research_lab")
|
||||
if game_state.is_planet_conquered("test_lab_planet"):
|
||||
return _fail("a lab with 2 real capture points started conquered")
|
||||
game_state.mark_point_captured("test_lab_planet", 0)
|
||||
if game_state.research_points != 0:
|
||||
return _fail("a research point was awarded before every point was captured")
|
||||
game_state.mark_point_captured("test_lab_planet", 1)
|
||||
if game_state.research_points != 1:
|
||||
return _fail("conquering a research_lab did not award exactly 1 point: %d" % game_state.research_points)
|
||||
if totals_seen != [1]:
|
||||
return _fail("research_points_changed did not fire exactly once with the new total: %s" % [totals_seen])
|
||||
print("conquering a research_lab awarded exactly 1 point and fired the signal once")
|
||||
|
||||
game_state.mark_point_captured("test_lab_planet", 0) # already captured - redundant re-mark
|
||||
if game_state.research_points != 1:
|
||||
return _fail("re-marking an already-captured point re-awarded a research point: %d" % game_state.research_points)
|
||||
print("double-award guarded against")
|
||||
|
||||
game_state.register_capture_points("test_rocky_planet", 1, "rocky")
|
||||
game_state.mark_point_captured("test_rocky_planet", 0)
|
||||
if game_state.research_points != 1:
|
||||
return _fail("conquering a non-research_lab planet awarded a research point: %d" % game_state.research_points)
|
||||
print("a non-research_lab planet's conquest never awards a point")
|
||||
|
||||
# --- D continued: unlocking spends the points, balance updates ------------
|
||||
if not game_state.can_unlock("coordination"):
|
||||
return _fail("coordination is still not unlockable with 1 research point and enough ore")
|
||||
if not game_state.unlock("coordination"):
|
||||
return _fail("unlock(coordination) failed despite can_unlock() being true")
|
||||
if game_state.research_points != 0:
|
||||
return _fail("unlocking a 1-point node did not spend the point: %d remaining" % game_state.research_points)
|
||||
print("unlocking a research-point-gated skill node spent the point, balance now 0")
|
||||
|
||||
# --- G. the ship-lab research path gates and spends the same currency -----
|
||||
var carrier_def: Resource = game_state.vessel_definition(GameState.VESSEL_CARRIER)
|
||||
var point_cost: int = int(carrier_def.research_point_cost)
|
||||
if point_cost <= 0:
|
||||
return _fail("carrier production data has no research-point cost")
|
||||
game_state.vessel_levels.erase(carrier_def.unlock_id)
|
||||
var lab_id: int = game_state.add_building(GameState.BUILDING_SHIP_LAB, Vector2.ZERO)
|
||||
if not game_state.queue_lab_research(lab_id, carrier_def.unlock_id):
|
||||
return _fail("could not queue carrier research")
|
||||
game_state.deposit_resource_to_building(lab_id, GameState.RESOURCE_ORE, 1000)
|
||||
game_state._advance_lab_research(100.0)
|
||||
if game_state.vessel_is_unlocked(GameState.VESSEL_CARRIER):
|
||||
return _fail("carrier research completed despite 0 research points")
|
||||
print("lab research correctly stalled on a research-point shortfall")
|
||||
|
||||
game_state.research_points = point_cost
|
||||
game_state._advance_lab_research(100.0)
|
||||
if not game_state.vessel_is_unlocked(GameState.VESSEL_CARRIER):
|
||||
return _fail("carrier research did not complete once research points were available")
|
||||
if game_state.research_points != 0:
|
||||
return _fail("lab research did not spend the research points: %d remaining" % game_state.research_points)
|
||||
print("lab research spent the research points once affordable, and completed")
|
||||
|
||||
print("TEST_RESEARCH_POINTS: PASS")
|
||||
quit(0)
|
||||
|
||||
|
||||
func _fail(message: String) -> void:
|
||||
print("TEST_RESEARCH_POINTS: FAIL (%s)" % message)
|
||||
quit(1)
|
||||
|
|
@ -81,6 +81,7 @@ func _process(delta: float) -> bool:
|
|||
print("no lab -> no upgrades")
|
||||
|
||||
game_state.unlocked = ["shipyards", "vessel_science", "coordination", "wing", "squadron"]
|
||||
game_state.research_points = 10
|
||||
field.place(GameState.BUILDING_SHIP_LAB, Vector2(-700.0, 0.0))
|
||||
field.place(GameState.BUILDING_SHIP_CRAFTER, Vector2(700.0, 0.0))
|
||||
crafter_id = int(game_state.buildings[-1].id)
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ func _process(delta: float) -> bool:
|
|||
|
||||
# --- fleet capacity -------------------------------------------
|
||||
var base_cap: int = game_state.max_fleet_size()
|
||||
game_state.research_points = 2
|
||||
game_state.unlock("coordination")
|
||||
var wing_ok: bool = game_state.unlock("wing")
|
||||
var raised_cap: int = game_state.max_fleet_size()
|
||||
|
|
|
|||
Loading…
Reference in a new issue