Resolve pending gameplay tickets
This commit is contained in:
parent
ec20778215
commit
c2ebbfaa2d
28 changed files with 618 additions and 188 deletions
|
|
@ -11,6 +11,8 @@ func _ready() -> void:
|
|||
GameState.extractors.clear()
|
||||
GameState.vessel_levels["unlock_laser"] = 1
|
||||
GameState.vessel_levels["unlock_gas_bomber"] = 1
|
||||
GameState.vessel_levels["unlock_distorder"] = 1
|
||||
GameState.unlocked.append("wing")
|
||||
GameState.deposit_fleet_resource(GameState.RESOURCE_ENERGY, 200)
|
||||
GameState.deposit_fleet_resource(GameState.RESOURCE_GAS, 200)
|
||||
var main: Node = load("res://world/Main.tscn").instantiate()
|
||||
|
|
@ -18,10 +20,12 @@ func _ready() -> void:
|
|||
main.get_node("EnemySpawner").set_process(false)
|
||||
main.get_node("AsteroidField").set_process(false)
|
||||
var fleet: Node = main.get_node("Fleet")
|
||||
fleet.spawn_ships(3, Vector2.ZERO, PackedStringArray([
|
||||
GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_LASER, GameState.VESSEL_GAS_BOMBER]))
|
||||
fleet.spawn_ships(4, Vector2.ZERO, PackedStringArray([
|
||||
GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_LASER,
|
||||
GameState.VESSEL_GAS_BOMBER, GameState.VESSEL_DISTORDER]))
|
||||
main.get_node("LaserController")._process(0.0)
|
||||
main.get_node("GasBombController")._process(0.0)
|
||||
main.get_node("DashController")._process(0.0)
|
||||
# Push the fire stick, so its knob shows deflected the way it looks in use.
|
||||
var stick: Control = main.get_node("UI/FireStick")
|
||||
var press := InputEventScreenTouch.new()
|
||||
|
|
|
|||
|
|
@ -2,19 +2,18 @@ extends SceneTree
|
|||
|
||||
# Headless gameplay test: run with
|
||||
# godot --headless -s res://tests/test_building_collateral.gd
|
||||
# Forgejo #165: a gas bomb and a vessel flying fast enough both damage a
|
||||
# structure. The blast used to stop politely at anything the player owned,
|
||||
# which made a bomb dropped on a besieged mothership a free way to sweep the
|
||||
# attackers off it.
|
||||
# Forgejo #176: the player's own ships and their weapons do not damage the
|
||||
# player's buildings. Friendly fire on structures made defending them with a
|
||||
# gas bomb or crossing them at speed punish ordinary play.
|
||||
#
|
||||
# Covers:
|
||||
# A. a bomb landing on a structure damages it
|
||||
# A. a bomb landing on a structure leaves it unharmed
|
||||
# B. a bomb landing clear of it does not - the radius is the radius, and
|
||||
# this is a weapon that has to be aimed
|
||||
# C. a vessel resting against a structure does NOT damage it. The fleet
|
||||
# sits on its own mothership constantly to bank ore; if that hurt, the
|
||||
# game's own economy loop would grind its buildings down
|
||||
# D. a vessel arriving fast does
|
||||
# D. a vessel arriving fast still does not damage it or itself
|
||||
|
||||
var game_state: Node
|
||||
var main: Node
|
||||
|
|
@ -23,6 +22,7 @@ var building_id: int = 0
|
|||
var stage: int = 0
|
||||
var elapsed: float = 0.0
|
||||
var hp_before: int = 0
|
||||
var ships_before: int = 0
|
||||
const BUILDING_AT := Vector2(1200.0, 0.0)
|
||||
|
||||
|
||||
|
|
@ -78,11 +78,11 @@ func _process(delta: float) -> bool:
|
|||
return _fail("a bomb 900 away still damaged the structure")
|
||||
print("a bomb clear of the structure leaves it alone")
|
||||
|
||||
# --- A. a bomb on top of it does --------------------------------
|
||||
# --- A. a friendly bomb on top of it is safe ----------------------
|
||||
_detonate_at(BUILDING_AT)
|
||||
if _hp() >= hp_before:
|
||||
return _fail("a bomb landing on the structure did not damage it (hp %d)" % _hp())
|
||||
print("a bomb on the structure took it from %d to %d" % [hp_before, _hp()])
|
||||
if _hp() != hp_before:
|
||||
return _fail("a friendly bomb damaged the structure (%d -> %d)" % [hp_before, _hp()])
|
||||
print("a friendly bomb on the structure leaves it at %d hp" % _hp())
|
||||
|
||||
# --- C. resting against it is free ------------------------------
|
||||
hp_before = _hp()
|
||||
|
|
@ -104,8 +104,9 @@ func _process(delta: float) -> bool:
|
|||
% [hp_before, _hp()])
|
||||
print("a vessel parked on the structure for %.1fs did nothing to it" % elapsed)
|
||||
|
||||
# --- D. arriving fast does --------------------------------------
|
||||
# --- D. arriving fast is still friendly -------------------------
|
||||
hp_before = _hp()
|
||||
ships_before = fleet.ships.size()
|
||||
for ship in fleet.ships:
|
||||
# Placed clear and sent IN, because the impact is detected on
|
||||
# entry - a ship spawned already overlapping never enters.
|
||||
|
|
@ -118,9 +119,11 @@ func _process(delta: float) -> bool:
|
|||
3:
|
||||
if elapsed < 1.2:
|
||||
return false
|
||||
if _hp() >= hp_before:
|
||||
return _fail("a vessel arriving at 900 did not damage the structure")
|
||||
print("a vessel arriving fast took it from %d to %d" % [hp_before, _hp()])
|
||||
if _hp() != hp_before:
|
||||
return _fail("a fast friendly vessel damaged the structure (%d -> %d)" % [hp_before, _hp()])
|
||||
if fleet.ships.size() != ships_before:
|
||||
return _fail("a friendly building destroyed a vessel crossing it")
|
||||
print("a vessel crossing the structure at speed leaves both unharmed")
|
||||
|
||||
print("TEST_BUILDING_COLLATERAL: PASS")
|
||||
quit(0)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ func _process(delta: float) -> bool:
|
|||
|
||||
point = load("res://surface/CapturePoint.tscn").instantiate()
|
||||
point.point_index = 0
|
||||
if point.capture_time > 4.0:
|
||||
return _fail("default capture still takes %.1fs" % point.capture_time)
|
||||
point.capture_time = 1.0
|
||||
point.capture_radius = 200.0
|
||||
point.enemy_clear_radius = 300.0
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ extends SceneTree
|
|||
# E. it types while the game is stopped, being the one node still running
|
||||
# F. the sci-fi part: the panel is drawn differently while decoding, and
|
||||
# settles when the line is complete
|
||||
# G. #168 - a KEY does exactly what a click does, both halves of it, since
|
||||
# the game is played on a desktop with WASD and a mouse and pressing
|
||||
# space at a dialog reached nothing at all. Modifiers alone and held-key
|
||||
# echoes are not presses, or a line would be run out by a held key.
|
||||
# G. #175 - keyboard presses do not finish or dismiss a dialog; only a
|
||||
# deliberate tap/click advances it
|
||||
# H. #178 - revealed text is seated at the left edge rather than growing
|
||||
# outward from the middle
|
||||
|
||||
var game_state: Node
|
||||
var scene: Node
|
||||
|
|
@ -90,6 +90,8 @@ func _process(delta: float) -> bool:
|
|||
return _fail("the label holds a partial string - the line will re-wrap as it types")
|
||||
if narration.label.visible_characters < 0:
|
||||
return _fail("the whole line is visible already")
|
||||
if narration.label.horizontal_alignment != HORIZONTAL_ALIGNMENT_LEFT:
|
||||
return _fail("dialog text is not left-aligned - partial text will grow from the middle")
|
||||
seen.append(narration.label.visible_characters)
|
||||
|
||||
# Hand-ticked, so the reveal is measured rather than raced. The
|
||||
|
|
@ -151,37 +153,34 @@ func _process(delta: float) -> bool:
|
|||
return _fail("dismissing landed on \"%s\"" % narration.current)
|
||||
print("the press after that moves on to \"%s\"" % narration.current)
|
||||
|
||||
# --- G. #168: a key is a press too -----------------------
|
||||
# --- G. #175: keys never advance a dialog ----------------
|
||||
narration.clear()
|
||||
narration.say(first_line, "TEST")
|
||||
narration.say("Second.", "TEST")
|
||||
if not narration.is_typing():
|
||||
return _fail("the fresh line was not arriving")
|
||||
# Not everything that reaches the banner is a press.
|
||||
_key(KEY_SHIFT)
|
||||
_key(KEY_SPACE, true)
|
||||
if not narration.is_typing():
|
||||
return _fail("a modifier or a held-key echo finished the line")
|
||||
# Still arriving: the key shows the whole of it, and no more.
|
||||
_key(KEY_SPACE)
|
||||
if narration.is_typing():
|
||||
return _fail("a key did not finish a line that was still arriving")
|
||||
if narration.current != first_line:
|
||||
return _fail("a key on an arriving line dismissed it - the next line would be skipped unread")
|
||||
# Whole: the key moves on, the same as a click.
|
||||
_key(KEY_SPACE)
|
||||
if narration.current != "Second.":
|
||||
return _fail("a key on a finished line landed on \"%s\"" % narration.current)
|
||||
# Any key, not one chosen key - the player has no reason to guess.
|
||||
_key(KEY_W)
|
||||
if narration.is_typing():
|
||||
return _fail("only some keys finish a line")
|
||||
_key(KEY_ENTER)
|
||||
if not narration.is_typing() or narration.current != first_line:
|
||||
return _fail("a keyboard press changed a dialog that was still arriving")
|
||||
narration.finish_typing()
|
||||
_key(KEY_W)
|
||||
_key(KEY_ENTER)
|
||||
if narration.current != first_line or narration.is_typing():
|
||||
return _fail("a keyboard press dismissed a complete dialog")
|
||||
_click()
|
||||
if narration.current != "Second.":
|
||||
return _fail("clicking after ignored keys did not reach the second line")
|
||||
narration.finish_typing()
|
||||
_click()
|
||||
if narration.is_speaking():
|
||||
return _fail("a key on the last line did not close the dialog")
|
||||
return _fail("clicking the last complete line did not close the dialog")
|
||||
if paused:
|
||||
return _fail("keying through every line left the game stopped")
|
||||
print("space, W and enter all finish then advance a line; shift and echoes do not")
|
||||
return _fail("clicking through every line left the game stopped")
|
||||
print("space, W and enter leave dialogs untouched; clicks still finish and advance")
|
||||
|
||||
print("TEST_DIALOG_TYPING: PASS")
|
||||
quit(0)
|
||||
|
|
|
|||
137
tests/test_energy_propulser_dash.gd
Normal file
137
tests/test_energy_propulser_dash.gd
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
extends SceneTree
|
||||
|
||||
# Forgejo #179: an Energy Propulser in the fleet grants a hold-aim-release
|
||||
# dash. Covers visibility, aimed movement, energy cost, formation integrity,
|
||||
# and one cooldown per Propulser.
|
||||
|
||||
const DASH_COST: int = 15
|
||||
const DASH_DISTANCE: float = 520.0
|
||||
|
||||
var state: Node
|
||||
var main: Node
|
||||
var fleet: Node
|
||||
var dash: Node
|
||||
var stage: int = 0
|
||||
var elapsed: float = 0.0
|
||||
var first_origin: Vector2
|
||||
var second_origin: Vector2
|
||||
var offsets_before: Array = []
|
||||
var first_component: Node
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
state = root.get_node("GameState")
|
||||
state.reset_new_game()
|
||||
state.intro_active = false
|
||||
state.reset_quests()
|
||||
state.vessel_levels["unlock_distorder"] = 1
|
||||
state.fleet_inventory.total_capacity = -1
|
||||
state.deposit_fleet_resource(GameState.RESOURCE_ENERGY, DASH_COST * 3)
|
||||
main = load("res://world/Main.tscn").instantiate()
|
||||
root.add_child(main)
|
||||
current_scene = main
|
||||
fleet = main.get_node("Fleet")
|
||||
dash = main.get_node("DashController")
|
||||
|
||||
|
||||
func _process(delta: float) -> bool:
|
||||
elapsed += delta
|
||||
match stage:
|
||||
0:
|
||||
if elapsed < 0.3:
|
||||
return false
|
||||
main.get_node("EnemySpawner").set_process(false)
|
||||
main.get_node("AsteroidField").set_process(false)
|
||||
if main.get_node("UI/DashButton").visible:
|
||||
return _fail("dash button appeared without an Energy Propulser")
|
||||
# The carrier keeps enough hold capacity for both dashes. Two
|
||||
# Propulsers prove that cooldown belongs to a vessel, not the fleet.
|
||||
fleet.spawn_ships(3, Vector2.ZERO, PackedStringArray([
|
||||
GameState.VESSEL_CARRIER, GameState.VESSEL_DISTORDER,
|
||||
GameState.VESSEL_DISTORDER,
|
||||
]))
|
||||
dash._process(0.0)
|
||||
if not main.get_node("UI/DashButton").visible:
|
||||
return _fail("an Energy Propulser did not reveal the dash button")
|
||||
first_component = dash._next_ready_ship().get_component("distorder")
|
||||
first_origin = fleet.get_centroid()
|
||||
for ship in fleet.ships:
|
||||
offsets_before.append(ship.position - first_origin)
|
||||
_aim_and_release(Vector2.RIGHT, 11)
|
||||
if not fleet.is_dashing():
|
||||
return _fail("releasing a rightward aim did not begin a dash")
|
||||
if state.fleet_resource(GameState.RESOURCE_ENERGY) != DASH_COST * 2:
|
||||
return _fail("first dash did not consume %d energy" % DASH_COST)
|
||||
if first_component.is_ready():
|
||||
return _fail("the Propulser that dashed did not enter cooldown")
|
||||
dash._process(0.0)
|
||||
if not main.get_node("UI/DashButton").enabled:
|
||||
return _fail("one cooling Propulser disabled a second ready one")
|
||||
stage = 1
|
||||
elapsed = 0.0
|
||||
return false
|
||||
1:
|
||||
if elapsed < 0.7:
|
||||
return false
|
||||
var moved: Vector2 = fleet.get_centroid() - first_origin
|
||||
if absf(moved.x - DASH_DISTANCE) > 8.0 or absf(moved.y) > 8.0:
|
||||
return _fail("rightward aim moved the fleet by %s" % moved)
|
||||
for i in range(fleet.ships.size()):
|
||||
var offset: Vector2 = fleet.ships[i].position - fleet.get_centroid()
|
||||
if offset.distance_to(offsets_before[i]) > 1.0:
|
||||
return _fail("dash distorted formation slot %d by %.2f" \
|
||||
% [i, offset.distance_to(offsets_before[i])])
|
||||
second_origin = fleet.get_centroid()
|
||||
_aim_and_release(Vector2.UP, 12)
|
||||
if state.fleet_resource(GameState.RESOURCE_ENERGY) != DASH_COST:
|
||||
return _fail("second Propulser did not pay for its own dash")
|
||||
dash._process(0.0)
|
||||
if main.get_node("UI/DashButton").enabled:
|
||||
return _fail("dash remained enabled after both Propulsers fired")
|
||||
stage = 2
|
||||
elapsed = 0.0
|
||||
return false
|
||||
2:
|
||||
if elapsed < 0.7:
|
||||
return false
|
||||
var moved: Vector2 = fleet.get_centroid() - second_origin
|
||||
if absf(moved.y + DASH_DISTANCE) > 8.0 or absf(moved.x) > 8.0:
|
||||
return _fail("upward aim moved the fleet by %s" % moved)
|
||||
first_component.tick(first_component.DASH_COOLDOWN)
|
||||
dash._process(0.0)
|
||||
if not main.get_node("UI/DashButton").enabled:
|
||||
return _fail("button did not re-enable when one Propulser recharged")
|
||||
var surface: Node = load("res://surface/PlanetSurface.tscn").instantiate()
|
||||
if not surface.has_node("DashController") or not surface.has_node("UI/DashButton"):
|
||||
return _fail("planet surface does not carry the dash ability")
|
||||
surface.free()
|
||||
print("two aimed dashes moved %.0f units, preserved formation, and cooled down per Propulser" \
|
||||
% DASH_DISTANCE)
|
||||
print("TEST_ENERGY_PROPULSER_DASH: PASS")
|
||||
quit(0)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _aim_and_release(direction: Vector2, touch_index: int) -> void:
|
||||
var button: Control = main.get_node("UI/DashButton")
|
||||
var press := InputEventScreenTouch.new()
|
||||
press.index = touch_index
|
||||
press.pressed = true
|
||||
press.position = button.global_position + button.size * 0.5
|
||||
main._handle_touch(press)
|
||||
var drag := InputEventScreenDrag.new()
|
||||
drag.index = touch_index
|
||||
drag.position = press.position + direction * 240.0
|
||||
main._handle_drag(drag)
|
||||
var release := InputEventScreenTouch.new()
|
||||
release.index = touch_index
|
||||
release.pressed = false
|
||||
release.position = drag.position
|
||||
main._handle_touch(release)
|
||||
|
||||
|
||||
func _fail(message: String) -> bool:
|
||||
print("TEST_ENERGY_PROPULSER_DASH: FAIL (%s)" % message)
|
||||
quit(1)
|
||||
return true
|
||||
|
|
@ -15,6 +15,8 @@ extends SceneTree
|
|||
# 5. #169 - a second press while a load is running is ignored, or an
|
||||
# impatient double tap builds the world twice (on the web a button fires
|
||||
# on PRESS, so that tap costs nothing to make)
|
||||
# 6. #174 - the opaque loading cover fills the viewport and puts its
|
||||
# activity bar at the middle of the screen
|
||||
|
||||
var menu: Node
|
||||
var game_state: Node
|
||||
|
|
@ -61,7 +63,19 @@ func _process(delta: float) -> bool:
|
|||
return _fail("the press started a load with no loading screen up")
|
||||
if current_scene != menu:
|
||||
return _fail("the scene swapped inside the press, so nothing was ever shown")
|
||||
var viewport_size: Vector2 = menu.get_viewport().get_visible_rect().size
|
||||
if menu.loading_screen.size.distance_to(viewport_size) > 1.0:
|
||||
return _fail("loading cover is %s, viewport is %s" \
|
||||
% [menu.loading_screen.size, viewport_size])
|
||||
if menu.loading_screen.BACKDROP.a < 0.999:
|
||||
return _fail("loading cover is translucent (alpha %.2f)" \
|
||||
% menu.loading_screen.BACKDROP.a)
|
||||
var bar: Rect2 = menu.loading_screen._bar_rect()
|
||||
if absf(bar.get_center().y - viewport_size.y * 0.5) > 1.0:
|
||||
return _fail("loading bar centre %.1f is not screen centre %.1f" \
|
||||
% [bar.get_center().y, viewport_size.y * 0.5])
|
||||
print("pressing an entry raises the loading screen and leaves the menu up")
|
||||
print("loading cover fills %s and centres its bar" % viewport_size)
|
||||
|
||||
# --- 5. and a second press changes nothing --------------------
|
||||
var screen: Node = menu.loading_screen
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ extends SceneTree
|
|||
|
||||
# Headless gameplay test: run with
|
||||
# godot --headless -s res://tests/test_payments_use_fleet_hold.gd
|
||||
# Every payment draws on the bank AND on what the ships are carrying. The
|
||||
# player can already shuttle resources between the two by hand, so refusing a
|
||||
# purchase that a couple of taps would have made affordable was ceremony.
|
||||
# A payment draws on the bank and, only at the place being paid, what the
|
||||
# ships are carrying. Fleet cargo is physical: a building across the map may
|
||||
# not reach into it (#180).
|
||||
#
|
||||
# It was also a deadlock. A new game starts with an empty bank, ore is mined
|
||||
# into the fleet hold, and the only way to bank it is to deliver it to a
|
||||
|
|
@ -22,8 +22,10 @@ extends SceneTree
|
|||
# are physically carrying
|
||||
# F. end to end: an empty bank and ore in the hold actually gets a
|
||||
# mothership built through BuildController
|
||||
# G. no payment position, or a distant one, cannot drain the fleet hold
|
||||
|
||||
const HOLD_ORE: int = 40
|
||||
const PAYMENT_AT := Vector2(120.0, 60.0)
|
||||
|
||||
var game_state: Node
|
||||
var scene: Node
|
||||
|
|
@ -37,6 +39,7 @@ func _initialize() -> void:
|
|||
game_state.reset_progression()
|
||||
game_state.buildings.clear()
|
||||
game_state.extractors.clear()
|
||||
game_state.fleet_position = PAYMENT_AT
|
||||
|
||||
var cost: int = int(game_state.building_costs(GameState.BUILDING_MOTHERSHIP)[GameState.RESOURCE_ORE])
|
||||
|
||||
|
|
@ -50,13 +53,16 @@ func _initialize() -> void:
|
|||
if game_state.bank_inventory.amount(GameState.RESOURCE_ORE) >= cost:
|
||||
_fail("the bank was not actually empty, so nothing below tests the deadlock")
|
||||
return
|
||||
if not game_state.can_afford(cost):
|
||||
if game_state.can_afford(cost):
|
||||
_fail("a location-free affordability check reached into the fleet hold")
|
||||
return
|
||||
if not game_state.can_afford(cost, PAYMENT_AT):
|
||||
_fail("a fleet holding %d ore could not afford a %d purchase" % [HOLD_ORE, cost])
|
||||
return
|
||||
print("bank 0 + hold %d can afford a %d purchase" % [HOLD_ORE, cost])
|
||||
|
||||
# --- B. bank first, hold for the shortfall -------------------------------
|
||||
if not game_state.spend(cost):
|
||||
if not game_state.spend(cost, PAYMENT_AT):
|
||||
_fail("spend refused a purchase it had just called affordable")
|
||||
return
|
||||
if game_state.bank_inventory.amount(GameState.RESOURCE_ORE) != 0 \
|
||||
|
|
@ -68,7 +74,7 @@ func _initialize() -> void:
|
|||
|
||||
game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 8)
|
||||
game_state.fleet_inventory.set_amount(GameState.RESOURCE_ORE, 30)
|
||||
if not game_state.spend(20):
|
||||
if not game_state.spend(20, PAYMENT_AT):
|
||||
_fail("a 20 purchase was refused with 8 banked and 30 held")
|
||||
return
|
||||
if game_state.bank_inventory.amount(GameState.RESOURCE_ORE) != 0 \
|
||||
|
|
@ -82,7 +88,7 @@ func _initialize() -> void:
|
|||
# --- C. no partial charge -----------------------------------------------
|
||||
game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 5)
|
||||
game_state.fleet_inventory.set_amount(GameState.RESOURCE_ORE, 5)
|
||||
if game_state.spend(50):
|
||||
if game_state.spend(50, PAYMENT_AT):
|
||||
_fail("a purchase costing more than everything owned was allowed")
|
||||
return
|
||||
if game_state.bank_inventory.amount(GameState.RESOURCE_ORE) != 5 \
|
||||
|
|
@ -93,6 +99,22 @@ func _initialize() -> void:
|
|||
return
|
||||
print("a purchase nobody can afford takes nothing")
|
||||
|
||||
# --- G. cargo does not teleport to a remote purchase ------------------
|
||||
game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 0)
|
||||
game_state.fleet_inventory.set_amount(GameState.RESOURCE_ORE, cost)
|
||||
game_state.fleet_position = PAYMENT_AT + Vector2(9000.0, 0.0)
|
||||
if game_state.spend(cost, PAYMENT_AT):
|
||||
_fail("a remote purchase took resources from the fleet hold")
|
||||
return
|
||||
if game_state.fleet_resource(GameState.RESOURCE_ORE) != cost:
|
||||
_fail("a refused remote payment still drained the hold")
|
||||
return
|
||||
if game_state.spend(cost):
|
||||
_fail("a purchase with no location reached into the fleet hold")
|
||||
return
|
||||
game_state.fleet_position = PAYMENT_AT
|
||||
print("distant and location-free payments leave fleet cargo untouched")
|
||||
|
||||
# The hold has a real capacity (one starting interceptor carries 20), and
|
||||
# set_amount() is clamped by it - which silently gave the checks below
|
||||
# less than they asked for and made an affordable price look unaffordable.
|
||||
|
|
@ -125,12 +147,12 @@ func _check_multi_resource_atomicity() -> bool:
|
|||
game_state.fleet_inventory.set_amount(GameState.RESOURCE_ORE, 10)
|
||||
game_state.bank_inventory.set_amount(GameState.RESOURCE_GAS, 1)
|
||||
game_state.fleet_inventory.set_amount(GameState.RESOURCE_GAS, 1)
|
||||
if game_state.spend_resources({GameState.RESOURCE_ORE: 15, GameState.RESOURCE_GAS: 40}):
|
||||
if game_state.spend_resources({GameState.RESOURCE_ORE: 15, GameState.RESOURCE_GAS: 40}, PAYMENT_AT):
|
||||
return _fail("a two-resource price was charged with only one of them affordable")
|
||||
if game_state.available_ore() != 20 or game_state.available_resource(GameState.RESOURCE_GAS) != 2:
|
||||
return _fail("a refused two-resource purchase still took some ore or gas (ore %d, gas %d)" \
|
||||
% [game_state.available_ore(), game_state.available_resource(GameState.RESOURCE_GAS)])
|
||||
if not game_state.spend_resources({GameState.RESOURCE_ORE: 15, GameState.RESOURCE_GAS: 2}):
|
||||
if not game_state.spend_resources({GameState.RESOURCE_ORE: 15, GameState.RESOURCE_GAS: 2}, PAYMENT_AT):
|
||||
return _fail("an affordable two-resource price was refused")
|
||||
if game_state.available_ore() != 5 or game_state.available_resource(GameState.RESOURCE_GAS) != 0:
|
||||
return _fail("a two-resource purchase charged wrongly (ore %d left, gas %d left)" \
|
||||
|
|
@ -147,9 +169,9 @@ func _check_skill_tree() -> bool:
|
|||
game_state.research_points = 999
|
||||
game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 0)
|
||||
game_state.fleet_inventory.set_amount(GameState.RESOURCE_ORE, cost)
|
||||
if not game_state.can_unlock(id):
|
||||
if not game_state.can_unlock(id, PAYMENT_AT):
|
||||
return _fail("the skill tree refused '%s' (cost %d) with the whole price in the fleet hold" % [id, cost])
|
||||
if not game_state.unlock(id):
|
||||
if not game_state.unlock(id, PAYMENT_AT):
|
||||
return _fail("unlocking '%s' failed despite can_unlock saying yes" % id)
|
||||
if game_state.fleet_inventory.amount(GameState.RESOURCE_ORE) != 0:
|
||||
return _fail("the skill unlock did not charge the hold: %d left of %d" \
|
||||
|
|
@ -177,9 +199,9 @@ func _check_ship_lab() -> bool:
|
|||
game_state.research_points = 999
|
||||
game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 0)
|
||||
game_state.fleet_inventory.set_amount(GameState.RESOURCE_ORE, cost)
|
||||
if not game_state.can_buy_upgrade(id):
|
||||
if not game_state.can_buy_upgrade(id, PAYMENT_AT):
|
||||
return _fail("the ship lab refused upgrade '%s' (cost %d) with the whole price in the hold" % [id, cost])
|
||||
if not game_state.buy_upgrade(id):
|
||||
if not game_state.buy_upgrade(id, PAYMENT_AT):
|
||||
return _fail("buying '%s' failed despite can_buy_upgrade saying yes" % id)
|
||||
if game_state.fleet_inventory.amount(GameState.RESOURCE_ORE) != 0:
|
||||
return _fail("the upgrade did not charge the hold: %d left of %d" \
|
||||
|
|
@ -206,7 +228,7 @@ func _check_vessel_research() -> bool:
|
|||
for resource_type in costs:
|
||||
game_state.bank_inventory.set_amount(String(resource_type), 0)
|
||||
game_state.fleet_inventory.set_amount(String(resource_type), int(costs[resource_type]))
|
||||
if not game_state.unlock_vessel(vessel):
|
||||
if not game_state.unlock_vessel(vessel, PAYMENT_AT):
|
||||
return _fail("researching '%s' was refused with its whole price (%s) in the fleet hold" % [vessel, str(costs)])
|
||||
for resource_type in costs:
|
||||
if game_state.available_resource(String(resource_type)) != 0:
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ func _initialize() -> void:
|
|||
game_state.buildings.clear()
|
||||
game_state.extractors.clear()
|
||||
game_state.reset_progression()
|
||||
game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO)
|
||||
main = load("res://world/Main.tscn").instantiate()
|
||||
root.add_child(main)
|
||||
current_scene = main
|
||||
|
|
@ -92,9 +93,17 @@ func _process(delta: float) -> bool:
|
|||
return _fail("the research panel still has an UnlockButton node")
|
||||
print("the separate buy button is gone from the panel")
|
||||
|
||||
main.get_node("UI/SkillMenu").visible = true
|
||||
tree_view.visible = true
|
||||
tree_view.reset_scroll()
|
||||
# Open through the menu's public API so research has the mothership as its
|
||||
# payment site. Since fleet-held resources became location-aware, merely
|
||||
# forcing the Control visible no longer represents a reachable game state.
|
||||
var mothership_id: int = 0
|
||||
for building in game_state.buildings:
|
||||
if String(building.type) == GameState.BUILDING_MOTHERSHIP:
|
||||
mothership_id = int(building.id)
|
||||
break
|
||||
if mothership_id == 0:
|
||||
return _fail("the test has no mothership research site")
|
||||
main.get_node("UI/SkillMenu").open_for(mothership_id)
|
||||
game_state.unlocked.clear()
|
||||
game_state.research_points = 99
|
||||
game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 500)
|
||||
|
|
|
|||
Loading…
Reference in a new issue