Queue lab research from local stock
This commit is contained in:
parent
798deb48a1
commit
e9acca569e
7 changed files with 137 additions and 27 deletions
|
|
@ -98,6 +98,7 @@ extends Node2D
|
|||
# Buy controls below them only describe the one the player tapped.
|
||||
var lab_vessel: String = GameState.VESSEL_INTERCEPTOR
|
||||
var lab_choice: int = 0
|
||||
var lab_building_id: int = 0
|
||||
@onready var planet_field: Node2D = get_node("../PlanetField")
|
||||
@onready var info_popup: Control = get_node("../UI/BuildMenu/InfoPopup")
|
||||
@onready var info_title: Label = get_node("../UI/BuildMenu/InfoPopup/Panel/TitleLabel")
|
||||
|
|
@ -488,6 +489,7 @@ func _on_action_pressed() -> void:
|
|||
craft_menu.visible = true
|
||||
_refresh_craft_status()
|
||||
elif current_building.building_type == GameState.BUILDING_SHIP_LAB:
|
||||
lab_building_id = current_building.building_id
|
||||
lab_menu.visible = true
|
||||
_refresh_lab()
|
||||
elif current_building.building_type == GameState.BUILDING_COMMAND_CENTER:
|
||||
|
|
@ -878,25 +880,11 @@ func _on_lab_buy() -> void:
|
|||
if options.is_empty():
|
||||
return
|
||||
var choice: Dictionary = options[lab_choice]
|
||||
if choice.unlock:
|
||||
if GameState.unlock_shield_vessel():
|
||||
lab_status.text = "Shield vessel unlocked - build one at a ship crafter"
|
||||
else:
|
||||
lab_status.text = "Not enough ore: needs %d, you have %d" % [
|
||||
GameState.UNLOCK_SHIELD_COST, GameState.rocks
|
||||
]
|
||||
lab_choice = 0
|
||||
_refresh_lab()
|
||||
var id: String = GameState.UNLOCK_SHIELD_VESSEL if choice.unlock else String(choice.id)
|
||||
if not GameState.queue_lab_research(lab_building_id, id):
|
||||
lab_status.text = "Research is already queued or complete"
|
||||
return
|
||||
var id: String = choice.id
|
||||
if GameState.upgrade_maxed(id):
|
||||
lab_status.text = "%s is already fully researched" % choice.name
|
||||
return
|
||||
var cost: int = GameState.upgrade_cost(id)
|
||||
if not GameState.buy_upgrade(id):
|
||||
lab_status.text = "Not enough ore: needs %d, you have %d" % [cost, GameState.rocks]
|
||||
return
|
||||
lab_status.text = "%s is now level %d" % [choice.name, GameState.upgrade_level(id)]
|
||||
lab_status.text = "%s queued - deliver ore to this lab" % choice.name
|
||||
_refresh_lab()
|
||||
|
||||
|
||||
|
|
@ -938,10 +926,10 @@ func _refresh_lab() -> void:
|
|||
|
||||
var choice: Dictionary = options[lab_choice]
|
||||
lab_detail.text = "%s\n%s" % [choice.name, choice.desc]
|
||||
if choice.unlock:
|
||||
lab_buy_button.enabled = GameState.can_afford(GameState.UNLOCK_SHIELD_COST)
|
||||
else:
|
||||
lab_buy_button.enabled = GameState.can_buy_upgrade(choice.id)
|
||||
var queued: Dictionary = GameState.lab_research_order(lab_building_id)
|
||||
lab_buy_button.enabled = queued.is_empty() and (choice.unlock or not GameState.upgrade_maxed(choice.id))
|
||||
if not queued.is_empty():
|
||||
lab_status.text = "%s %d%%" % ["Researching" if bool(queued.started) else "Waiting for ore", int(100.0 * float(queued.elapsed) / maxf(GameState.lab_research_time(), 0.001))]
|
||||
lab_buy_button.queue_redraw()
|
||||
|
||||
|
||||
|
|
|
|||
50
GameState.gd
50
GameState.gd
|
|
@ -357,6 +357,7 @@ const BUILD_TIMES: Dictionary = {
|
|||
VESSEL_SHIELD: 16.0,
|
||||
VESSEL_TRANSPORTER: 8.0,
|
||||
}
|
||||
const LAB_RESEARCH_TIME: float = 8.0
|
||||
|
||||
|
||||
# Test seam. Headless tests need the timed path - queued, started, finishing
|
||||
|
|
@ -368,6 +369,10 @@ var build_time_scale: float = 1.0
|
|||
func build_time(product: String) -> float:
|
||||
return float(BUILD_TIMES.get(product, 10.0)) * build_time_scale
|
||||
|
||||
|
||||
func lab_research_time() -> float:
|
||||
return LAB_RESEARCH_TIME * build_time_scale
|
||||
|
||||
# Researched at the ship lab, per vessel type. Separate from SKILL_TREE on
|
||||
# purpose: that gates what the player may build at all and is a strategic
|
||||
# spine, while these are repeatable tuning on a vessel you already have.
|
||||
|
|
@ -712,6 +717,50 @@ func _advance_production(delta: float) -> void:
|
|||
order_completed.emit(int(b.id), product)
|
||||
|
||||
|
||||
func queue_lab_research(lab_id: int, upgrade_id: String) -> bool:
|
||||
var lab: Dictionary = find_building(lab_id)
|
||||
if lab.is_empty() or lab.type != BUILDING_SHIP_LAB or upgrade_id == "":
|
||||
return false
|
||||
var orders: Array = lab.get("research_orders", [])
|
||||
if not orders.is_empty() or (upgrade_id != UNLOCK_SHIELD_VESSEL and upgrade_maxed(upgrade_id)):
|
||||
return false
|
||||
orders.append({"id": upgrade_id, "elapsed": 0.0, "started": false})
|
||||
lab.research_orders = orders
|
||||
return true
|
||||
|
||||
|
||||
func lab_research_order(lab_id: int) -> Dictionary:
|
||||
var lab: Dictionary = find_building(lab_id)
|
||||
var orders: Array = lab.get("research_orders", [])
|
||||
return {} if orders.is_empty() else orders[0]
|
||||
|
||||
|
||||
func _advance_lab_research(delta: float) -> void:
|
||||
for lab in buildings:
|
||||
if lab.type != BUILDING_SHIP_LAB:
|
||||
continue
|
||||
var orders: Array = lab.get("research_orders", [])
|
||||
if orders.is_empty():
|
||||
continue
|
||||
var order: Dictionary = orders[0]
|
||||
var id: String = String(order.id)
|
||||
var cost: int = UNLOCK_SHIELD_COST if id == UNLOCK_SHIELD_VESSEL else upgrade_cost(id)
|
||||
if not bool(order.started):
|
||||
if int(lab.get("stock", 0)) < cost:
|
||||
continue
|
||||
lab.stock = int(lab.stock) - cost
|
||||
order.started = true
|
||||
order.elapsed = float(order.elapsed) + delta
|
||||
if float(order.elapsed) < lab_research_time():
|
||||
continue
|
||||
if id == UNLOCK_SHIELD_VESSEL:
|
||||
vessel_levels[id] = 1
|
||||
else:
|
||||
vessel_levels[id] = upgrade_level(id) + 1
|
||||
vessel_upgraded.emit(id)
|
||||
orders.pop_front()
|
||||
|
||||
|
||||
func claim_stored_vessel(index: int) -> String:
|
||||
# Hands one finished vessel over to be crewed. Returns its type, or ""
|
||||
# when there is nothing there - the caller decides whether the fleet
|
||||
|
|
@ -829,6 +878,7 @@ func _process(delta: float) -> void:
|
|||
# Runs in the autoload, so mining and transport continue while the player
|
||||
# is on a planet's surface or nowhere near either.
|
||||
_advance_production(delta)
|
||||
_advance_lab_research(delta)
|
||||
_advance_vessel_deliveries(delta)
|
||||
for e in extractors:
|
||||
e.mining_progress = float(e.get("mining_progress", 0.0)) + delta * EXTRACTOR_RATE
|
||||
|
|
|
|||
21
GasVentField.gd
Normal file
21
GasVentField.gd
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
extends Node2D
|
||||
@onready var fleet: Node2D = get_node("../Fleet")
|
||||
var vents: Array[Vector2] = []
|
||||
var progress: float = 0.0
|
||||
func _ready() -> void:
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.seed = hash(GameState.planet_name)
|
||||
for i in range(6): vents.append(Vector2(rng.randf_range(-800,800), rng.randf_range(-650,650)))
|
||||
queue_redraw()
|
||||
func _process(delta: float) -> void:
|
||||
for vent in vents:
|
||||
if fleet.get_centroid().distance_to(vent) < 90.0:
|
||||
progress += delta
|
||||
if progress >= 1.0:
|
||||
GameState.gas += int(progress)
|
||||
progress = fmod(progress, 1.0)
|
||||
return
|
||||
progress = 0.0
|
||||
func _draw() -> void:
|
||||
for vent in vents:
|
||||
NeonDraw.draw_glowing_polygon(self, PackedVector2Array([vent+Vector2(0,-44),vent+Vector2(38,-22),vent+Vector2(38,22),vent+Vector2(0,44),vent+Vector2(-38,22),vent+Vector2(-38,-22)]), Color(0.85,0.52,1),0.8,Color(0.15,0.03,0.22),0.5)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=11 format=3]
|
||||
[gd_scene load_steps=12 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://GameController.gd" id="1"]
|
||||
[ext_resource type="Script" path="res://LunarBackground.gd" id="2"]
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
[ext_resource type="Script" path="res://TouchJoystick.gd" id="7"]
|
||||
[ext_resource type="Script" path="res://RockField.gd" id="8"]
|
||||
[ext_resource type="Script" path="res://ResourceCounter.gd" id="9"]
|
||||
[ext_resource type="Script" path="res://GasVentField.gd" id="10"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="1"]
|
||||
shader = ExtResource("3")
|
||||
|
|
@ -48,6 +49,10 @@ zoom = Vector2(1, 1)
|
|||
[node name="RockField" type="Node2D" parent="."]
|
||||
script = ExtResource("8")
|
||||
|
||||
[node name="GasVentField" type="Node2D" parent="."]
|
||||
visible = false
|
||||
script = ExtResource("10")
|
||||
|
||||
[node name="SurfaceController" type="Node2D" parent="."]
|
||||
script = ExtResource("5")
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ var transitioning: bool = false
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
if GameState.planet_type == "gas_giant":
|
||||
# A gas giant is not lunar terrain: its surface view uses a deep,
|
||||
# coloured atmosphere instead of the rocky field.
|
||||
get_node("../RockField").visible = false
|
||||
get_node("../GasVentField").visible = true
|
||||
get_node("../BackgroundLayer/LunarBackground").color = Color(0.16, 0.035, 0.26, 1.0)
|
||||
fleet.spawn_ships(GameState.surviving_ship_count, Vector2.ZERO)
|
||||
camera.position = Vector2.ZERO
|
||||
camera.zoom = Vector2(GameState.return_camera_zoom, GameState.return_camera_zoom)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const CHUNK_SIZE: float = 1200.0
|
|||
const NOISE_SEED: int = 1337
|
||||
const ASTEROID_FIELD_THRESHOLD: float = 0.15
|
||||
const PLANET_THRESHOLD: float = 0.33
|
||||
const GAS_GIANT_THRESHOLD: float = 0.55
|
||||
|
||||
const PLANET_NAMES: Array = [
|
||||
"Kepler's Rest", "Ceres Minor", "Vesta's Shadow", "Ironhold",
|
||||
|
|
@ -61,10 +62,8 @@ static func biome_at_chunk(chunk: Vector2i) -> int:
|
|||
|
||||
|
||||
static func has_planet_at_chunk(chunk: Vector2i) -> bool:
|
||||
if biome_at_chunk(chunk) != Biome.ASTEROID_FIELD:
|
||||
return false
|
||||
var n: float = _get_planet_noise().get_noise_2d(float(chunk.x), float(chunk.y))
|
||||
return n > PLANET_THRESHOLD
|
||||
return (biome_at_chunk(chunk) == Biome.ASTEROID_FIELD and n > PLANET_THRESHOLD) or (biome_at_chunk(chunk) == Biome.SPACE and n > GAS_GIANT_THRESHOLD)
|
||||
|
||||
|
||||
static func planet_data_at_chunk(chunk: Vector2i) -> Dictionary:
|
||||
|
|
@ -82,7 +81,7 @@ static func planet_data_at_chunk(chunk: Vector2i) -> Dictionary:
|
|||
var shape_seed: int = rng.randi()
|
||||
# A stable per-chunk roll makes gas giants persistent landmarks rather
|
||||
# than a different resource whenever the scene reloads.
|
||||
var gas_giant: bool = rng.randf() < 0.28
|
||||
var gas_giant: bool = biome_at_chunk(chunk) == Biome.SPACE
|
||||
if gas_giant:
|
||||
planet_name = GAS_GIANT_NAMES[rng.randi() % GAS_GIANT_NAMES.size()]
|
||||
return {"local_pos": local_pos, "name": planet_name, "shape_seed": shape_seed,
|
||||
|
|
|
|||
41
tests/test_lab_resources.gd
Normal file
41
tests/test_lab_resources.gd
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
extends SceneTree
|
||||
|
||||
var game: Node
|
||||
var lab_id: int
|
||||
var elapsed := 0.0
|
||||
|
||||
func _initialize() -> void:
|
||||
game = root.get_node("GameState")
|
||||
game.buildings.clear()
|
||||
game.vessel_levels.clear()
|
||||
game.build_time_scale = 0.02
|
||||
game.rocks = 500
|
||||
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")
|
||||
|
||||
func _process(delta: float) -> bool:
|
||||
elapsed += delta
|
||||
if elapsed < 0.2:
|
||||
return false
|
||||
var order: Dictionary = game.lab_research_order(lab_id)
|
||||
if not order.is_empty() and not bool(order.started):
|
||||
if game.rocks != 500:
|
||||
return _fail("research drew from mothership bank")
|
||||
game.deposit_to_building(lab_id, game.upgrade_cost("int_damage"))
|
||||
return false
|
||||
if game.upgrade_level("int_damage") == 1:
|
||||
if game.building_stock(lab_id) != 0 or game.rocks != 500:
|
||||
return _fail("local lab stock/payment was wrong")
|
||||
print("lab research consumed local ore and completed over time")
|
||||
print("TEST_LAB_RESOURCES: PASS")
|
||||
quit(0)
|
||||
return true
|
||||
if elapsed > 3.0:
|
||||
return _fail("research did not complete")
|
||||
return false
|
||||
|
||||
func _fail(message: String) -> bool:
|
||||
print("TEST_LAB_RESOURCES: FAIL (%s)" % message)
|
||||
quit(1)
|
||||
return true
|
||||
Loading…
Reference in a new issue