150 lines
5.6 KiB
GDScript3
150 lines
5.6 KiB
GDScript3
|
|
extends SceneTree
|
||
|
|
|
||
|
|
# Forgejo #230: the upkeep menu shows what the fleet costs IN TOTAL, laid out
|
||
|
|
# as a table.
|
||
|
|
#
|
||
|
|
# It listed each berth and never added them up, so the bill had to be done in
|
||
|
|
# the player's head. The rows were also one Label padded with %-16s, which
|
||
|
|
# lines up only in a monospace font - the theme's is CarroisGothicSC, which
|
||
|
|
# is proportional, so the "columns" never actually aligned. Worse, that Label
|
||
|
|
# had autowrap on, so a long row folded and the table lost its shape.
|
||
|
|
#
|
||
|
|
# Covers:
|
||
|
|
# A. a row per paying berth, in a real grid
|
||
|
|
# B. a TOTAL row, and it equals the sum of the rows
|
||
|
|
# C. the headline states both the per-charge bill and the per-minute rate
|
||
|
|
# D. an unpaid berth is marked
|
||
|
|
# E. with nothing to charge, the table gives way to an explanation
|
||
|
|
# F. the sheet is not rebuilt on every frame - the menu refreshes at frame
|
||
|
|
# rate and these numbers move every 25 seconds
|
||
|
|
|
||
|
|
var game_state: Node
|
||
|
|
var scene: Node
|
||
|
|
var menu: Control
|
||
|
|
var elapsed: float = 0.0
|
||
|
|
|
||
|
|
|
||
|
|
func _initialize() -> void:
|
||
|
|
game_state = root.get_node("GameState")
|
||
|
|
game_state.buildings.clear()
|
||
|
|
game_state.extractors.clear()
|
||
|
|
game_state.reset_progression()
|
||
|
|
game_state.reset_new_game()
|
||
|
|
game_state.reset_quests()
|
||
|
|
game_state.intro_active = false
|
||
|
|
scene = load("res://world/Main.tscn").instantiate()
|
||
|
|
root.add_child(scene)
|
||
|
|
current_scene = scene
|
||
|
|
menu = scene.get_node("UI/FleetUpkeepMenu")
|
||
|
|
|
||
|
|
|
||
|
|
func _rows() -> Array:
|
||
|
|
# Grid cells come out in reading order; COLUMNS wide makes one row.
|
||
|
|
var width: int = menu.sheet.columns
|
||
|
|
var cells: Array = []
|
||
|
|
for child in menu.sheet.get_children():
|
||
|
|
cells.append(String((child as Label).text))
|
||
|
|
var rows: Array = []
|
||
|
|
for i in range(0, cells.size(), width):
|
||
|
|
rows.append(cells.slice(i, i + width))
|
||
|
|
return rows
|
||
|
|
|
||
|
|
|
||
|
|
func _process(delta: float) -> bool:
|
||
|
|
elapsed += delta
|
||
|
|
if elapsed < 0.4:
|
||
|
|
return false
|
||
|
|
|
||
|
|
# --- E. nothing to charge -------------------------------------------
|
||
|
|
menu.open_menu()
|
||
|
|
menu.refresh()
|
||
|
|
if not menu.empty_label.visible or menu.sheet.visible:
|
||
|
|
return _fail("with no berths the table is shown instead of an explanation")
|
||
|
|
if not menu.total_label.text.contains("NOTHING"):
|
||
|
|
return _fail("with no berths the headline reads '%s'" % menu.total_label.text)
|
||
|
|
print("with nothing to charge: '%s'" % menu.total_label.text)
|
||
|
|
|
||
|
|
# Two offices, two crewed berths - the second costs more than the first.
|
||
|
|
game_state.add_building(GameState.BUILDING_FLEET_OFFICE, Vector2.ZERO)
|
||
|
|
game_state.add_building(GameState.BUILDING_FLEET_OFFICE, Vector2(600.0, 0.0))
|
||
|
|
game_state.fleet_vessel_types = PackedStringArray([
|
||
|
|
GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_INTERCEPTOR,
|
||
|
|
GameState.VESSEL_INTERCEPTOR])
|
||
|
|
menu.refresh()
|
||
|
|
|
||
|
|
var breakdown: Dictionary = game_state.fleet_upkeep_breakdown()
|
||
|
|
if breakdown.berths.size() < 2:
|
||
|
|
return _fail("only %d berth(s) are being charged - that proves nothing about a total" \
|
||
|
|
% breakdown.berths.size())
|
||
|
|
|
||
|
|
# --- A. a row per berth, plus header and total ------------------------
|
||
|
|
var rows: Array = _rows()
|
||
|
|
if not menu.sheet.visible or menu.empty_label.visible:
|
||
|
|
return _fail("the table is hidden while berths are being charged")
|
||
|
|
if rows.size() != breakdown.berths.size() + 2:
|
||
|
|
return _fail("%d rows for %d berths - expected a header and a total too" \
|
||
|
|
% [rows.size(), breakdown.berths.size()])
|
||
|
|
if String(rows[0][0]) != "BERTH":
|
||
|
|
return _fail("the first row is not a header: %s" % str(rows[0]))
|
||
|
|
print("%d berths -> %d rows (header, %d berths, total)" \
|
||
|
|
% [breakdown.berths.size(), rows.size(), breakdown.berths.size()])
|
||
|
|
|
||
|
|
# --- B. and the total is the sum of the rows --------------------------
|
||
|
|
var total_row: Array = rows[rows.size() - 1]
|
||
|
|
if String(total_row[0]) != "TOTAL":
|
||
|
|
return _fail("the last row is '%s', not a total" % total_row[0])
|
||
|
|
var summed: int = 0
|
||
|
|
for berth in breakdown.berths:
|
||
|
|
for resource_type in berth.costs:
|
||
|
|
summed += int(berth.costs[resource_type])
|
||
|
|
var shown: int = 0
|
||
|
|
for word in String(total_row[2]).split(" "):
|
||
|
|
if word.is_valid_int():
|
||
|
|
shown += int(word)
|
||
|
|
if shown != summed:
|
||
|
|
return _fail("the total row reads '%s' (%d), but the berths add to %d" \
|
||
|
|
% [total_row[2], shown, summed])
|
||
|
|
if summed <= 0:
|
||
|
|
return _fail("the berths cost nothing, so a total proves nothing")
|
||
|
|
print("total row '%s' matches the %d the berths add to" % [total_row[2], summed])
|
||
|
|
|
||
|
|
# --- C. the headline carries both figures -----------------------------
|
||
|
|
var headline: String = menu.total_label.text
|
||
|
|
if not headline.contains("PER MINUTE") or not headline.contains("EVERY"):
|
||
|
|
return _fail("the headline does not state both the bill and the rate: '%s'" % headline)
|
||
|
|
print("headline: %s" % headline)
|
||
|
|
|
||
|
|
# --- F. and it is not rebuilt every frame -----------------------------
|
||
|
|
var first: Array = menu.sheet.get_children()
|
||
|
|
menu.refresh()
|
||
|
|
menu.refresh()
|
||
|
|
var again: Array = menu.sheet.get_children()
|
||
|
|
if first.size() != again.size():
|
||
|
|
return _fail("the row count changed across a refresh with no change behind it")
|
||
|
|
for i in range(first.size()):
|
||
|
|
if first[i] != again[i]:
|
||
|
|
return _fail("refreshing rebuilt the table though nothing changed")
|
||
|
|
print("refreshing twice reuses the same %d cells" % again.size())
|
||
|
|
|
||
|
|
# --- D. an unpaid berth is called out ---------------------------------
|
||
|
|
for building in game_state.buildings:
|
||
|
|
if String(building.type) == GameState.BUILDING_FLEET_OFFICE:
|
||
|
|
building["starved"] = true
|
||
|
|
menu.refresh()
|
||
|
|
var marked: bool = false
|
||
|
|
for row in _rows():
|
||
|
|
if String(row[4]) == "UNPAID":
|
||
|
|
marked = true
|
||
|
|
if not marked:
|
||
|
|
return _fail("a starved berth is not marked unpaid")
|
||
|
|
print("a starved berth is marked UNPAID")
|
||
|
|
|
||
|
|
print("TEST_FLEET_UPKEEP_SHEET: PASS")
|
||
|
|
quit(0)
|
||
|
|
return true
|
||
|
|
|
||
|
|
|
||
|
|
func _fail(message: String) -> bool:
|
||
|
|
print("TEST_FLEET_UPKEEP_SHEET: FAIL (%s)" % message)
|
||
|
|
quit(1)
|
||
|
|
return true
|