Stock a Fleet Office for its own berth, and stop there
#223: an office declared a flat buffer - 60 ore, 30 gas, 30 energy - whatever was in its berth, so one keeping an interceptor had transporters haul it gas and energy it can never spend, and the figure bore no relation to what it was actually charged. Since #220 that charge depends on the berth's occupant and on how far down the fleet the berth sits, neither of which a definition can state. It asks for what its own berth costs, four charges deep: enough that the tax is payable whenever the bank has anything - a transporter round trip fits inside four intervals - and no more, so an office is not a hoard. Same idiom outposts and workshops already use for world-dependent demand. A small floor stays in the definition on purpose: an empty berth asks for nothing dynamic, so without it a newly crewed berth would start from zero and could starve before its first delivery landed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
This commit is contained in:
parent
f77596395b
commit
fe3ae97f94
3 changed files with 138 additions and 3 deletions
|
|
@ -7,9 +7,9 @@
|
|||
[sub_resource type="Resource" id="Resource_office_logistics"]
|
||||
script = ExtResource("3")
|
||||
input_buffers = {
|
||||
"energy": 30,
|
||||
"gas": 30,
|
||||
"ore": 60
|
||||
"energy": 6,
|
||||
"gas": 6,
|
||||
"ore": 10
|
||||
}
|
||||
collected_outputs = PackedStringArray()
|
||||
conversion_input = ""
|
||||
|
|
|
|||
|
|
@ -786,6 +786,12 @@ const FLEET_OFFICE_UPKEEP_DIVISOR: int = 8
|
|||
# seven and a half. A steeper rate reaches "unaffordable" while the player is
|
||||
# still learning what the fleet is for.
|
||||
const FLEET_UPKEEP_GROWTH: float = 1.12
|
||||
# #223: how many charges an office is kept stocked for. Enough that the tax
|
||||
# is always payable while the bank has anything - a transporter round trip is
|
||||
# well inside four charge intervals - and no more, so an office does not sit
|
||||
# on resources the rest of the base needs. Below this it can starve with ore
|
||||
# in the bank; far above it, offices become a hoard.
|
||||
const FLEET_OFFICE_STOCK_CHARGES: int = 4
|
||||
|
||||
|
||||
# Offices in the order they were built, which is the order they are matched to
|
||||
|
|
@ -868,6 +874,28 @@ func fleet_upkeep_breakdown() -> Dictionary:
|
|||
|
||||
# The vessel an office is answerable for, or "" when the fleet is smaller than
|
||||
# the berths built for it - an office with nothing in it costs nothing.
|
||||
# What this office should hold of one resource: enough charges of whatever is
|
||||
# in its berth to keep paying while a transporter is on its way, and nothing
|
||||
# it will never spend. An office keeping an interceptor asks for no gas at
|
||||
# all, where a flat buffer had it hauling gas it could not use.
|
||||
#
|
||||
# An empty berth asks for nothing beyond the small floor its definition
|
||||
# declares - which is what keeps a newly crewed berth payable before its
|
||||
# first delivery arrives.
|
||||
func fleet_office_stock_target(id: int, resource_type: String) -> int:
|
||||
var offices: Array = fleet_offices()
|
||||
for i in range(offices.size()):
|
||||
if int(offices[i].get("id", 0)) != id:
|
||||
continue
|
||||
var slot_index: int = i + BASE_FLEET_SIZE
|
||||
var vessel_type: String = fleet_office_vessel(slot_index)
|
||||
if vessel_type == "":
|
||||
return 0
|
||||
var upkeep: Dictionary = fleet_office_upkeep(vessel_type, slot_index)
|
||||
return int(upkeep.get(resource_type, 0)) * FLEET_OFFICE_STOCK_CHARGES
|
||||
return 0
|
||||
|
||||
|
||||
func fleet_office_vessel(slot_index: int) -> String:
|
||||
if slot_index < 0 or slot_index >= fleet_vessel_types.size():
|
||||
return ""
|
||||
|
|
@ -2842,6 +2870,10 @@ func building_logistics_deficit(id: int, resource_type: String) -> int:
|
|||
# take, which likewise depends on the world rather than on a definition.
|
||||
if resource_type == RESOURCE_ORE:
|
||||
target = maxi(target, workshop_ore_demand(id))
|
||||
# #223: a Fleet Office asks for what the vessel in ITS berth is charged,
|
||||
# which no definition can state - the berth's occupant decides it, and
|
||||
# since #220 so does how far down the fleet that berth sits.
|
||||
target = maxi(target, fleet_office_stock_target(id, resource_type))
|
||||
return maxi(0, target - building_resource(id, resource_type))
|
||||
|
||||
|
||||
|
|
|
|||
103
tests/test_fleet_office_stock.gd
Normal file
103
tests/test_fleet_office_stock.gd
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
extends SceneTree
|
||||
|
||||
# Forgejo #223: a Fleet Office is stocked to a bounded amount - enough that
|
||||
# its charge is always payable while the bank has anything, and no more.
|
||||
#
|
||||
# It used to declare a flat buffer (60 ore, 30 gas, 30 energy) whatever was
|
||||
# in its berth, so an office keeping an interceptor had transporters haul it
|
||||
# gas and energy it could never spend, and the amount had no relation to what
|
||||
# it was actually charged. Since #220 the charge also depends on how far down
|
||||
# the fleet the berth sits, which no definition can state.
|
||||
#
|
||||
# Covers:
|
||||
# A. an office asks for what its OWN berth is charged, not a flat figure
|
||||
# B. it asks for nothing of a resource that berth never spends
|
||||
# C. what it asks for covers at least one charge, so the tax is payable
|
||||
# D. the request is bounded - a stocked office stops asking
|
||||
# E. and a deeper berth, which #220 charges more, asks for more
|
||||
|
||||
var state: Node
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
state = root.get_node("GameState")
|
||||
state.buildings.clear()
|
||||
state.transporters.clear()
|
||||
state.reset_progression()
|
||||
state.reset_new_game()
|
||||
state.intro_active = false
|
||||
state.fleet_vessel_types = PackedStringArray()
|
||||
|
||||
|
||||
func _process(_delta: float) -> bool:
|
||||
var offices: Array = []
|
||||
for i in range(4):
|
||||
offices.append(state.add_building(GameState.BUILDING_FLEET_OFFICE,
|
||||
Vector2(600.0 * float(i + 1), 0.0)))
|
||||
# Berth 2 keeps an interceptor (ore only); berth 5 keeps a laser, which
|
||||
# is dearer both in kind and in depth.
|
||||
state.fleet_vessel_types = PackedStringArray([
|
||||
GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_INTERCEPTOR,
|
||||
GameState.VESSEL_LASER, GameState.VESSEL_LASER, GameState.VESSEL_LASER])
|
||||
|
||||
var first: int = int(offices[0])
|
||||
var charge: Dictionary = state.fleet_office_upkeep(
|
||||
GameState.VESSEL_INTERCEPTOR, GameState.BASE_FLEET_SIZE)
|
||||
var ore_charge: int = int(charge.get(GameState.RESOURCE_ORE, 0))
|
||||
if ore_charge <= 0:
|
||||
return _fail("an interceptor berth is charged no ore, so this proves nothing")
|
||||
|
||||
# --- A + C. it asks for its own charge, several times over ---------
|
||||
var ore_wanted: int = state.building_requested_resource(first, GameState.RESOURCE_ORE)
|
||||
if ore_wanted < ore_charge:
|
||||
return _fail("an empty office asks for %d ore against a %d charge - it can starve with ore banked" \
|
||||
% [ore_wanted, ore_charge])
|
||||
if ore_wanted < ore_charge * GameState.FLEET_OFFICE_STOCK_CHARGES:
|
||||
return _fail("asks for %d ore, under the %d charges it is meant to hold" \
|
||||
% [ore_wanted, GameState.FLEET_OFFICE_STOCK_CHARGES])
|
||||
# A cheap hull's charges come to less than the small floor the definition
|
||||
# declares, and the floor wins - that floor is what keeps a berth payable
|
||||
# before its first delivery lands. The dynamic term is what matters for a
|
||||
# dear one, which is checked at berth 5 below.
|
||||
|
||||
# --- B. and nothing it cannot spend --------------------------------
|
||||
# An interceptor costs ore alone, so the gas request is the definition's
|
||||
# small floor rather than a buffer's worth of cargo it will never burn.
|
||||
var gas_wanted: int = state.building_requested_resource(first, GameState.RESOURCE_GAS)
|
||||
if gas_wanted >= ore_wanted:
|
||||
return _fail("an ore-only berth asks for %d gas against %d ore" % [gas_wanted, ore_wanted])
|
||||
print("berth 2 (interceptor): %d ore wanted for a %d charge, %d gas" \
|
||||
% [ore_wanted, ore_charge, gas_wanted])
|
||||
|
||||
# --- D. bounded: stocked, it stops asking ---------------------------
|
||||
state.deposit_resource_to_building(first, GameState.RESOURCE_ORE, ore_wanted)
|
||||
if state.building_requested_resource(first, GameState.RESOURCE_ORE) != 0:
|
||||
return _fail("a stocked office is still asking for ore - the fill is unbounded")
|
||||
state.deposit_resource_to_building(first, GameState.RESOURCE_ORE, 500)
|
||||
if state.building_requested_resource(first, GameState.RESOURCE_ORE) != 0:
|
||||
return _fail("an over-stocked office asks for still more")
|
||||
print("stocked, it asks for nothing further")
|
||||
|
||||
# --- E. a deeper berth costs more, and asks for more ----------------
|
||||
var deep: int = int(offices[3])
|
||||
var deep_ore: int = state.building_requested_resource(deep, GameState.RESOURCE_ORE)
|
||||
var shallow_laser: Dictionary = state.fleet_office_upkeep(
|
||||
GameState.VESSEL_LASER, GameState.BASE_FLEET_SIZE)
|
||||
if deep_ore <= int(shallow_laser.get(GameState.RESOURCE_ORE, 0)) * GameState.FLEET_OFFICE_STOCK_CHARGES - 1:
|
||||
return _fail("berth 5 asks for %d ore, no more than the same hull would at berth 2" % deep_ore)
|
||||
# A laser burns energy too, so that berth asks for energy where the
|
||||
# interceptor's did not.
|
||||
if state.building_requested_resource(deep, GameState.RESOURCE_ENERGY) \
|
||||
<= state.building_requested_resource(first, GameState.RESOURCE_ENERGY):
|
||||
return _fail("a laser berth asks for no more energy than an interceptor berth")
|
||||
print("berth 5 (laser, deeper): %d ore and energy on top" % deep_ore)
|
||||
|
||||
print("TEST_FLEET_OFFICE_STOCK: PASS")
|
||||
quit(0)
|
||||
return true
|
||||
|
||||
|
||||
func _fail(message: String) -> bool:
|
||||
print("TEST_FLEET_OFFICE_STOCK: FAIL (%s)" % message)
|
||||
quit(1)
|
||||
return true
|
||||
Loading…
Reference in a new issue