diff --git a/package-lock.json b/package-lock.json index f0cb437..ec9cc14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tlecommunity/api-spec", - "version": "1.2.2", + "version": "1.3.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tlecommunity/api-spec", - "version": "1.2.2", + "version": "1.3.2", "license": "MIT", "devDependencies": { "@types/node": "^26.2.0", diff --git a/package.json b/package.json index 80a0ac5..ff00072 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@tlecommunity/api-spec", "type": "module", - "version": "1.2.2", + "version": "1.3.2", "description": "Open API specification for The Lacuna Expanse", "author": "Natalie Rose (https://nataliethistime.com)", "license": "MIT", diff --git a/src/spec/components.ts b/src/spec/components.ts index 6e7e3c0..e9dc6d3 100644 --- a/src/spec/components.ts +++ b/src/spec/components.ts @@ -207,8 +207,9 @@ export const components = { waste: { type: 'integer' }, ore: { type: 'integer' }, time: { type: 'integer' }, + halls: { type: 'integer' }, }, - required: ['food', 'water', 'energy', 'waste', 'ore', 'time'], + required: ['time'], }, // The lightweight per-building shape returned by get_buildings/repair_list — not the full @@ -287,6 +288,107 @@ export const components = { ], }, + // A single building's contribution to the planet economy, as returned by + // get_buildings_resources: the same aggregate figures as `building_production` + // plus a per-food-type and per-ore-type production breakdown. + // - `food` is present only when the building produces food, `ore` only when + // it produces ore (Perl omits an empty map entirely). + // - `ore_hour` / `ore_capacity` / `ore` are ALL omitted for the Mining + // Ministry, whose ore output is driven by its deployed mining platforms + // rather than the building itself - hence they are not required here even + // though `building_production` requires ore_hour / ore_capacity. + building_resources: { + type: 'object', + properties: { + food_hour: { type: 'integer' }, + food_capacity: { type: 'integer' }, + energy_hour: { type: 'integer' }, + energy_capacity: { type: 'integer' }, + water_hour: { type: 'integer' }, + water_capacity: { type: 'integer' }, + waste_hour: { type: 'integer' }, + waste_capacity: { type: 'integer' }, + happiness_hour: { type: 'integer' }, + // Omitted for the Mining Ministry (see above). + ore_hour: { type: 'integer' }, + ore_capacity: { type: 'integer' }, + food: { + type: 'object', + description: 'Hourly production keyed by food type.', + additionalProperties: { type: 'integer' }, + }, + ore: { + type: 'object', + description: 'Hourly production keyed by ore type.', + additionalProperties: { type: 'integer' }, + }, + }, + required: [ + 'food_hour', + 'food_capacity', + 'energy_hour', + 'energy_capacity', + 'water_hour', + 'water_capacity', + 'waste_hour', + 'waste_capacity', + 'happiness_hour', + ], + }, + + // The per-building shape returned by get_buildings_resources — the lightweight + // grid shape (as in buildings_list) with a `resources` object added. Keyed by + // building id, one level deep (no extra `buildings` wrapper). + buildings_resources_list: { + type: 'object', + description: 'Keyed by building id.', + additionalProperties: { + type: 'object', + required: ['name', 'x', 'y', 'url', 'level', 'image', 'efficiency', 'resources'], + properties: { + name: { type: 'string' }, + x: { type: 'integer', minimum: -5, maximum: 5 }, + y: { type: 'integer', minimum: -5, maximum: 5 }, + url: { type: 'string' }, + level: { type: 'integer' }, + image: { type: 'string' }, + efficiency: { type: 'integer' }, + resources: { $ref: '#/components/schemas/building_resources' }, + // Only included when the building is being built/upgraded. + pending_build: { + type: 'object', + properties: { + seconds_remaining: { type: 'integer' }, + start: { type: 'string' }, + end: { type: 'string' }, + }, + required: ['seconds_remaining', 'start', 'end'], + }, + // Only included when the building is working (Parks, Waste Recycling, etc). + work: { + type: 'object', + properties: { + seconds_remaining: { type: 'integer' }, + start: { type: 'string' }, + end: { type: 'string' }, + }, + required: ['seconds_remaining', 'start', 'end'], + }, + // Only included when the building is damaged (efficiency < 100). + repair_costs: { + type: 'object', + properties: { + food: { type: 'integer' }, + water: { type: 'integer' }, + energy: { type: 'integer' }, + ore: { type: 'integer' }, + }, + required: ['food', 'water', 'energy', 'ore'], + }, + }, + }, + }, + // The fields common to every building's `view` response. Building-type-specific endpoints // (see src/spec/paths/buildings/*.ts) add extra sibling fields alongside this on `view`. building: { diff --git a/src/spec/paths/body.ts b/src/spec/paths/body.ts index 78ab922..59e761f 100644 --- a/src/spec/paths/body.ts +++ b/src/spec/paths/body.ts @@ -85,6 +85,38 @@ export const body = { }, }, + '/v2/body/get_buildings_resources': { + post: { + ...security(), + + ...requestBodySchema({ + type: 'object', + required: ['body_id'], + properties: { body_id: { type: 'string' } }, + }), + + responses: { + ...response200( + 'Like get_buildings, but every building entry additionally carries a `resources` object: hourly production/consumption and storage capacity for food/ore/water/waste/energy plus happiness_hour, and per-food-type / per-ore-type production breakdowns (each omitted when the building produces none of that resource).', + { + type: 'object', + required: ['buildings', 'body', 'status'], + properties: { + buildings: { $ref: '#/components/schemas/buildings_resources_list' }, + body: { + type: 'object', + required: ['surface_image'], + properties: { surface_image: { type: 'string' } }, + }, + status: statusSchema(), + }, + } + ), + ...response500(), + }, + }, + }, + '/v2/body/repair_list': { post: { ...security(), diff --git a/src/spec/paths/inbox.ts b/src/spec/paths/inbox.ts index 5e95b59..6023096 100644 --- a/src/spec/paths/inbox.ts +++ b/src/spec/paths/inbox.ts @@ -43,7 +43,7 @@ const messageSummarySchema = { has_read: { type: 'integer', enum: [1, 0] }, has_replied: { type: 'integer', enum: [1, 0] }, body_preview: { type: 'string' }, - tags: { type: 'string', enum: MESSAGE_TAGS }, + tags: { type: 'array', items: { type: 'string', enum: MESSAGE_TAGS } }, }, };