diff --git a/app/components/building/recycleTab.test.tsx b/app/components/building/recycleTab.test.tsx index b0723ec6..29f9892e 100644 --- a/app/components/building/recycleTab.test.tsx +++ b/app/components/building/recycleTab.test.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { Building, RecycleStatus } from 'app/interfaces'; -import BodyRPCStore from 'app/stores/rpc/body'; +import BodyRPCStore, { emptyBodyData } from 'app/stores/rpc/body'; +import MenuStore from 'app/stores/menu'; import lacuna from 'app/lacuna'; import createQueryWrapper from 'app/testing/queryWrapper'; import { RecycleForm } from 'app/components/building/recycleTab'; @@ -38,7 +39,8 @@ const inputs = () => screen.getAllByRole('spinbutton') as HTMLInputElement[]; beforeEach(() => { // Comfortably above max_recycle, so the building's own ceiling is what binds. - BodyRPCStore.waste_stored = 5000; + MenuStore.changePlanet(100); + BodyRPCStore.bodies.set(100, { ...emptyBodyData(100), waste_stored: 5000 }); }); test('the header reports the ceiling and the hourly rate', () => { @@ -90,7 +92,7 @@ test('an amount over the building ceiling is refused without a request', async ( test('an amount over the waste on hand is refused without a request', async () => { const user = userEvent.setup(); - BodyRPCStore.waste_stored = 10; + BodyRPCStore.bodies.set(100, { ...emptyBodyData(100), waste_stored: 10 }); renderForm(); diff --git a/app/components/resources.test.tsx b/app/components/resources.test.tsx index 7b5d4d46..c6f0a0ea 100644 --- a/app/components/resources.test.tsx +++ b/app/components/resources.test.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import lacuna from 'app/lacuna'; -import BodyRPCStore from 'app/stores/rpc/body'; +import BodyRPCStore, { emptyBodyData } from 'app/stores/rpc/body'; +import MenuStore from 'app/stores/menu'; import createQueryWrapper from 'app/testing/queryWrapper'; import ResourcesWindow from 'app/components/resources'; @@ -66,7 +67,8 @@ const renderWindow = (props: React.ComponentProps = {}) beforeEach(() => { mockedGetBuildingsResources.mockResolvedValue({ result: response }); - BodyRPCStore.id = 100; + MenuStore.changePlanet(100); + BodyRPCStore.bodies.set(100, emptyBodyData(100)); }); test('opens on the Food tab and lists only buildings that touch food', async () => { @@ -112,17 +114,18 @@ test('the Waste tab is inverted, so falling waste is shown in green', async () = test('the Overview tab describes each resource from the body store', async () => { const user = userEvent.setup(); - BodyRPCStore.food_stored = 40000; - BodyRPCStore.food_capacity = 40000; - BodyRPCStore.food_hour = 100; - - BodyRPCStore.water_stored = 100; - BodyRPCStore.water_capacity = 1000; - BodyRPCStore.water_hour = 0; - - BodyRPCStore.energy_stored = 500; - BodyRPCStore.energy_capacity = 1000; - BodyRPCStore.energy_hour = -100; + BodyRPCStore.bodies.set(100, { + ...emptyBodyData(100), + food_stored: 40000, + food_capacity: 40000, + food_hour: 100, + water_stored: 100, + water_capacity: 1000, + water_hour: 0, + energy_stored: 500, + energy_capacity: 1000, + energy_hour: -100, + }); renderWindow(); diff --git a/app/components/trade/supplyChainsTab.test.tsx b/app/components/trade/supplyChainsTab.test.tsx index 80ff47ca..f79145d8 100644 --- a/app/components/trade/supplyChainsTab.test.tsx +++ b/app/components/trade/supplyChainsTab.test.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { Building } from 'app/interfaces'; -import BodyRPCStore from 'app/stores/rpc/body'; +import BodyRPCStore, { emptyBodyData } from 'app/stores/rpc/body'; +import MenuStore from 'app/stores/menu'; import EmpireRPCStore from 'app/stores/rpc/empire'; import lacuna from 'app/lacuna'; import createQueryWrapper from 'app/testing/queryWrapper'; @@ -41,7 +42,8 @@ beforeEach(() => { mockedView.mockResolvedValue({ result: response }); mockedConfirm.mockImplementation((_message: string, yes: () => void) => yes()); - BodyRPCStore.id = 100; + MenuStore.changePlanet(100); + BodyRPCStore.bodies.set(100, emptyBodyData(100)); EmpireRPCStore.bodies.colonies = [ { id: 100, name: 'Here' }, { id: 200, name: 'There' }, diff --git a/app/stores/rpc/body.ts b/app/stores/rpc/body.ts index a325f006..85915950 100644 --- a/app/stores/rpc/body.ts +++ b/app/stores/rpc/body.ts @@ -3,313 +3,588 @@ import * as util from 'app/util'; import { types } from '@tlecommunity/client'; import queryClient from 'app/queryClient'; import queryKeys from 'app/queryKeys'; +import MenuStore from 'app/stores/menu'; const { int } = util; +interface BodyData { + id: number; + x: number; + y: number; + star_id: number; + star_name: string; + orbit: number; + type: string; + name: string; + image: string; + surfaceImage: string; + size: number; + water: number; + ore: { + anthracite: number; + bauxite: number; + beryl: number; + chromite: number; + chalcopyrite: number; + fluorite: number; + galena: number; + goethite: number; + gold: number; + gypsum: number; + halite: number; + kerogen: number; + magnetite: number; + methane: number; + monazite: number; + rutile: number; + sulfur: number; + trona: number; + uraninite: number; + zircon: number; + }; + empire: { + id: number; + name: string; + alignment: string; + is_isolationist: number; + }; + station: { + id: number; + x: number; + y: number; + name: string; + }; + needs_surface_refresh: number; + building_count: number; + build_queue_size: number; + build_queue_len: number; + plots_available: number; + happiness: number; + happiness_hour: number; + unhappy_date: string; + neutral_entry: string; + propaganda_boost: number; + food_stored: number; + food_capacity: number; + food_hour: number; + energy_stored: number; + energy_capacity: number; + energy_hour: number; + ore_hour: number; + ore_capacity: number; + ore_stored: number; + population: number; + waste_hour: number; + waste_stored: number; + waste_capacity: number; + water_stored: number; + water_hour: number; + water_capacity: number; + skip_incoming_ships: number; + num_incoming_enemy: number; + num_incoming_ally: number; + num_incoming_own: number; + incoming_enemy_ships: types.SpacePort.Ship[]; + incoming_ally_ships: types.SpacePort.Ship[]; + incoming_own_ships: types.SpacePort.Ship[]; + alliance: { + id: number; + name: string; + }; + influence: { + total: number; + spent: number; + }; +} + +export function emptyBodyData(id = 0): BodyData { + return { + id, + x: 0, + y: 0, + star_id: 0, + star_name: '', + orbit: 0, + type: '', + name: '', + image: '', + surfaceImage: '', + size: 0, + water: 0, + ore: { + anthracite: 0, + bauxite: 0, + beryl: 0, + chromite: 0, + chalcopyrite: 0, + fluorite: 0, + galena: 0, + goethite: 0, + gold: 0, + gypsum: 0, + halite: 0, + kerogen: 0, + magnetite: 0, + methane: 0, + monazite: 0, + rutile: 0, + sulfur: 0, + trona: 0, + uraninite: 0, + zircon: 0, + }, + empire: { + id: 0, + name: '', + alignment: '', + is_isolationist: 0, + }, + station: { + id: 0, + x: 0, + y: 0, + name: '', + }, + needs_surface_refresh: 0, + building_count: 0, + build_queue_size: 0, + build_queue_len: 0, + plots_available: 0, + happiness: 0, + happiness_hour: 0, + unhappy_date: '01 13 2014 16:11:21 +0600', + neutral_entry: '01 13 2014 16:11:21 +0600', + propaganda_boost: 0, + food_stored: 0, + food_capacity: 0, + food_hour: 0, + energy_stored: 0, + energy_capacity: 0, + energy_hour: 0, + ore_hour: 0, + ore_capacity: 0, + ore_stored: 0, + population: 0, + waste_hour: 0, + waste_stored: 0, + waste_capacity: 0, + water_stored: 0, + water_hour: 0, + water_capacity: 0, + skip_incoming_ships: 0, + num_incoming_enemy: 0, + num_incoming_ally: 0, + num_incoming_own: 0, + incoming_enemy_ships: [], + incoming_ally_ships: [], + incoming_own_ships: [], + alliance: { + id: 0, + name: '', + }, + influence: { + total: 0, + spent: 0, + }, + }; +} + +function tickResource( + production: number, + capacity: number | undefined, + stored: number, + stopAtZero: boolean +) { + const amount = production / 60 / 60; + const rv = stored + amount; + + if (typeof capacity !== 'undefined' && rv > capacity) { + return Math.floor(capacity); + } + if (rv < 0 && stopAtZero) { + return 0; + } + return Math.floor(rv); +} + class BodyRPCStore { - id = 0; + // Status blocks keyed by body id. The server can push a status block for a + // body other than the one currently open (e.g. a stale block riding along + // on a response for a request made just before the player switched + // planets), so we keep every body's data in its own slot instead of + // overwriting a single shared one, and read "the current body" through + // MenuStore.planetId below. + bodies = new Map(); - x = 0; + constructor() { + makeAutoObservable(this); + } - y = 0; + get current(): BodyData { + return this.bodies.get(MenuStore.planetId) ?? emptyBodyData(MenuStore.planetId); + } - star_id = 0; + get id() { + return this.current.id; + } - star_name = ''; + get x() { + return this.current.x; + } - orbit = 0; + get y() { + return this.current.y; + } - type = ''; + get star_id() { + return this.current.star_id; + } - name = ''; + get star_name() { + return this.current.star_name; + } - image = ''; + get orbit() { + return this.current.orbit; + } - surfaceImage = ''; + get type() { + return this.current.type; + } - size = 0; + get name() { + return this.current.name; + } - water = 0; + get image() { + return this.current.image; + } - ore = { - anthracite: 0, - bauxite: 0, - beryl: 0, - chromite: 0, - chalcopyrite: 0, - fluorite: 0, - galena: 0, - goethite: 0, - gold: 0, - gypsum: 0, - halite: 0, - kerogen: 0, - magnetite: 0, - methane: 0, - monazite: 0, - rutile: 0, - sulfur: 0, - trona: 0, - uraninite: 0, - zircon: 0, - }; + get surfaceImage() { + return this.current.surfaceImage; + } - empire = { - id: 0, - name: '', - alignment: '', - is_isolationist: 0, - }; + get size() { + return this.current.size; + } - station = { - id: 0, - x: 0, - y: 0, - name: '', - }; + get water() { + return this.current.water; + } - needs_surface_refresh = 0; + get ore() { + return this.current.ore; + } + + get empire() { + return this.current.empire; + } + + get station() { + return this.current.station; + } - building_count = 0; + get needs_surface_refresh() { + return this.current.needs_surface_refresh; + } - build_queue_size = 0; + get building_count() { + return this.current.building_count; + } - build_queue_len = 0; + get build_queue_size() { + return this.current.build_queue_size; + } - plots_available = 0; + get build_queue_len() { + return this.current.build_queue_len; + } - happiness = 0; + get plots_available() { + return this.current.plots_available; + } - happiness_hour = 0; + get happiness() { + return this.current.happiness; + } - unhappy_date = '01 13 2014 16:11:21 +0600'; + get happiness_hour() { + return this.current.happiness_hour; + } - neutral_entry = '01 13 2014 16:11:21 +0600'; + get unhappy_date() { + return this.current.unhappy_date; + } - propaganda_boost = 0; + get neutral_entry() { + return this.current.neutral_entry; + } - food_stored = 0; + get propaganda_boost() { + return this.current.propaganda_boost; + } - food_capacity = 0; + get food_stored() { + return this.current.food_stored; + } - food_hour = 0; + get food_capacity() { + return this.current.food_capacity; + } - energy_stored = 0; + get food_hour() { + return this.current.food_hour; + } - energy_capacity = 0; + get energy_stored() { + return this.current.energy_stored; + } - energy_hour = 0; + get energy_capacity() { + return this.current.energy_capacity; + } - ore_hour = 0; + get energy_hour() { + return this.current.energy_hour; + } - ore_capacity = 0; + get ore_hour() { + return this.current.ore_hour; + } - ore_stored = 0; + get ore_capacity() { + return this.current.ore_capacity; + } - population = 0; + get ore_stored() { + return this.current.ore_stored; + } - waste_hour = 0; + get population() { + return this.current.population; + } - waste_stored = 0; + get waste_hour() { + return this.current.waste_hour; + } - waste_capacity = 0; + get waste_stored() { + return this.current.waste_stored; + } - water_stored = 0; + get waste_capacity() { + return this.current.waste_capacity; + } - water_hour = 0; + get water_stored() { + return this.current.water_stored; + } - water_capacity = 0; + get water_hour() { + return this.current.water_hour; + } - skip_incoming_ships = 0; + get water_capacity() { + return this.current.water_capacity; + } - num_incoming_enemy = 0; + get skip_incoming_ships() { + return this.current.skip_incoming_ships; + } - num_incoming_ally = 0; + get num_incoming_enemy() { + return this.current.num_incoming_enemy; + } - num_incoming_own = 0; + get num_incoming_ally() { + return this.current.num_incoming_ally; + } - incoming_enemy_ships: types.SpacePort.Ship[] = []; + get num_incoming_own() { + return this.current.num_incoming_own; + } - incoming_ally_ships: types.SpacePort.Ship[] = []; + get incoming_enemy_ships() { + return this.current.incoming_enemy_ships; + } - incoming_own_ships: types.SpacePort.Ship[] = []; + get incoming_ally_ships() { + return this.current.incoming_ally_ships; + } - alliance = { - id: 0, - name: '', - }; + get incoming_own_ships() { + return this.current.incoming_own_ships; + } - influence = { - total: 0, - spent: 0, - }; + get alliance() { + return this.current.alliance; + } - constructor() { - makeAutoObservable(this); + get influence() { + return this.current.influence; } - clear() {} + clear() { + this.bodies.clear(); + } update(body: types.Status.BodyBlock) { - this.id = body.id; - this.x = int(body.x); - this.y = int(body.y); - this.star_id = int(body.star_id); - this.star_name = body.star_name; - this.orbit = int(body.orbit); - this.type = body.type; - this.name = body.name; - this.image = body.image; - this.surfaceImage = body.image.replace(/-\d+$/, ''); - this.size = int(body.size); - this.water = body.water; - - this.ore.anthracite = body.ore.anthracite; - this.ore.bauxite = body.ore.bauxite; - this.ore.beryl = body.ore.beryl; - this.ore.chromite = body.ore.chromite; - this.ore.chalcopyrite = body.ore.chalcopyrite; - this.ore.fluorite = body.ore.fluorite; - this.ore.galena = body.ore.galena; - this.ore.goethite = body.ore.goethite; - this.ore.gold = body.ore.gold; - this.ore.gypsum = body.ore.gypsum; - this.ore.halite = body.ore.halite; - this.ore.kerogen = body.ore.kerogen; - this.ore.magnetite = body.ore.magnetite; - this.ore.methane = body.ore.methane; - this.ore.monazite = body.ore.monazite; - this.ore.rutile = body.ore.rutile; - this.ore.sulfur = body.ore.sulfur; - this.ore.trona = body.ore.trona; - this.ore.uraninite = body.ore.uraninite; - this.ore.zircon = body.ore.zircon; + const next = { ...(this.bodies.get(body.id) ?? emptyBodyData(body.id)) }; + + next.id = body.id; + next.x = int(body.x); + next.y = int(body.y); + next.star_id = int(body.star_id); + next.star_name = body.star_name; + next.orbit = int(body.orbit); + next.type = body.type; + next.name = body.name; + next.image = body.image; + next.surfaceImage = body.image.replace(/-\d+$/, ''); + next.size = int(body.size); + next.water = body.water; + + next.ore = { + anthracite: body.ore.anthracite, + bauxite: body.ore.bauxite, + beryl: body.ore.beryl, + chromite: body.ore.chromite, + chalcopyrite: body.ore.chalcopyrite, + fluorite: body.ore.fluorite, + galena: body.ore.galena, + goethite: body.ore.goethite, + gold: body.ore.gold, + gypsum: body.ore.gypsum, + halite: body.ore.halite, + kerogen: body.ore.kerogen, + magnetite: body.ore.magnetite, + methane: body.ore.methane, + monazite: body.ore.monazite, + rutile: body.ore.rutile, + sulfur: body.ore.sulfur, + trona: body.ore.trona, + uraninite: body.ore.uraninite, + zircon: body.ore.zircon, + }; if (body.empire) { - this.empire.id = body.empire.id; - this.empire.name = body.empire.name; - this.empire.alignment = body.empire.alignment; - this.empire.is_isolationist = body.empire.is_isolationist; + next.empire = { + id: body.empire.id, + name: body.empire.name, + alignment: body.empire.alignment, + is_isolationist: body.empire.is_isolationist, + }; } if (body.station) { - this.station.id = body.station.id; - this.station.x = body.station.x; - this.station.y = body.station.y; - this.station.name = body.station.name; + next.station = { + id: body.station.id, + x: body.station.x, + y: body.station.y, + name: body.station.name, + }; } if (typeof body.needs_surface_refresh !== 'undefined') - this.needs_surface_refresh = body.needs_surface_refresh; - if (typeof body.building_count !== 'undefined') this.building_count = int(body.building_count); - if (typeof body.build_queue_size !== 'undefined') this.build_queue_size = body.build_queue_size; - if (typeof body.build_queue_len !== 'undefined') this.build_queue_len = body.build_queue_len; + next.needs_surface_refresh = body.needs_surface_refresh; + if (typeof body.building_count !== 'undefined') next.building_count = int(body.building_count); + if (typeof body.build_queue_size !== 'undefined') next.build_queue_size = body.build_queue_size; + if (typeof body.build_queue_len !== 'undefined') next.build_queue_len = body.build_queue_len; if (typeof body.plots_available !== 'undefined') - this.plots_available = int(body.plots_available); - if (typeof body.happiness !== 'undefined') this.happiness = body.happiness; - if (typeof body.happiness_hour !== 'undefined') this.happiness_hour = body.happiness_hour; - if (typeof body.unhappy_date !== 'undefined') this.unhappy_date = body.unhappy_date; - if (typeof body.neutral_entry !== 'undefined') this.neutral_entry = body.neutral_entry; - if (typeof body.propaganda_boost !== 'undefined') this.propaganda_boost = body.propaganda_boost; - if (typeof body.food_stored !== 'undefined') this.food_stored = body.food_stored; - if (typeof body.food_capacity !== 'undefined') this.food_capacity = body.food_capacity; - if (typeof body.food_hour !== 'undefined') this.food_hour = body.food_hour; - if (typeof body.energy_stored !== 'undefined') this.energy_stored = body.energy_stored; - if (typeof body.energy_capacity !== 'undefined') this.energy_capacity = body.energy_capacity; - if (typeof body.energy_hour !== 'undefined') this.energy_hour = body.energy_hour; - if (typeof body.ore_hour !== 'undefined') this.ore_hour = body.ore_hour; - if (typeof body.ore_capacity !== 'undefined') this.ore_capacity = body.ore_capacity; - if (typeof body.ore_stored !== 'undefined') this.ore_stored = body.ore_stored; - if (typeof body.population !== 'undefined') this.population = body.population; - if (typeof body.waste_hour !== 'undefined') this.waste_hour = body.waste_hour; - if (typeof body.waste_stored !== 'undefined') this.waste_stored = body.waste_stored; - if (typeof body.waste_capacity !== 'undefined') this.waste_capacity = body.waste_capacity; - if (typeof body.water_stored !== 'undefined') this.water_stored = body.water_stored; - if (typeof body.water_hour !== 'undefined') this.water_hour = body.water_hour; - if (typeof body.water_capacity !== 'undefined') this.water_capacity = body.water_capacity; + next.plots_available = int(body.plots_available); + if (typeof body.happiness !== 'undefined') next.happiness = body.happiness; + if (typeof body.happiness_hour !== 'undefined') next.happiness_hour = body.happiness_hour; + if (typeof body.unhappy_date !== 'undefined') next.unhappy_date = body.unhappy_date; + if (typeof body.neutral_entry !== 'undefined') next.neutral_entry = body.neutral_entry; + if (typeof body.propaganda_boost !== 'undefined') next.propaganda_boost = body.propaganda_boost; + if (typeof body.food_stored !== 'undefined') next.food_stored = body.food_stored; + if (typeof body.food_capacity !== 'undefined') next.food_capacity = body.food_capacity; + if (typeof body.food_hour !== 'undefined') next.food_hour = body.food_hour; + if (typeof body.energy_stored !== 'undefined') next.energy_stored = body.energy_stored; + if (typeof body.energy_capacity !== 'undefined') next.energy_capacity = body.energy_capacity; + if (typeof body.energy_hour !== 'undefined') next.energy_hour = body.energy_hour; + if (typeof body.ore_hour !== 'undefined') next.ore_hour = body.ore_hour; + if (typeof body.ore_capacity !== 'undefined') next.ore_capacity = body.ore_capacity; + if (typeof body.ore_stored !== 'undefined') next.ore_stored = body.ore_stored; + if (typeof body.population !== 'undefined') next.population = body.population; + if (typeof body.waste_hour !== 'undefined') next.waste_hour = body.waste_hour; + if (typeof body.waste_stored !== 'undefined') next.waste_stored = body.waste_stored; + if (typeof body.waste_capacity !== 'undefined') next.waste_capacity = body.waste_capacity; + if (typeof body.water_stored !== 'undefined') next.water_stored = body.water_stored; + if (typeof body.water_hour !== 'undefined') next.water_hour = body.water_hour; + if (typeof body.water_capacity !== 'undefined') next.water_capacity = body.water_capacity; if (typeof body.skip_incoming_ships !== 'undefined') - this.skip_incoming_ships = body.skip_incoming_ships; + next.skip_incoming_ships = body.skip_incoming_ships; if (typeof body.num_incoming_own !== 'undefined') - this.num_incoming_own = int(body.num_incoming_own); + next.num_incoming_own = int(body.num_incoming_own); if (typeof body.num_incoming_ally !== 'undefined') - this.num_incoming_ally = int(body.num_incoming_ally); + next.num_incoming_ally = int(body.num_incoming_ally); if (typeof body.num_incoming_enemy !== 'undefined') - this.num_incoming_enemy = int(body.num_incoming_enemy); + next.num_incoming_enemy = int(body.num_incoming_enemy); if (typeof body.incoming_enemy_ships !== 'undefined') - this.incoming_enemy_ships = body.incoming_enemy_ships; + next.incoming_enemy_ships = body.incoming_enemy_ships; if (typeof body.incoming_ally_ships !== 'undefined') - this.incoming_ally_ships = body.incoming_ally_ships; + next.incoming_ally_ships = body.incoming_ally_ships; if (typeof body.incoming_own_ships !== 'undefined') - this.incoming_own_ships = body.incoming_own_ships; + next.incoming_own_ships = body.incoming_own_ships; if (body.alliance) { - this.alliance = body.alliance; + next.alliance = body.alliance; } if (body.influence) { - this.influence = body.influence; + next.influence = body.influence; } + this.bodies.set(body.id, next); + if (body.needs_surface_refresh) { - queryClient.invalidateQueries({ queryKey: queryKeys.body.buildings(this.id) }); + queryClient.invalidateQueries({ queryKey: queryKeys.body.buildings(body.id) }); } } tick() { - const tickResource = function ( - production: number, - capacity: number | undefined, - stored: number, - stopAtZero: boolean - ) { - const amount = production / 60 / 60; - const rv = stored + amount; - - if (typeof capacity !== 'undefined' && rv > capacity) { - return Math.floor(capacity); - } - if (rv < 0 && stopAtZero) { - return 0; - } - return Math.floor(rv); - }; - - this.food_stored = tickResource(this.food_hour, this.food_capacity, this.food_stored, true); - this.ore_stored = tickResource(this.ore_hour, this.ore_capacity, this.ore_stored, true); - this.water_stored = tickResource(this.water_hour, this.water_capacity, this.water_stored, true); - this.energy_stored = tickResource( - this.energy_hour, - this.energy_capacity, - this.energy_stored, - true - ); - this.waste_stored = tickResource(this.waste_hour, this.waste_capacity, this.waste_stored, true); - this.happiness = tickResource(this.happiness_hour, undefined, this.happiness, false); + for (const [id, data] of this.bodies) { + this.bodies.set(id, { + ...data, + food_stored: tickResource(data.food_hour, data.food_capacity, data.food_stored, true), + ore_stored: tickResource(data.ore_hour, data.ore_capacity, data.ore_stored, true), + water_stored: tickResource(data.water_hour, data.water_capacity, data.water_stored, true), + energy_stored: tickResource( + data.energy_hour, + data.energy_capacity, + data.energy_stored, + true + ), + waste_stored: tickResource(data.waste_hour, data.waste_capacity, data.waste_stored, true), + happiness: tickResource(data.happiness_hour, undefined, data.happiness, false), + }); + } } get food_percent_full() { - if (this.food_capacity === 0) return 100; - return Math.ceil((this.food_stored / this.food_capacity) * 100); + if (this.current.food_capacity === 0) return 100; + return Math.ceil((this.current.food_stored / this.current.food_capacity) * 100); } get ore_percent_full() { - if (this.ore_capacity === 0) return 100; - return Math.ceil((this.ore_stored / this.ore_capacity) * 100); + if (this.current.ore_capacity === 0) return 100; + return Math.ceil((this.current.ore_stored / this.current.ore_capacity) * 100); } get water_percent_full() { - if (this.water_capacity === 0) return 100; - return Math.ceil((this.water_stored / this.water_capacity) * 100); + if (this.current.water_capacity === 0) return 100; + return Math.ceil((this.current.water_stored / this.current.water_capacity) * 100); } get energy_percent_full() { - if (this.energy_capacity === 0) return 100; - return Math.ceil((this.energy_stored / this.energy_capacity) * 100); + if (this.current.energy_capacity === 0) return 100; + return Math.ceil((this.current.energy_stored / this.current.energy_capacity) * 100); } get waste_percent_full() { - if (this.waste_capacity === 0) return 100; - return Math.ceil((this.waste_stored / this.waste_capacity) * 100); + if (this.current.waste_capacity === 0) return 100; + return Math.ceil((this.current.waste_stored / this.current.waste_capacity) * 100); } }