extends SceneTree # Headless test: run with # godot --headless -s res://tests/test_local_costs_use_hold.gd # Forgejo #158: "any costs local to a building (creating a ship in the ship # crafter, researching in the lab...) can also be paid thanks to the ship # inventory." # # #12 made a building's production cost resources physically DELIVERED to it # rather than money out of the bank, and that principle stands - the bank is # still not raided from here. What changes is that the fleet's own hold # counts as well, because it is the one other place resources physically # are: a hull that flew in carrying gas can hand it over, and making the # player park, open the transfer menu and move it across by hand was # ceremony rather than a decision. # # Covers: # A. a crafter with an empty stock builds out of the fleet's hold # B. the building's OWN stock is spent first - what was hauled in goes # before the hold the player is still flying around with # C. a cost split across both is taken from both # D. the bank is still not touched: banked ore alone does not start an # order, which is the #12 rule this must not quietly undo # E. all or nothing - an order that cannot be paid for takes nothing # F. the same for lab research # G. and only WHERE THE FLEET IS. A crafter across the map must not reach # into the hold and spend ore the player withdrew to carry elsewhere - # which is the same complaint that got automatic banking removed in #24, # arriving from the other direction. test_manual_supply_route caught the # first draft of this doing exactly that. var game_state: Node var crafter_id: int = 0 var lab_id: int = 0 func _fail(message: String) -> bool: print("TEST_LOCAL_COSTS_USE_HOLD: FAIL (%s)" % message) quit(1) return true # Emptied rather than replaced: GameState.INVENTORY is reached through the # autoload's name, which an `extends SceneTree` script cannot compile. func _reset() -> void: for resource_type in game_state.resource_types(): var id: String = String(resource_type) game_state.bank_inventory.set_amount(id, 0) game_state.fleet_inventory.set_amount(id, 0) game_state.building_inventory(crafter_id).set_amount(id, 0) game_state.building_inventory(lab_id).set_amount(id, 0) game_state.fleet_inventory.total_capacity = 500 for b in game_state.buildings: b["orders"] = [] b["research_orders"] = [] func _initialize() -> void: # GameState is an autoload: a bare GameState. does not # compile in a SceneTree script, so the node is fetched by name instead. game_state = root.get_node("GameState") game_state.reset_new_game() game_state.intro_active = false game_state.reset_quests() game_state.build_time_scale = 0.02 crafter_id = game_state.add_building(GameState.BUILDING_SHIP_CRAFTER, Vector2.ZERO) lab_id = game_state.add_building(GameState.BUILDING_SHIP_LAB, Vector2(500.0, 0.0)) # Parked on the crafter. The hold only pays for what the fleet is # standing at - see G below, which is the half of #158 that matters. game_state.fleet_position = Vector2.ZERO var cost: int = int(game_state.product_costs(GameState.VESSEL_INTERCEPTOR).get( GameState.RESOURCE_ORE, 0)) if cost <= 1: _fail("an interceptor costs %d ore - nothing here would be measurable" % cost) return # --- A. built out of the hold alone --------------------------------- _reset() game_state.fleet_inventory.deposit(GameState.RESOURCE_ORE, cost) if not game_state.queue_order(crafter_id, GameState.VESSEL_INTERCEPTOR): _fail("the order would not queue") return game_state._advance_production(0.01) var order: Dictionary = game_state.order_list(crafter_id)[0] if not bool(order.started): _fail("a crafter with the fleet parked on it and %d ore aboard did not start" % cost) return if game_state.fleet_resource(GameState.RESOURCE_ORE) != 0: _fail("the hold still holds %d after paying" \ % game_state.fleet_resource(GameState.RESOURCE_ORE)) return print("an empty crafter built out of the fleet's hold (%d ore)" % cost) # --- B and C. the building's own stock goes first ------------------- _reset() for b in game_state.buildings: b["orders"] = [] var in_building: int = cost - 1 game_state.building_inventory(crafter_id).set_amount(GameState.RESOURCE_ORE, in_building) game_state.fleet_inventory.deposit(GameState.RESOURCE_ORE, 5) game_state.queue_order(crafter_id, GameState.VESSEL_INTERCEPTOR) game_state._advance_production(0.01) if not bool(game_state.order_list(crafter_id)[0].started): _fail("a cost split across the crafter and the hold did not start") return if game_state.building_resource(crafter_id, GameState.RESOURCE_ORE) != 0: _fail("the crafter's own %d ore was not spent first - %d left" \ % [in_building, game_state.building_resource(crafter_id, GameState.RESOURCE_ORE)]) return if game_state.fleet_resource(GameState.RESOURCE_ORE) != 4: _fail("the hold paid %d, expected to cover the last 1" \ % (5 - game_state.fleet_resource(GameState.RESOURCE_ORE))) return print("stock first, then the hold: %d from the crafter and 1 from the fleet" % in_building) # --- D. the bank is still not raided -------------------------------- _reset() game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 5000) game_state.queue_order(crafter_id, GameState.VESSEL_INTERCEPTOR) game_state._advance_production(0.01) if bool(game_state.order_list(crafter_id)[0].started): _fail("the order started out of the BANK - #12's rule is undone") return if game_state.banked_resource(GameState.RESOURCE_ORE) != 5000: _fail("the bank was charged %d for an order that did not start" \ % (5000 - game_state.banked_resource(GameState.RESOURCE_ORE))) return print("banked ore alone does not start an order, and is not touched") # --- E. all or nothing ---------------------------------------------- _reset() game_state.fleet_inventory.deposit(GameState.RESOURCE_ORE, cost - 1) game_state.queue_order(crafter_id, GameState.VESSEL_INTERCEPTOR) game_state._advance_production(0.01) if bool(game_state.order_list(crafter_id)[0].started): _fail("an order started one ore short") return if game_state.fleet_resource(GameState.RESOURCE_ORE) != cost - 1: _fail("an order that could not be paid for still took %d from the hold" \ % (cost - 1 - game_state.fleet_resource(GameState.RESOURCE_ORE))) return print("one short takes nothing at all") # --- F. and the lab does the same ----------------------------------- _reset() var upgrade: String = "int_damage" var research: Dictionary = game_state.research_costs(upgrade) if research.is_empty(): _fail("the upgrade %s costs nothing - F would prove nothing" % upgrade) return game_state.research_points = 99 game_state.fleet_position = Vector2(500.0, 0.0) for resource_type in research: game_state.fleet_inventory.deposit(String(resource_type), int(research[resource_type])) if not game_state.queue_lab_research(lab_id, upgrade): _fail("the research would not queue") return game_state._advance_lab_research(0.01) if not bool(game_state.lab_research_order(lab_id).started): _fail("research did not start from the fleet's hold") return for resource_type in research: if game_state.fleet_resource(String(resource_type)) != 0: _fail("the hold still holds %s after paying for research" % resource_type) return print("a lab researches out of the hold too (%s)" % str(research)) # --- G. and not from across the map --------------------------------- _reset() game_state.fleet_position = Vector2(9000.0, 9000.0) game_state.fleet_inventory.deposit(GameState.RESOURCE_ORE, cost * 4) game_state.queue_order(crafter_id, GameState.VESSEL_INTERCEPTOR) game_state._advance_production(0.01) if bool(game_state.order_list(crafter_id)[0].started): _fail("a crafter on the far side of the map started out of the fleet's hold") return if game_state.fleet_resource(GameState.RESOURCE_ORE) != cost * 4: _fail("a distant crafter took %d from the hold" \ % (cost * 4 - game_state.fleet_resource(GameState.RESOURCE_ORE))) return # ...and flying back is all it takes. game_state.fleet_position = Vector2.ZERO game_state._advance_production(0.01) if not bool(game_state.order_list(crafter_id)[0].started): _fail("the order did not start once the fleet was parked on the crafter") return print("out of range it takes nothing; parked on it, the same order starts") print("TEST_LOCAL_COSTS_USE_HOLD: PASS") quit(0)