# Vehicle upgrades in your game (vehicle.upgrades) ## What it is for `vehicle.upgrades` lets the player improve a vehicle with modules: one module per slot (engine, wheels, storage, tool, anything the game names), fitted near a garage or a workbench for a cost in items. A module changes how the vehicle drives (top speed, acceleration, grip, steering, braking), adds cargo slots, or supplies the driver's needs. A module is a document of the `vehicle_modules` collection. The fitted modules live on the vehicle, so the save keeps them. The garage screen is a `ui.webui` fragment. The cars themselves come from `vehicle.driveable`: see the [vehicles guide](guide:vehicles). ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "vehicle.upgrades" } gripforge_gamekit_configure { project_id: "gkp_…", id: "vehicle.upgrades", config: { "stations": ["workbench", "garage"], "reach": 8, "cargoLabel": "Cargo" } } ``` It requires `vehicle.drive` (from `vehicle.driveable`), `input.actions` and `game.engine`. Cost needs an inventory (`inventory.basic`), stations come from the craft kits, cargo from `storage.containers`, needs from `survival.needs`; each is optional. ## First example A module with a cost, fitted with the service; the trace replays this against the Rust kit: ```json { "id": "engine_2", "name": "Engine II", "slot": "engine", "stats": { "topSpeed": 1.5, "acceleration": 1.25 }, "cost": [{ "item": "scrap", "count": 2 }, { "item": "bolt" }] } ``` ```text vehicle.upgrades.fit("e2", "engine_2") // { ok: true } once the vehicle is near a station and the bag holds the cost ``` ## Settings and defaults | Setting | Type | Default | Meaning | |---|---|---|---| | `stations` | string[] | `["workbench", "garage"]` | kinds of `craft.station` where modules are fitted (empty: anywhere) | | `reach` | number (m) | `8` | how near a station the vehicle must stand, and how near the vehicle the player must stay with the garage open | | `cargoLabel` | string | `Cargo` | name of the hold that storage modules give | A `vehicle_modules` document: `{ id, name, description?, slot, stats?, storage?, needs?, cost?, unlocked?, requires? }`. `stats` holds the multipliers `topSpeed`, `acceleration`, `grip`, `steer`, `brake` (0.05 to 20, 1 unchanged); `storage` adds cargo slots; `needs` is `{ "oxygen": { "supply": 2, "drain": 0 } }`, applied while the player drives; `cost` is a list of `{ item, count }`; `unlocked: false` hides it until unlocked; `requires` lists modules that must already be fitted in other slots. The fitted modules multiply together. ## Services, events, variables - `vehicle.upgrades` (service): `modules()`, `fitted(vehicle)`, `fit(vehicle, module)` (answers `{ ok, reason? }`), `unfit(vehicle, slot)`, `unlock(id)`, `unlocked(id)`, `stats(vehicle)`, `cargo(vehicle)`, `canFitHere(vehicle)`, `open(vehicle?)`, `close()`, `current()`. - Events: `vehicle.module_fitted` (with the module it replaced), `vehicle.module_removed` (no refund), `vehicle.module_unlocked`, `garage.opened`, `garage.closed`. - Variables: `garage.open`, `vehicle.modules`. - Content: action `unlock_vehicle_module`, condition `vehicle_has_module`, and the archetype component `vehicle_modules` (`{ "engine": "engine_2" }`) for a vehicle placed with modules already fitted, at no cost. - Input: `garage_menu` (V) opens the garage by the vehicle; using a vehicle near a station opens it too. ## Hooks and modding | Hook | Payload | What a tap may do | |---|---|---| | `vehicle.fit` | `vehicle`, `module`, `slot`, `replaced`, `costScale` | scale the cost (`costScale`, 0 is free) or refuse the fit | | `vehicle.unfit` | `vehicle`, `module`, `slot` | cancel (a welded part): the module stays | | `vehicle.unlock_module` | `module`, `slot` | cancel: it stays locked | | `garage.open` | `vehicle`, `station` | cancel: the garage stays closed | ```json { "id": "sale", "hook": "vehicle.fit", "match": { "slot": "engine" }, "mul": { "costScale": 0.5 } } ``` A mod can change the three settings and add, patch or remove documents of `vehicle_modules`. ## When it does not do what you expect `fit` answers `{ ok: false, reason }`: `unknown` (no such module), `vehicle`, `locked`, `requires`, `station` (no station of a configured kind within `reach`), `cost` (the bag lacks items), `fitted` (already in place), or the reason of a hook tap. ## Limits No refund when a module is replaced or removed. Removing a storage module never drops what the hold contains. Web and native players run it; the garage screen is drawn by the `ui.webui` fragment. ## Versions Kit version 1.1.0: adds the hooks and the moddable collection.