Charge more for each berth, and show the bill
#220: every berth past the first paid one costs 1.12x the one before it, on top of the flat per-vessel charge. A linear bill makes the tenth hull no more of a decision than the second; compounding weighs each addition against the whole fleet behind it. Shallow on purpose - the thirteenth berth costs about three and a half times the second, the twentieth about seven and a half. Fleet Offices gain a menu naming the whole bill per minute, a line per paying berth with its multiplier, and the arithmetic in words. It renders fleet_upkeep_breakdown(), which is the calculation the charging loop itself runs: a readout that summed independently could quote a bill the game does not take, which is the thing worth preventing here. The test pins that what the menu quotes for a berth is what that office is charged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
This commit is contained in:
parent
cc71811d89
commit
2bb46cba94
9 changed files with 369 additions and 5 deletions
|
|
@ -776,6 +776,16 @@ signal fleet_upkeep_failed(slot_index: int, vessel_type: String)
|
|||
# ticket asks for and needs no second table to fall out of step with.
|
||||
const FLEET_OFFICE_UPKEEP_SECONDS: float = 25.0
|
||||
const FLEET_OFFICE_UPKEEP_DIVISOR: int = 8
|
||||
# #220: and each berth costs more than the one before it. A flat per-vessel
|
||||
# charge means a big fleet is only linearly harder to keep, so the tenth hull
|
||||
# is no more of a decision than the second; compounding makes every addition
|
||||
# weigh against the whole fleet behind it.
|
||||
#
|
||||
# Deliberately shallow - the ticket asks for a slow one. At 1.12 the thirteenth
|
||||
# berth costs about three and a half times the second, and the twentieth about
|
||||
# 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
|
||||
|
||||
|
||||
# Offices in the order they were built, which is the order they are matched to
|
||||
|
|
@ -800,16 +810,62 @@ func supplied_fleet_office_count() -> int:
|
|||
# What one berth costs per charge, for the vessel actually sitting in it. An
|
||||
# empty answer means this vessel is free to keep - which is true of nothing
|
||||
# the crafter builds, and is what an unknown type falls back to.
|
||||
func fleet_office_upkeep(vessel_type: String) -> Dictionary:
|
||||
# What one berth costs per charge. `slot_index` is the fleet position it
|
||||
# keeps, so the first berth past the free one pays the base rate and every
|
||||
# berth after it pays the compounded one (#220).
|
||||
func fleet_office_upkeep(vessel_type: String, slot_index: int = BASE_FLEET_SIZE) -> Dictionary:
|
||||
var upkeep: Dictionary = {}
|
||||
var scale: float = fleet_upkeep_scale(slot_index)
|
||||
for resource_type in product_costs(vessel_type):
|
||||
var share: int = int(ceil(float(product_costs(vessel_type)[resource_type])
|
||||
/ float(FLEET_OFFICE_UPKEEP_DIVISOR)))
|
||||
/ float(FLEET_OFFICE_UPKEEP_DIVISOR) * scale))
|
||||
if share > 0:
|
||||
upkeep[resource_type] = share
|
||||
return upkeep
|
||||
|
||||
|
||||
# The multiplier on a berth at this fleet position. The free hull and the
|
||||
# first paid berth both sit at 1.0, so the curve starts where the charging
|
||||
# starts rather than partway up it.
|
||||
func fleet_upkeep_scale(slot_index: int) -> float:
|
||||
return pow(FLEET_UPKEEP_GROWTH, maxi(slot_index - BASE_FLEET_SIZE, 0))
|
||||
|
||||
|
||||
# The whole bill, berth by berth, for the readout #220 asks for: what each
|
||||
# one is keeping, what it is charged, and what that is per minute. Computed
|
||||
# here rather than in the menu so the numbers shown are the numbers charged -
|
||||
# a readout that recomputes them separately can disagree with the loop that
|
||||
# actually takes the resources.
|
||||
func fleet_upkeep_breakdown() -> Dictionary:
|
||||
var berths: Array = []
|
||||
var per_minute: Dictionary = {}
|
||||
var charges_per_minute: float = 60.0 / FLEET_OFFICE_UPKEEP_SECONDS
|
||||
var offices: Array = fleet_offices()
|
||||
for i in range(offices.size()):
|
||||
var slot_index: int = i + BASE_FLEET_SIZE
|
||||
var vessel_type: String = fleet_office_vessel(slot_index)
|
||||
if vessel_type == "":
|
||||
continue # a berth standing empty is charged nothing
|
||||
var costs: Dictionary = fleet_office_upkeep(vessel_type, slot_index)
|
||||
berths.append({
|
||||
"slot": slot_index + 1,
|
||||
"vessel_type": vessel_type,
|
||||
"scale": fleet_upkeep_scale(slot_index),
|
||||
"costs": costs,
|
||||
"starved": bool(offices[i].get("starved", false)),
|
||||
})
|
||||
for resource_type in costs:
|
||||
per_minute[resource_type] = float(per_minute.get(resource_type, 0.0)) \
|
||||
+ float(costs[resource_type]) * charges_per_minute
|
||||
return {
|
||||
"berths": berths,
|
||||
"per_minute": per_minute,
|
||||
"charge_seconds": FLEET_OFFICE_UPKEEP_SECONDS,
|
||||
"divisor": FLEET_OFFICE_UPKEEP_DIVISOR,
|
||||
"growth": FLEET_UPKEEP_GROWTH,
|
||||
}
|
||||
|
||||
|
||||
# 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.
|
||||
func fleet_office_vessel(slot_index: int) -> String:
|
||||
|
|
@ -841,7 +897,8 @@ func _advance_fleet_office_upkeep(delta: float) -> void:
|
|||
office.upkeep_progress = progress
|
||||
continue
|
||||
office.upkeep_progress = progress - FLEET_OFFICE_UPKEEP_SECONDS
|
||||
var upkeep: Dictionary = fleet_office_upkeep(vessel_type)
|
||||
var upkeep: Dictionary = fleet_office_upkeep(vessel_type,
|
||||
i + BASE_FLEET_SIZE)
|
||||
var inventory: RefCounted = _inventory_for_record(office)
|
||||
var affordable: bool = true
|
||||
for resource_type in upkeep:
|
||||
|
|
|
|||
Loading…
Reference in a new issue