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.
|
# Buy controls below them only describe the one the player tapped.
|
||||||
var lab_vessel: String = GameState.VESSEL_INTERCEPTOR
|
var lab_vessel: String = GameState.VESSEL_INTERCEPTOR
|
||||||
var lab_choice: int = 0
|
var lab_choice: int = 0
|
||||||
|
var lab_building_id: int = 0
|
||||||
@onready var planet_field: Node2D = get_node("../PlanetField")
|
@onready var planet_field: Node2D = get_node("../PlanetField")
|
||||||
@onready var info_popup: Control = get_node("../UI/BuildMenu/InfoPopup")
|
@onready var info_popup: Control = get_node("../UI/BuildMenu/InfoPopup")
|
||||||
@onready var info_title: Label = get_node("../UI/BuildMenu/InfoPopup/Panel/TitleLabel")
|
@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
|
craft_menu.visible = true
|
||||||
_refresh_craft_status()
|
_refresh_craft_status()
|
||||||
elif current_building.building_type == GameState.BUILDING_SHIP_LAB:
|
elif current_building.building_type == GameState.BUILDING_SHIP_LAB:
|
||||||
|
lab_building_id = current_building.building_id
|
||||||
lab_menu.visible = true
|
lab_menu.visible = true
|
||||||
_refresh_lab()
|
_refresh_lab()
|
||||||
elif current_building.building_type == GameState.BUILDING_COMMAND_CENTER:
|
elif current_building.building_type == GameState.BUILDING_COMMAND_CENTER:
|
||||||
|
|
@ -878,25 +880,11 @@ func _on_lab_buy() -> void:
|
||||||
if options.is_empty():
|
if options.is_empty():
|
||||||
return
|
return
|
||||||
var choice: Dictionary = options[lab_choice]
|
var choice: Dictionary = options[lab_choice]
|
||||||
if choice.unlock:
|
var id: String = GameState.UNLOCK_SHIELD_VESSEL if choice.unlock else String(choice.id)
|
||||||
if GameState.unlock_shield_vessel():
|
if not GameState.queue_lab_research(lab_building_id, id):
|
||||||
lab_status.text = "Shield vessel unlocked - build one at a ship crafter"
|
lab_status.text = "Research is already queued or complete"
|
||||||
else:
|
|
||||||
lab_status.text = "Not enough ore: needs %d, you have %d" % [
|
|
||||||
GameState.UNLOCK_SHIELD_COST, GameState.rocks
|
|
||||||
]
|
|
||||||
lab_choice = 0
|
|
||||||
_refresh_lab()
|
|
||||||
return
|
return
|
||||||
var id: String = choice.id
|
lab_status.text = "%s queued - deliver ore to this lab" % choice.name
|
||||||
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)]
|
|
||||||
_refresh_lab()
|
_refresh_lab()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -938,10 +926,10 @@ func _refresh_lab() -> void:
|
||||||
|
|
||||||
var choice: Dictionary = options[lab_choice]
|
var choice: Dictionary = options[lab_choice]
|
||||||
lab_detail.text = "%s\n%s" % [choice.name, choice.desc]
|
lab_detail.text = "%s\n%s" % [choice.name, choice.desc]
|
||||||
if choice.unlock:
|
var queued: Dictionary = GameState.lab_research_order(lab_building_id)
|
||||||
lab_buy_button.enabled = GameState.can_afford(GameState.UNLOCK_SHIELD_COST)
|
lab_buy_button.enabled = queued.is_empty() and (choice.unlock or not GameState.upgrade_maxed(choice.id))
|
||||||
else:
|
if not queued.is_empty():
|
||||||
lab_buy_button.enabled = GameState.can_buy_upgrade(choice.id)
|
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()
|
lab_buy_button.queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
50
GameState.gd
50
GameState.gd
|
|
@ -357,6 +357,7 @@ const BUILD_TIMES: Dictionary = {
|
||||||
VESSEL_SHIELD: 16.0,
|
VESSEL_SHIELD: 16.0,
|
||||||
VESSEL_TRANSPORTER: 8.0,
|
VESSEL_TRANSPORTER: 8.0,
|
||||||
}
|
}
|
||||||
|
const LAB_RESEARCH_TIME: float = 8.0
|
||||||
|
|
||||||
|
|
||||||
# Test seam. Headless tests need the timed path - queued, started, finishing
|
# 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:
|
func build_time(product: String) -> float:
|
||||||
return float(BUILD_TIMES.get(product, 10.0)) * build_time_scale
|
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
|
# 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
|
# 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.
|
# 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)
|
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:
|
func claim_stored_vessel(index: int) -> String:
|
||||||
# Hands one finished vessel over to be crewed. Returns its type, or ""
|
# Hands one finished vessel over to be crewed. Returns its type, or ""
|
||||||
# when there is nothing there - the caller decides whether the fleet
|
# 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
|
# Runs in the autoload, so mining and transport continue while the player
|
||||||
# is on a planet's surface or nowhere near either.
|
# is on a planet's surface or nowhere near either.
|
||||||
_advance_production(delta)
|
_advance_production(delta)
|
||||||
|
_advance_lab_research(delta)
|
||||||
_advance_vessel_deliveries(delta)
|
_advance_vessel_deliveries(delta)
|
||||||
for e in extractors:
|
for e in extractors:
|
||||||
e.mining_progress = float(e.get("mining_progress", 0.0)) + delta * EXTRACTOR_RATE
|
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://GameController.gd" id="1"]
|
||||||
[ext_resource type="Script" path="res://LunarBackground.gd" id="2"]
|
[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://TouchJoystick.gd" id="7"]
|
||||||
[ext_resource type="Script" path="res://RockField.gd" id="8"]
|
[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://ResourceCounter.gd" id="9"]
|
||||||
|
[ext_resource type="Script" path="res://GasVentField.gd" id="10"]
|
||||||
|
|
||||||
[sub_resource type="ShaderMaterial" id="1"]
|
[sub_resource type="ShaderMaterial" id="1"]
|
||||||
shader = ExtResource("3")
|
shader = ExtResource("3")
|
||||||
|
|
@ -48,6 +49,10 @@ zoom = Vector2(1, 1)
|
||||||
[node name="RockField" type="Node2D" parent="."]
|
[node name="RockField" type="Node2D" parent="."]
|
||||||
script = ExtResource("8")
|
script = ExtResource("8")
|
||||||
|
|
||||||
|
[node name="GasVentField" type="Node2D" parent="."]
|
||||||
|
visible = false
|
||||||
|
script = ExtResource("10")
|
||||||
|
|
||||||
[node name="SurfaceController" type="Node2D" parent="."]
|
[node name="SurfaceController" type="Node2D" parent="."]
|
||||||
script = ExtResource("5")
|
script = ExtResource("5")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,12 @@ var transitioning: bool = false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
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)
|
fleet.spawn_ships(GameState.surviving_ship_count, Vector2.ZERO)
|
||||||
camera.position = Vector2.ZERO
|
camera.position = Vector2.ZERO
|
||||||
camera.zoom = Vector2(GameState.return_camera_zoom, GameState.return_camera_zoom)
|
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 NOISE_SEED: int = 1337
|
||||||
const ASTEROID_FIELD_THRESHOLD: float = 0.15
|
const ASTEROID_FIELD_THRESHOLD: float = 0.15
|
||||||
const PLANET_THRESHOLD: float = 0.33
|
const PLANET_THRESHOLD: float = 0.33
|
||||||
|
const GAS_GIANT_THRESHOLD: float = 0.55
|
||||||
|
|
||||||
const PLANET_NAMES: Array = [
|
const PLANET_NAMES: Array = [
|
||||||
"Kepler's Rest", "Ceres Minor", "Vesta's Shadow", "Ironhold",
|
"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:
|
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))
|
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:
|
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()
|
var shape_seed: int = rng.randi()
|
||||||
# A stable per-chunk roll makes gas giants persistent landmarks rather
|
# A stable per-chunk roll makes gas giants persistent landmarks rather
|
||||||
# than a different resource whenever the scene reloads.
|
# 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:
|
if gas_giant:
|
||||||
planet_name = GAS_GIANT_NAMES[rng.randi() % GAS_GIANT_NAMES.size()]
|
planet_name = GAS_GIANT_NAMES[rng.randi() % GAS_GIANT_NAMES.size()]
|
||||||
return {"local_pos": local_pos, "name": planet_name, "shape_seed": shape_seed,
|
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