From 1a49602362c188d93935fc7fbc1015c341e17cd2 Mon Sep 17 00:00:00 2001 From: Jure Rotar Date: Thu, 16 Nov 2023 10:47:40 +0100 Subject: [PATCH] feat: implemented additional workers for server creation, added more mocks, added factions and reputations, added viewport context, code refactors --- ...fined-villages-coordinates-100x100-mock.ts | 54 - ...fined-villages-coordinates-200x200-mock.ts | 54 - ...fined-villages-coordinates-400x400-mock.ts | 54 - .../predefined-villages-coordinates-mock.ts | 112 + .../game/village/building-fields-mock.ts | 4 + __mocks__/models/game/player-mock.ts | 41 + jest-setup.ts | 5 + jest.config.ts | 4 +- src/app/(game)/error-boundary.tsx | 7 +- src/app/(game)/loader.ts | 91 +- src/app/(game)/map/components/cell.tsx | 56 +- .../(game)/map/components/isometric-map.tsx | 320 - src/app/(game)/map/page.tsx | 10 +- .../create-server-modal-content.tsx | 130 +- .../(public)/home/components/server-card.tsx | 4 +- .../workers/generate-achievements-worker.ts | 18 + .../workers/generate-effects-worker.ts | 23 + .../workers/generate-players-worker.ts | 23 + .../workers/generate-quests-worker.ts | 23 + .../workers/generate-reputations-worker.ts | 22 + .../workers/generate-villages-worker.ts | 27 +- .../workers/generate-world-map-worker.ts | 13 +- src/app/(test)/test/page.tsx | 1 - src/app/layout.tsx | 11 +- .../providers/preferences-context.tsx | 0 src/app/providers/viewport-context.tsx | 75 + src/assets/_buildings.json | 9694 ----------------- src/assets/achievements.json | 1 - src/assets/achievements.ts | 3 + src/assets/buildings.json | 1793 +-- src/assets/effects.ts | 14 + src/assets/quests.json | 1 - src/assets/quests.ts | 14 + src/assets/village-presets.ts | 19 + src/constants/breakpoints.ts | 8 - src/constants/defaults.ts | 53 - src/constants/effects.ts | 118 - src/constants/enums/react-query-keys.ts | 5 - src/constants/new-village.ts | 260 - src/constants/oasis-shapes.ts | 210 - src/database/__tests__/database.test.ts | 95 +- src/database/database.ts | 10 +- src/factories/__tests__/map-factory.test.ts | 18 +- .../__tests__/server-factory.test.ts | 2 - src/factories/achievement-factory.ts | 10 +- src/factories/effect-factory.ts | 27 + src/factories/map-factory.ts | 332 +- src/factories/player-factory.ts | 15 + src/factories/quest-factory.ts | 21 +- src/factories/reputation-factory.ts | 27 + src/factories/server-factory.ts | 3 +- src/factories/village-factory.ts | 235 +- src/hooks/game/use-current-village.ts | 6 +- src/hooks/game/use-effects.ts | 10 +- src/hooks/game/use-players.ts | 41 + src/hooks/game/use-quests.ts | 10 +- src/hooks/game/use-reputations.ts | 42 + src/hooks/game/use-villages.ts | 8 +- src/hooks/use-available-servers.ts | 10 +- .../models/database/crylite-table.ts | 2 + src/interfaces/models/game/building.ts | 11 +- src/interfaces/models/game/effect.ts | 4 +- src/interfaces/models/game/player.ts | 14 + src/interfaces/models/game/quest.ts | 4 +- src/interfaces/models/game/reputation.ts | 23 + src/interfaces/models/game/tile.ts | 17 +- src/interfaces/models/game/village.ts | 63 +- src/maps/resource-to-building-map.ts | 8 - src/utils/common.ts | 12 + src/utils/errors.ts | 19 - src/utils/game/__tests__/common.test.ts | 24 + src/utils/game/common.ts | 45 + ...te-resource-fields-from-resource-layout.ts | 14 - src/utils/workers.ts | 12 + tsconfig.json | 12 +- vite.config.ts | 17 +- 76 files changed, 1581 insertions(+), 13017 deletions(-) delete mode 100644 __mocks__/game/map/predefined-villages-coordinates-100x100-mock.ts delete mode 100644 __mocks__/game/map/predefined-villages-coordinates-200x200-mock.ts delete mode 100644 __mocks__/game/map/predefined-villages-coordinates-400x400-mock.ts create mode 100644 __mocks__/game/map/predefined-villages-coordinates-mock.ts create mode 100644 __mocks__/game/village/building-fields-mock.ts create mode 100644 __mocks__/models/game/player-mock.ts create mode 100644 jest-setup.ts delete mode 100644 src/app/(game)/map/components/isometric-map.tsx create mode 100644 src/app/(public)/workers/generate-achievements-worker.ts create mode 100644 src/app/(public)/workers/generate-effects-worker.ts create mode 100644 src/app/(public)/workers/generate-players-worker.ts create mode 100644 src/app/(public)/workers/generate-quests-worker.ts create mode 100644 src/app/(public)/workers/generate-reputations-worker.ts rename src/{ => app}/providers/preferences-context.tsx (100%) create mode 100644 src/app/providers/viewport-context.tsx delete mode 100644 src/assets/_buildings.json delete mode 100644 src/assets/achievements.json create mode 100644 src/assets/achievements.ts create mode 100644 src/assets/effects.ts delete mode 100644 src/assets/quests.json create mode 100644 src/assets/quests.ts create mode 100644 src/assets/village-presets.ts delete mode 100644 src/constants/breakpoints.ts delete mode 100644 src/constants/defaults.ts delete mode 100644 src/constants/effects.ts delete mode 100644 src/constants/enums/react-query-keys.ts delete mode 100644 src/constants/new-village.ts delete mode 100644 src/constants/oasis-shapes.ts create mode 100644 src/factories/effect-factory.ts create mode 100644 src/factories/player-factory.ts create mode 100644 src/factories/reputation-factory.ts create mode 100644 src/hooks/game/use-players.ts create mode 100644 src/hooks/game/use-reputations.ts create mode 100644 src/interfaces/models/game/player.ts create mode 100644 src/interfaces/models/game/reputation.ts delete mode 100644 src/maps/resource-to-building-map.ts delete mode 100644 src/utils/errors.ts create mode 100644 src/utils/game/__tests__/common.test.ts create mode 100644 src/utils/game/common.ts delete mode 100644 src/utils/game/create-resource-fields-from-resource-layout.ts create mode 100644 src/utils/workers.ts diff --git a/__mocks__/game/map/predefined-villages-coordinates-100x100-mock.ts b/__mocks__/game/map/predefined-villages-coordinates-100x100-mock.ts deleted file mode 100644 index 678ef3e..0000000 --- a/__mocks__/game/map/predefined-villages-coordinates-100x100-mock.ts +++ /dev/null @@ -1,54 +0,0 @@ -export const predefinedVillagesCoordinates100x100Mock = { - artifactVillagesCoordinates: [ - { - x: -17, - y: 40 - }, - { - x: 17, - y: 40 - }, - { - x: -17, - y: -40 - }, - { - x: 17, - y: -40 - }, - { - x: 40, - y: 17 - }, - { - x: 40, - y: -17 - }, - { - x: -40, - y: 17 - }, - { - x: -40, - y: -17 - } - ], - quadrantBaseCoordinates: [ - { - x: -40, - y: 40 - }, - { - x: 40, - y: 40 - }, - { - x: -40, - y: -40 - }, - { - x: 40, - y: -40 - } - ] -}; diff --git a/__mocks__/game/map/predefined-villages-coordinates-200x200-mock.ts b/__mocks__/game/map/predefined-villages-coordinates-200x200-mock.ts deleted file mode 100644 index bb25767..0000000 --- a/__mocks__/game/map/predefined-villages-coordinates-200x200-mock.ts +++ /dev/null @@ -1,54 +0,0 @@ -export const predefinedVillagesCoordinates200x200Mock = { - artifactVillagesCoordinates: [ - { - x: -33, - y: 80 - }, - { - x: 33, - y: 80 - }, - { - x: -33, - y: -80 - }, - { - x: 33, - y: -80 - }, - { - x: 80, - y: 33 - }, - { - x: 80, - y: -33 - }, - { - x: -80, - y: 33 - }, - { - x: -80, - y: -33 - } - ], - quadrantBaseCoordinates: [ - { - x: -80, - y: 80 - }, - { - x: 80, - y: 80 - }, - { - x: -80, - y: -80 - }, - { - x: 80, - y: -80 - } - ] -}; diff --git a/__mocks__/game/map/predefined-villages-coordinates-400x400-mock.ts b/__mocks__/game/map/predefined-villages-coordinates-400x400-mock.ts deleted file mode 100644 index 11606a4..0000000 --- a/__mocks__/game/map/predefined-villages-coordinates-400x400-mock.ts +++ /dev/null @@ -1,54 +0,0 @@ -export const predefinedVillagesCoordinates400x400Mock = { - artifactVillagesCoordinates: [ - { - x: -66, - y: 160 - }, - { - x: 66, - y: 160 - }, - { - x: -66, - y: -160 - }, - { - x: 66, - y: -160 - }, - { - x: 160, - y: 66 - }, - { - x: 160, - y: -66 - }, - { - x: -160, - y: 66 - }, - { - x: -160, - y: -66 - } - ], - quadrantBaseCoordinates: [ - { - x: -160, - y: 160 - }, - { - x: 160, - y: 160 - }, - { - x: -160, - y: -160 - }, - { - x: 160, - y: -160 - } - ] -}; diff --git a/__mocks__/game/map/predefined-villages-coordinates-mock.ts b/__mocks__/game/map/predefined-villages-coordinates-mock.ts new file mode 100644 index 0000000..e616c36 --- /dev/null +++ b/__mocks__/game/map/predefined-villages-coordinates-mock.ts @@ -0,0 +1,112 @@ +export const predefinedVillagesCoordinates100x100Mock = { + artifactVillagesCoordinates: [ + { + x: -17, + y: 40 + }, + { + x: 17, + y: 40 + }, + { + x: -17, + y: -40 + }, + { + x: 17, + y: -40 + }, + { + x: 40, + y: 17 + }, + { + x: 40, + y: -17 + }, + { + x: -40, + y: 17 + }, + { + x: -40, + y: -17 + } + ], +}; + +export const predefinedVillagesCoordinates200x200Mock = { + artifactVillagesCoordinates: [ + { + x: -33, + y: 80 + }, + { + x: 33, + y: 80 + }, + { + x: -33, + y: -80 + }, + { + x: 33, + y: -80 + }, + { + x: 80, + y: 33 + }, + { + x: 80, + y: -33 + }, + { + x: -80, + y: 33 + }, + { + x: -80, + y: -33 + } + ], +}; + +export const predefinedVillagesCoordinates400x400Mock = { + artifactVillagesCoordinates: [ + { + x: -66, + y: 160 + }, + { + x: 66, + y: 160 + }, + { + x: -66, + y: -160 + }, + { + x: 66, + y: -160 + }, + { + x: 160, + y: 66 + }, + { + x: 160, + y: -66 + }, + { + x: -160, + y: 66 + }, + { + x: -160, + y: -66 + } + ], +}; + + diff --git a/__mocks__/game/village/building-fields-mock.ts b/__mocks__/game/village/building-fields-mock.ts new file mode 100644 index 0000000..dbd41cc --- /dev/null +++ b/__mocks__/game/village/building-fields-mock.ts @@ -0,0 +1,4 @@ +import { BuildingField } from 'interfaces/models/game/village'; +import { villagePresets } from 'assets/village-presets'; + +export const newVillageBuildingFieldsMock: BuildingField[] = villagePresets.find(({ preset }) => preset === 'new-village')!.buildingFields; diff --git a/__mocks__/models/game/player-mock.ts b/__mocks__/models/game/player-mock.ts new file mode 100644 index 0000000..bf93896 --- /dev/null +++ b/__mocks__/models/game/player-mock.ts @@ -0,0 +1,41 @@ +import { Player } from 'interfaces/models/game/player'; + +export const playerMock: Player = { + "id": "42c53448-ab04-4ae3-8ce5-dccad55162a7", + "serverId": "80d4669b-4222-41cd-a0c0-7c95dcaf2f59", + "faction": "player" +}; + +export const npcPlayerMock: Player = { + "id": "524228a8-8610-43cf-807b-af241f18ba13", + "serverId": "80d4669b-4222-41cd-a0c0-7c95dcaf2f59", + "faction": "npc3" +}; + +export const playersMock: Player[] = [ + { + "id": "42c53448-ab04-4ae3-8ce5-dccad55162a7", + "serverId": "80d4669b-4222-41cd-a0c0-7c95dcaf2f59", + "faction": "player" + }, + { + "id": "524228a8-8610-43cf-807b-af241f18ba13", + "serverId": "80d4669b-4222-41cd-a0c0-7c95dcaf2f59", + "faction": "npc3" + }, + { + "id": "b2949612-7e4f-4695-8cb4-0f0b636a2d3e", + "serverId": "80d4669b-4222-41cd-a0c0-7c95dcaf2f59", + "faction": "npc2" + }, + { + "id": "c6be0a22-d6d2-4b79-a837-e844a4a865f3", + "serverId": "80d4669b-4222-41cd-a0c0-7c95dcaf2f59", + "faction": "npc4" + }, + { + "id": "dfacafb5-e241-4d4d-b673-ff56f6bcf03b", + "serverId": "80d4669b-4222-41cd-a0c0-7c95dcaf2f59", + "faction": "npc1" + } +] diff --git a/jest-setup.ts b/jest-setup.ts new file mode 100644 index 0000000..68f76c6 --- /dev/null +++ b/jest-setup.ts @@ -0,0 +1,5 @@ +Object.defineProperty(globalThis, 'crypto', { + value: { + randomUUID: () => 'uuid' + } +}); diff --git a/jest.config.ts b/jest.config.ts index 4557925..cbbd5ca 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -137,7 +137,7 @@ export default { // runner: "jest-runner", // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], + setupFiles: ['/jest-setup.ts'], // A list of paths to modules that run some code to configure or set up the testing framework before each test // setupFilesAfterEnv: [], @@ -191,7 +191,7 @@ export default { // unmockedModulePathPatterns: undefined, // Indicates whether each individual test should be reported during the run - verbose: true + verbose: false // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode // watchPathIgnorePatterns: [], diff --git a/src/app/(game)/error-boundary.tsx b/src/app/(game)/error-boundary.tsx index bcbfe07..c2810c7 100644 --- a/src/app/(game)/error-boundary.tsx +++ b/src/app/(game)/error-boundary.tsx @@ -3,7 +3,12 @@ import { useRouteError } from 'react-router-dom'; export const GameErrorBoundary = () => { const error = useRouteError(); + const { name, message } = error as Error; + return ( -

Error...

+ <> +

{name}

+

{message}

+ ); }; diff --git a/src/app/(game)/loader.ts b/src/app/(game)/loader.ts index 4f9d909..0fc5c3d 100644 --- a/src/app/(game)/loader.ts +++ b/src/app/(game)/loader.ts @@ -22,50 +22,79 @@ import { ResearchLevel } from 'interfaces/models/game/research-level'; import { Server } from 'interfaces/models/game/server'; import { GameEvent } from 'interfaces/models/events/game-event'; import { Report } from 'interfaces/models/game/report'; -import { CacheHydrationError, MissingServerError } from 'utils/errors'; import { LoaderFunction } from 'react-router-dom'; +import { Player } from 'interfaces/models/game/player'; +import { getPlayers, playersCacheKey } from 'hooks/game/use-players'; +import { Reputation } from 'interfaces/models/game/reputation'; +import { getReputations, reputationsCacheKey } from 'hooks/game/use-reputations'; + +class CacheHydrationError extends Error { + constructor(serverId: Server['id']) { + super(); + this.name = 'CacheHydrationError'; + this.message = `One or more database entries are missing for server with id: ${serverId}.`; + } +} + +class MissingServerError extends Error { + constructor(serverSlug: Server['slug']) { + super(); + this.name = 'MissingServerError'; + this.message = `Server with slug "${serverSlug}" does not exist. If you're accessing this server from an url, try accessing the server from the homepage, to make sure the server still exists.`; + } +} + +class MissingVillageError extends Error { + constructor(serverSlug: Server['slug'], villageSlug: Village['slug']) { + super(); + this.name = 'MissingVillageError'; + this.message = `No valid villages found for slug ${villageSlug}. Recheck if the path is correct and manually change it to /${serverSlug}/v-1 if not.`; + } +} type GameLoaderParams = Record<'serverSlug' | 'villageSlug', string>; const promiseErrorFunction = (resolvedPromise: PromiseSettledResult) => resolvedPromise.status === 'rejected' || resolvedPromise?.value === undefined; export const gameLoader: LoaderFunction = async ({ params }) => { - try { - const { serverSlug, villageSlug } = params as GameLoaderParams; - const currentServer = await getAndSetCacheData(currentServerCacheKey, () => getCurrentServer(serverSlug)); + const { serverSlug, villageSlug } = params as GameLoaderParams; + const currentServer = await getAndSetCacheData(currentServerCacheKey, () => getCurrentServer(serverSlug)); - if (!currentServer) { - throw new MissingServerError(serverSlug); - } + if (!currentServer) { + throw new MissingServerError(serverSlug); + } - const serverId = currentServer.id!; + const serverId = currentServer.id!; - // TODO: Check if village from url exists - const currentVillage = getAndSetCacheData(currentVillageCacheKey, () => getCurrentVillage(serverId, 'v-1')); + const currentVillage = await getAndSetCacheData(currentVillageCacheKey, () => getCurrentVillage(serverId, villageSlug)); - // Cache hydration - const resolvedPromises = await Promise.allSettled([ - getAndSetCacheData(achievementsCacheKey, () => getAchievements(serverId)), - getAndSetCacheData(banksCacheKey, () => getBank(serverId)), - getAndSetCacheData(effectsCacheKey, () => getEffects(serverId)), - getAndSetCacheData(eventsCacheKey, () => getEvents(serverId)), - getAndSetCacheData(heroCacheKey, () => getHero(serverId)), - getAndSetCacheData(mapCacheKey, () => getMap(serverId)), - getAndSetCacheData(questsCacheKey, () => getQuests(serverId)), - getAndSetCacheData(reportsCacheKey, () => getReports(serverId)), - getAndSetCacheData(researchLevelsCacheKey, () => getResearchLevels(serverId)), - getAndSetCacheData(villagesCacheKey, () => getVillages(serverId)) - ]); + if(!currentVillage) { + throw new MissingVillageError(serverSlug, villageSlug); + } - const hasHydrationErrorOccurred = resolvedPromises.some(promiseErrorFunction); + // Cache hydration + const resolvedPromises = await Promise.allSettled([ + getAndSetCacheData(playersCacheKey, () => getPlayers(serverId)), + getAndSetCacheData(reputationsCacheKey, () => getReputations(serverId)), + getAndSetCacheData(achievementsCacheKey, () => getAchievements(serverId)), + getAndSetCacheData(banksCacheKey, () => getBank(serverId)), + getAndSetCacheData(effectsCacheKey, () => getEffects(serverId)), + getAndSetCacheData(eventsCacheKey, () => getEvents(serverId)), + getAndSetCacheData(heroCacheKey, () => getHero(serverId)), + getAndSetCacheData(mapCacheKey, () => getMap(serverId)), + getAndSetCacheData(questsCacheKey, () => getQuests(serverId)), + getAndSetCacheData(reportsCacheKey, () => getReports(serverId)), + getAndSetCacheData(researchLevelsCacheKey, () => getResearchLevels(serverId)), + getAndSetCacheData(villagesCacheKey, () => getVillages(serverId)) + ]); - if (hasHydrationErrorOccurred) { - throw new CacheHydrationError(serverId); - } - return { - resolved: true - }; - } catch (error) { + const hasHydrationErrorOccurred = resolvedPromises.some(promiseErrorFunction); + if (hasHydrationErrorOccurred) { + throw new CacheHydrationError(serverId); } + + return { + resolved: true + }; }; diff --git a/src/app/(game)/map/components/cell.tsx b/src/app/(game)/map/components/cell.tsx index eb4fd06..296a581 100644 --- a/src/app/(game)/map/components/cell.tsx +++ b/src/app/(game)/map/components/cell.tsx @@ -10,6 +10,10 @@ import { GridChildComponentProps } from 'react-window'; import { useCurrentServer } from 'hooks/game/use-current-server'; import { OccupiableOasisIcon } from 'app/(game)/map/components/occupiable-oasis-icon'; import { useMapOptions } from 'app/(game)/map/providers/map-context'; +import clsx from 'clsx'; +import { usePlayers } from 'hooks/game/use-players'; +import { useReputations } from 'hooks/game/use-reputations'; +import { ReputationLevel } from 'interfaces/models/game/reputation'; type CellProps = GridChildComponentProps; @@ -23,7 +27,7 @@ const OasisTile: React.FC = (props) => { const { mapFilters: { shouldShowOasisIcons } } = useMapOptions(); const isOccupiable = tile.oasisBonus !== null; - const isOccupied = Object.hasOwn(tile, 'ownerVillageId'); + const isOccupied = Object.hasOwn(tile, 'villageId'); return ( = (props) => { ); }; +const reputationColorMap = new Map([ + ['ecstatic', 'border-purple-800'], + ['respected', 'border-violet-500'], + ['friendly', 'border-green-600'], + ['neutral', 'border-gray-500'], + ['unfriendly', 'border-yellow-300'], + ['hostile', 'border-red-600'], +]); + type FreeTileProps = { - tile: FreeTileType | OccupiedFreeTileType; + tile: FreeTileType; }; const FreeTile: React.FC = (props) => { @@ -53,7 +66,30 @@ const FreeTile: React.FC = (props) => { return ( + {tile.coordinates.x}, {tile.coordinates.y} {tile.resourceFieldComposition} + + ); +}; + +type OccupiedFreeTileProps = { + tile: OccupiedFreeTileType; +}; + +const OccupiedFreeTile: React.FC = (props) => { + const { tile } = props; + + const { getFactionByPlayerId } = usePlayers(); + const { getReputationByFaction } = useReputations(); + + const faction = getFactionByPlayerId(tile.ownedBy); + const { reputationLevel } = getReputationByFaction(faction); + + return ( + {tile.coordinates.x}, {tile.coordinates.y} {tile.resourceFieldComposition} @@ -77,18 +113,26 @@ export const Cell: React.FC = (props) => { const isOasis = tile.type === 'oasis-tile'; const isFreeTile = tile.type === 'free-tile'; + const isOccupiedFreeTile = isFreeTile && Object.hasOwn(tile, 'ownedBy'); return ( ); diff --git a/src/app/(game)/map/components/isometric-map.tsx b/src/app/(game)/map/components/isometric-map.tsx deleted file mode 100644 index 1f82fc7..0000000 --- a/src/app/(game)/map/components/isometric-map.tsx +++ /dev/null @@ -1,320 +0,0 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { useOutletContext } from 'react-router-dom'; -import { useContextSelector } from 'use-context-selector'; - -import { Point, Size } from 'interfaces/models/common'; -import { Tile } from 'interfaces/models/game/tile'; -import { MapGeneratorService } from 'services/map-generator-service'; -import { VillageContext } from 'providers/game/village-context'; -import { useWindowSize } from 'usehooks-ts'; - -type ContextType = { - navigationElement: React.RefObject; -}; - -const { BORDER_SIZE } = MapGeneratorService; -const BASE_TILE_SIZE: number = 100; - -const calculateTileOffset = (tile: Tile, canvasDimensions: Size, tileDimensions: Size): [number, number] => { - return [ - ((tile.coordinates.x - 1) * tileDimensions.width + tile.coordinates.y * tileDimensions.width) + canvasDimensions.width / 2, - -((tile.coordinates.y + 1) * tileDimensions.height - tile.coordinates.x * tileDimensions.height) + canvasDimensions.height / 2 - ]; -}; - -const calculateTilePoints = (offsetX: number, offsetY: number, tileDimensions: Size): Point[] => { - const point1: Point = { - x: offsetX, - y: offsetY + tileDimensions.height - }; - const point2: Point = { - x: offsetX + tileDimensions.width, - y: offsetY - }; - const point3: Point = { - x: offsetX + 2 * tileDimensions.width, - y: offsetY + tileDimensions.height - }; - const point4: Point = { - x: offsetX + tileDimensions.width, - y: offsetY + 2 * tileDimensions.height - }; - return [point1, point2, point3, point4]; -}; - -const draw = (data: Tile[], context: CanvasRenderingContext2D, canvasDimensions: Size, tileDimensions: Size): void => { - context.clearRect(0, 0, canvasDimensions.width, canvasDimensions.height); - context.strokeStyle = 'black'; - context.font = '10px'; - - for (const tile of data) { - // Offsets determine starting position for each tile - const [offsetX, offsetY] = calculateTileOffset(tile, canvasDimensions, tileDimensions); - const [point1, point2, point3, point4] = calculateTilePoints(offsetX, offsetY, tileDimensions); - - context.fillStyle = tile.backgroundColor; - - const path = new Path2D(`M${point1.x},${point1.y} L${point2.x},${point2.y} L${point3.x},${point3.y} L${point4.x},${point4.y} Z`); - context.fill(path); - - const textPosition: Point = { - x: offsetX + tileDimensions.width - 15, - y: offsetY + tileDimensions.height + 1 - }; - - context.beginPath(); - // context.strokeText(tile?.type ? `${tile.type}` : `(${tile.oasisGroup})`, textPosition.x, textPosition.y); - context.strokeText(`(${tile.coordinates.x}, ${tile.coordinates.y})`, textPosition.x, textPosition.y); - context.stroke(); - context.closePath(); - } -}; - -export const IsometricMap: React.FC = () => { - const { - width, - height - } = useWindowSize(); - const { navigationElement } = useOutletContext(); - const selectedVillage = useContextSelector(VillageContext, (v) => v.selectedVillage); - const server = useContextSelector(GameContext, (v) => v.server); - const tiles = useContextSelector(GameContext, (v) => v.tiles); - - const [tileMagnification, setTileMagnification] = useState<1 | 0.5 | 0.25>(1); - - // We use a layered canvas approach to reduce the amount of things we need to draw. - // This one is used for base map, without any effects (attacks,...) or user actions (tile outlining). - const backgroundCanvas = useRef(null); - const activeEffectsCanvas = useRef(null); - const userActionsCanvas = useRef(null); - - const isDragging = useRef(false); - const previousPosition = useRef({ - x: 0, - y: 0 - }); - - const tileSize = useMemo(() => { - return { - width: 0.5 * tileMagnification * BASE_TILE_SIZE, - height: 0.25 * tileMagnification * BASE_TILE_SIZE - }; - }, [tileMagnification]); - - const fullCanvasSize: Size = useMemo(() => { - return { - height: tileSize.height * (server!.configuration.mapSize + 2 + BORDER_SIZE), - width: tileSize.width * (server!.configuration.mapSize + 2 + BORDER_SIZE) - }; - }, [tileSize, server]); - - // Map will always be centered on currently selected village's coordinates - const currentPosition = useRef({ - x: fullCanvasSize.width / 2, - y: fullCanvasSize.height / 2 - }); - - const canvasSize = useMemo(() => { - return { - width, - height: navigationElement.current ? height - navigationElement.current.offsetHeight : height - }; - }, [width, navigationElement, height]); - - // We draw the full map in to this offscreen canvas, then we'll take subsections of it and draw them on to the screen - // TODO: Use 'OffscreenCanvas' only whenever Safari decides to implement it - const offscreenCanvasContext = useMemo(() => { - let offscreenCanvas: HTMLCanvasElement; - - // if ('OffscreenCanvas' in window) { - // offscreenCanvas = new OffscreenCanvas(fullCanvasSize.width, fullCanvasSize.height); - // } else { - // - // } - offscreenCanvas = document.createElement('canvas'); - offscreenCanvas.height = fullCanvasSize.height; - offscreenCanvas.width = fullCanvasSize.width; - const offscreenContext = offscreenCanvas.getContext('2d')!; - draw(tiles, offscreenContext, fullCanvasSize, tileSize); - return offscreenCanvas; - }, [fullCanvasSize, tileSize, tiles]); - - useEffect(() => { - const backgroundCanvasRef = backgroundCanvas.current; - const activeEffectsCanvasRef = activeEffectsCanvas.current; - const userActionsCanvasRef = userActionsCanvas.current; - - if (!(tiles && backgroundCanvasRef && activeEffectsCanvasRef && userActionsCanvasRef)) { - return; - } - - const backgroundCanvasContext = backgroundCanvasRef.getContext('2d'); - const activeEffectsCanvasContext = activeEffectsCanvasRef.getContext('2d'); - const userActionsCanvasContext = userActionsCanvasRef.getContext('2d'); - - const a = () => { - const startingPositionCoordinates: Point = { - x: currentPosition.current.x - canvasSize.width / 2, - y: currentPosition.current.y - canvasSize.height / 2 - }; - - // const startingBackgroundImage = offscreenCanvasContext.getImageData( - // startingPositionCoordinates.x, - // startingPositionCoordinates.y, - // canvasSize.width, - // canvasSize.height - // ); - // - backgroundCanvasContext!.drawImage( - offscreenCanvasContext, - startingPositionCoordinates.x, - startingPositionCoordinates.y, - canvasSize.width, - canvasSize.height, - 0, - 0, - canvasSize.width, - canvasSize.height - ); - backgroundCanvasContext!.stroke(); - }; - - a(); - - // const context: CanvasRenderingContext2D = canvasRef.getContext('2d')!; - // draw(tiles, context, canvasSize, tileSize); - - const mouseDownHandler = (event: MouseEvent) => { - isDragging.current = true; - previousPosition.current = { - x: event.clientX, - y: event.clientY - }; - event.preventDefault(); - }; - - const mouseMoveHandler = (event: MouseEvent) => { - // const columnWidth = 0.5 * tileSize; - // const rowWidth = 0.25 * tileSize; - // - // const x = Math.floor((-mapMargins.current.x + event.clientX) / columnWidth); - // const y = Math.floor((-mapMargins.current.y + event.clientY - navigationElement!.current!.offsetHeight) / rowWidth) + 100; - // const isometric = cartesianToIsometric({ - // x, y - // }); - // - // console.log(x - 100, 100 - y); - - // var dx = Math.abs(x - cellCenterX), - // dy = Math.abs(y - cellCenterY); - // - // if (dx / (cellWidth * 0.5) + dy / (cellHeight * 0.5) <= 1) - - // const mouse_grid_x = Math.floor((mouse_y / tile_height) + (mouse_x / tile_width)); - // const mouse_grid_y = Math.floor((-mouse_x / tile_width) + (mouse_y / tile_height)); - - // console.log(mouse_grid_x, mouse_grid_y) - - // const mouse_grid_x = Math.floor((0.5 * event.offsetY / (tileSize.height)) + (event.offsetX / (tileSize.width))); - // const mouse_grid_y = Math.floor((-event.offsetX / (tileSize.width)) + (event.offsetY / (tileSize.height))); - // - // console.log('x' + mouse_grid_x, 'y' + mouse_grid_y); - - if (isDragging.current) { - // const horizontalDifference: Point['x'] = event.clientX - previousPosition.current.x; - // const verticalDifference: Point['y'] = event.clientY - previousPosition.current.y; - // previousPosition.current = { - // x: event.clientX, - // y: event.clientY - // }; - // - // let xMargin = mapMargins.current.x + horizontalDifference; - // if (xMargin > canvasSize.width) { - // xMargin = canvasSize.width; - // } else if (xMargin > 0) { - // xMargin = 0; - // } else if (mapMargins.current.x - window.innerWidth < -canvasSize.width) { - // xMargin = -canvasSize.width + window.innerWidth; - // } - // - // let yMargin = mapMargins.current.y + verticalDifference; - // if (yMargin > canvasSize.height) { - // yMargin = canvasSize.height; - // } else if (yMargin > 0) { - // yMargin = 0; - // } else if (mapMargins.current.y - window.innerHeight < -canvasSize.height - tileMagnification) { - // yMargin = -canvasSize.height + window.innerHeight - tileMagnification; - // } - // - // mapMargins.current.x = xMargin; - // mapMargins.current.y = yMargin; - // mapCenter.current = { - // x: xMargin, - // y: yMargin - // }; - a(); - // canvas.current!.style.transform = `translate(${mapMargins.current.x}px, ${mapMargins.current.y}px)`; - } - event.preventDefault(); - }; - - const mouseUpHandler = () => { - isDragging.current = false; - }; - - // const wheelHandler = (event: WheelEvent) => { - // if (event.deltaY < 0) { - // if (tileMagnification === 50) { - // setTileMagnification(100); - // } else if (tileMagnification === 100) { - // setTileMagnification(150); - // } - // } else if (event.deltaY > 0) { - // if (tileMagnification === 150) { - // setTileMagnification(100); - // } else if (tileMagnification === 100) { - // setTileMagnification(50); - // } - // } - // }; - - userActionsCanvasRef.addEventListener('mousedown', mouseDownHandler, false); - userActionsCanvasRef.addEventListener('mousemove', mouseMoveHandler, false); - userActionsCanvasRef.addEventListener('mouseup', mouseUpHandler, false); - // window.addEventListener('wheel', (event) => wheelHandler(event), false); - - return () => { - userActionsCanvasRef.removeEventListener('mousedown', mouseDownHandler, false); - userActionsCanvasRef.removeEventListener('mousemove', mouseMoveHandler, false); - userActionsCanvasRef.removeEventListener('mouseup', mouseUpHandler, false); - // window.removeEventListener('wheel', (event) => wheelHandler(event), false); - }; - - // tiles.forEach((tile: Tile, index: number) => { - // ctx?.fillRect(index * size, 0, size, size); - // }); - }, [tiles, canvasSize, tileSize]); - - return ( -
- - - -
- ); -}; diff --git a/src/app/(game)/map/page.tsx b/src/app/(game)/map/page.tsx index f244194..5be0c8e 100644 --- a/src/app/(game)/map/page.tsx +++ b/src/app/(game)/map/page.tsx @@ -4,6 +4,8 @@ import { useCurrentServer } from 'hooks/game/use-current-server'; import { FixedSizeGrid } from 'react-window'; import { useWindowSize } from 'usehooks-ts'; import { Cell } from 'app/(game)/map/components/cell'; +import { useCurrentVillage } from 'hooks/game/use-current-village'; +import { usePlayers } from 'hooks/game/use-players'; const TILE_BASE_SIZE = 30; @@ -23,11 +25,14 @@ export const MapPage: React.FC = () => { width } = useWindowSize(); - // const { currentVillage } = useCurrentVillage(); + const { currentVillage } = useCurrentVillage(); + const { players } = usePlayers(); const mapSize = server?.configuration?.mapSize; const isLoading = isLoadingMap || isLoadingServer; + console.log(players); + const [magnification, setMagnification] = useState(2); const tileSize = TILE_BASE_SIZE * magnification; const gridSize = mapSize! + 1; @@ -41,6 +46,9 @@ export const MapPage: React.FC = () => { )} {!isLoading && ( ; type CreateServerModalView = 'configuration' | 'loader'; @@ -21,7 +34,7 @@ type CreateServerConfigurationViewProps = { }; const generateSeed = (length: number = 10): string => { - return uuidv4().replaceAll('-', '').substring(0, length); + return crypto.randomUUID().replaceAll('-', '').substring(0, length); }; const CreateServerConfigurationView: React.FC = (props) => { @@ -34,7 +47,7 @@ const CreateServerConfigurationView: React.FC({ initialValues: { seed: generateSeed(), - name: `s-${generateSeed(4)}`, + name: `server-${generateSeed(4)}`, configuration: { mapSize: 100, speed: 1 @@ -185,7 +198,7 @@ export const CreateServerModalContent: React.FC = () => { setProgressPercentage((prevState) => prevState + percentage); }; - const executeStep = async (currentMessage: string, historyMessage: string, promise: () => Promise): Promise => { + const executeStep = async (currentMessage: string, historyMessage: string, promise: () => Promise): Promise => { setProgressMessage(currentMessage); updatePercentage(); const result = await promise(); @@ -198,75 +211,102 @@ export const CreateServerModalContent: React.FC = () => { await executeStep('Creating server data...', 'Created server data.', async () => { await createServer(server); }); + + // Players/factions + const players = await executeStep('Creating factions...', 'Created factions.', async () => { + const { players: generatedPlayers } = await workerFactory( + CreatePlayersWorker, + { server }, + '' + ); + await database.players.bulkAdd(generatedPlayers); + return generatedPlayers; + }); + + const userPlayer = players.find(({ faction }) => faction === 'player')!; + + // Reputations + await executeStep('Creating reputations...', 'Created reputations.', async () => { + const { reputations } = await workerFactory( + CreateReputationsWorker, + { server, players }, + '' + ); + await database.reputations.bulkAdd(reputations); + }); + // Map data const tiles = await executeStep('Creating map data...', 'Created map data.', async () => { - const mapData = await new Promise((resolve, reject) => { - const createMapWorker = new Worker(new URL('../workers/generate-world-map-worker', import.meta.url), { - type: 'module' - }); - createMapWorker.postMessage({ server }); - createMapWorker.addEventListener('message', async (event: MessageEvent<{ tiles: Tile[] }>) => { - const { tiles } = event.data; - resolve(tiles); - }); - createMapWorker.addEventListener('error', () => { - reject(new Error('Error occurred when creating world data')); - }); - }); - - await database.maps.bulkAdd(mapData); - return mapData; + const { tiles: generatedTiles } = await workerFactory( + CreateMapWorker, + { server, players }, + '' + ); + await database.maps.bulkAdd(generatedTiles); + return generatedTiles; }); + // Villages - await executeStep('Creating village data...', 'Created village data.', async () => { - const villages = await new Promise((resolve, reject) => { - const createVillagesWorker = new Worker(new URL('../workers/generate-villages-worker', import.meta.url), { - type: 'module' - }); - createVillagesWorker.postMessage({ - server, - tiles - }); - createVillagesWorker.addEventListener('message', async (event: MessageEvent<{ villages: Village[] }>) => { - const { villages } = event.data; - resolve(villages); - }); - createVillagesWorker.addEventListener('error', () => { - reject(new Error('Error occurred when creating villages')); - }); - }); - await database.villages.bulkAdd(villages); + const villages = await executeStep('Creating village data...', 'Created village data.', async () => { + const { villages: generatedVillages } = await workerFactory( + CreateVillagesWorker, + { server, tiles, players }, + '' + ); + await database.villages.bulkAdd(generatedVillages); + return generatedVillages; }); + + const playerStartingVillage = villages.find(({ playerId }) => playerId === userPlayer.id)!; + // Research levels await executeStep('Creating research levels data...', 'Created research levels data.', async () => { const researchLevels = researchLevelsFactory({ server }); await database.researchLevels.bulkAdd(researchLevels); }); + // Bank data await executeStep('Creating bank data...', 'Created bank data.', async () => { const bank = bankFactory({ server }); await database.banks.add(bank); }); + // Hero data await executeStep('Creating hero data...', 'Created hero data.', async () => { const hero = heroFactory({ server }); await database.heroes.add(hero); }); + // Quests await executeStep('Creating quests...', 'Created quests.', async () => { - // const quests = CreateServerService.createQuests(); - await database.quests.bulkAdd([]); + const { quests } = await workerFactory( + CreateQuestsWorker, + { server, village: playerStartingVillage }, + '' + ); + await database.quests.bulkAdd(quests); }); + // Achievements await executeStep('Creating achievements...', 'Created achievements.', async () => { - // const achievements = CreateServerService.createAchievements(); - await database.achievements.bulkAdd([]); + const { achievements } = await workerFactory( + CreateAchievementsWorker, + { server }, + '' + ); + await database.achievements.bulkAdd(achievements); }); + // Effects await executeStep('Creating effects...', 'Created effects.', async () => { - // const effects = CreateServerService.createAccountEffects(); - await database.effects.bulkAdd([]); + const { effects } = await workerFactory( + CreateEffectsWorker, + { server, village: playerStartingVillage }, + '' + ); + await database.effects.bulkAdd(effects); }); + setHasCreatedServer(true); } catch (err) { setError(err as string); diff --git a/src/app/(public)/home/components/server-card.tsx b/src/app/(public)/home/components/server-card.tsx index e10c373..c49ecc9 100644 --- a/src/app/(public)/home/components/server-card.tsx +++ b/src/app/(public)/home/components/server-card.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Server } from 'interfaces/models/game/server'; -import { Link, useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { Button } from 'components/buttons/button'; import { useAvailableServers } from 'hooks/use-available-servers'; import { setToCache } from 'database/cache'; @@ -19,7 +19,7 @@ export const ServerCard: React.FC = (props) => { const hydrateCurrentServerCache = (server: Server) => { setToCache('current-server', server); - navigate(`/game/${server.name}/v-1/map`); + navigate(`/game/${server.slug}/v-1/map`); }; return ( diff --git a/src/app/(public)/workers/generate-achievements-worker.ts b/src/app/(public)/workers/generate-achievements-worker.ts new file mode 100644 index 0000000..d220dd6 --- /dev/null +++ b/src/app/(public)/workers/generate-achievements-worker.ts @@ -0,0 +1,18 @@ +import { Server } from 'interfaces/models/game/server'; +import { Achievement } from 'interfaces/models/game/achievement'; + +export type GenerateAchievementsWorkerPayload = { + server: Server; +}; + +export type GenerateAchievementsWorkerReturn = { + achievements: Achievement[]; +} + +const self = globalThis as unknown as DedicatedWorkerGlobalScope; + +self.addEventListener('message', (event: MessageEvent) => { + const { server } = event.data; + const achievements: Achievement[] = []; + self.postMessage({ achievements }); +}); diff --git a/src/app/(public)/workers/generate-effects-worker.ts b/src/app/(public)/workers/generate-effects-worker.ts new file mode 100644 index 0000000..39f0243 --- /dev/null +++ b/src/app/(public)/workers/generate-effects-worker.ts @@ -0,0 +1,23 @@ +import { Server } from 'interfaces/models/game/server'; +import { globalEffectsFactory, newVillageEffectsFactory } from 'factories/effect-factory'; +import { Village } from 'interfaces/models/game/village'; +import { Effect } from 'interfaces/models/game/effect'; + +export type GenerateEffectsWorkerPayload = { + server: Server; + village: Village; +}; + +export type GenerateEffectsWorkerReturn = { + effects: Effect[]; +} + +const self = globalThis as unknown as DedicatedWorkerGlobalScope; + +self.addEventListener('message', (event: MessageEvent) => { + const { server, village } = event.data; + const villageEffects = newVillageEffectsFactory({ server, village }); + const globalEffects = globalEffectsFactory({ server }); + const effects = [...villageEffects, globalEffects]; + self.postMessage({ effects }); +}); diff --git a/src/app/(public)/workers/generate-players-worker.ts b/src/app/(public)/workers/generate-players-worker.ts new file mode 100644 index 0000000..ac28ea2 --- /dev/null +++ b/src/app/(public)/workers/generate-players-worker.ts @@ -0,0 +1,23 @@ +import { Server } from 'interfaces/models/game/server'; +import { playerFactory } from 'factories/player-factory'; +import { Player, PlayerFaction } from 'interfaces/models/game/player'; + +export type GeneratePlayersWorkerPayload = { + server: Server; +}; + +export type GeneratePlayersWorkerReturn = { + players: Player[]; +} + +const self = globalThis as unknown as DedicatedWorkerGlobalScope; + +const factions: PlayerFaction[] = [ + 'player', 'npc1', 'npc2', 'npc3', 'npc4' +]; + +self.addEventListener('message', (event: MessageEvent) => { + const { server } = event.data; + const players = factions.map((faction: PlayerFaction) => playerFactory({ server, faction })) + self.postMessage({ players }); +}); diff --git a/src/app/(public)/workers/generate-quests-worker.ts b/src/app/(public)/workers/generate-quests-worker.ts new file mode 100644 index 0000000..e8e4678 --- /dev/null +++ b/src/app/(public)/workers/generate-quests-worker.ts @@ -0,0 +1,23 @@ +import { Server } from 'interfaces/models/game/server'; +import { Quest } from 'interfaces/models/game/quest'; +import { globalQuestsFactory, newVillageQuestsFactory } from 'factories/quest-factory'; +import { Village } from 'interfaces/models/game/village'; + +export type GenerateQuestsWorkerPayload = { + server: Server; + village: Village; +}; + +export type GenerateQuestsWorkerReturn = { + quests: Quest[]; +} + +const self = globalThis as unknown as DedicatedWorkerGlobalScope; + +self.addEventListener('message', (event: MessageEvent) => { + const { server, village } = event.data; + const villageQuests = newVillageQuestsFactory({ server, village }); + const globalQuests = globalQuestsFactory({ server }); + const quests = [...villageQuests, globalQuests]; + self.postMessage({ quests }); +}); diff --git a/src/app/(public)/workers/generate-reputations-worker.ts b/src/app/(public)/workers/generate-reputations-worker.ts new file mode 100644 index 0000000..eac6e3c --- /dev/null +++ b/src/app/(public)/workers/generate-reputations-worker.ts @@ -0,0 +1,22 @@ +import { Server } from 'interfaces/models/game/server'; +import { Player } from 'interfaces/models/game/player'; +import { Reputation } from 'interfaces/models/game/reputation'; +import { reputationFactory } from 'factories/reputation-factory'; + +export type GenerateReputationsWorkerPayload = { + server: Server; + players: Player[]; +}; + +export type GenerateReputationsWorkerReturn = { + reputations: Reputation[]; +} + +const self = globalThis as unknown as DedicatedWorkerGlobalScope; + +self.addEventListener('message', (event: MessageEvent) => { + const { server, players } = event.data; + const npcPlayers = players.filter(({ faction }) => faction !== 'player'); + const reputations = npcPlayers.map(({ faction }) => reputationFactory({ server, faction })) + self.postMessage({ reputations }); +}); diff --git a/src/app/(public)/workers/generate-villages-worker.ts b/src/app/(public)/workers/generate-villages-worker.ts index 0a681c7..10e6c7d 100644 --- a/src/app/(public)/workers/generate-villages-worker.ts +++ b/src/app/(public)/workers/generate-villages-worker.ts @@ -1,17 +1,32 @@ -import { Tile } from 'interfaces/models/game/tile'; +import { OccupiedFreeTile, Tile } from 'interfaces/models/game/tile'; import { Village } from 'interfaces/models/game/village'; import { Server } from 'interfaces/models/game/server'; import { villageFactory } from 'factories/village-factory'; +import { Player } from 'interfaces/models/game/player'; -type GenerateWorldMapWorkerPayload = { +export type GenerateVillageWorkerPayload = { server: Server; tiles: Tile[]; + players: Player[]; +}; + +export type GenerateVillageWorkerReturn = { + villages: Village[]; }; const self = globalThis as unknown as DedicatedWorkerGlobalScope; -self.addEventListener('message', (event: MessageEvent) => { - const { server, tiles } = event.data; - // const villages: Village[] = villageFactory(); - self.postMessage({ villages: [] }); +self.addEventListener('message', (event: MessageEvent) => { + const { server, tiles, players } = event.data; + + const freeTiles: OccupiedFreeTile[] = tiles.filter((tile) => tile.type === 'free-tile' && Object.hasOwn(tile, 'ownedBy')) as OccupiedFreeTile[]; + + const villages: Village[] = freeTiles.map((tile) => { + const { faction } = players.find(({ id }) => tile.ownedBy === id)!; + + const slug = faction === 'player' ? 'v-1' : tile.tileId; + return villageFactory({ server, players, tile, slug }); + }); + + self.postMessage({ villages }); }); diff --git a/src/app/(public)/workers/generate-world-map-worker.ts b/src/app/(public)/workers/generate-world-map-worker.ts index ec31222..eb2014b 100644 --- a/src/app/(public)/workers/generate-world-map-worker.ts +++ b/src/app/(public)/workers/generate-world-map-worker.ts @@ -1,14 +1,21 @@ import { Server } from 'interfaces/models/game/server'; import { mapFactory } from 'factories/map-factory'; +import { Tile } from 'interfaces/models/game/tile'; +import { Player } from 'interfaces/models/game/player'; -type GenerateWorldMapWorkerPayload = { +export type GenerateWorldMapWorkerPayload = { server: Server; + players: Player[]; +}; + +export type GenerateWorldMapWorkerReturn = { + tiles: Tile[]; }; const self = globalThis as unknown as DedicatedWorkerGlobalScope; self.addEventListener('message', (event: MessageEvent) => { - const { server } = event.data; - const tiles = mapFactory({ server }); + const { server, players } = event.data; + const tiles = mapFactory({ server, players }); self.postMessage({ tiles }); }); diff --git a/src/app/(test)/test/page.tsx b/src/app/(test)/test/page.tsx index 5456187..c0819fd 100644 --- a/src/app/(test)/test/page.tsx +++ b/src/app/(test)/test/page.tsx @@ -3,7 +3,6 @@ import { useLocation } from 'react-router-dom'; export const TestPage: React.FC = () => { const location = useLocation(); - console.log('burek'); return ( <> Test page {location.pathname} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 6015ff0..db59b27 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,11 +1,14 @@ import React from 'react'; import { Outlet } from 'react-router-dom'; -import { PreferencesProvider } from 'providers/global/preferences-context'; +import { PreferencesProvider } from './providers/preferences-context'; +import { ViewportProvider } from 'app/providers/viewport-context'; export const AppLayout: React.FC = () => { return ( - - - + + + + + ); }; diff --git a/src/providers/preferences-context.tsx b/src/app/providers/preferences-context.tsx similarity index 100% rename from src/providers/preferences-context.tsx rename to src/app/providers/preferences-context.tsx diff --git a/src/app/providers/viewport-context.tsx b/src/app/providers/viewport-context.tsx new file mode 100644 index 0000000..b043f78 --- /dev/null +++ b/src/app/providers/viewport-context.tsx @@ -0,0 +1,75 @@ +import React, { useState, createContext, useContext, useEffect, useMemo, FunctionComponentWithChildren } from 'react'; + +type WindowSize = { + height: number; + width: number; +}; + +type ViewportContextValues = { + isWiderThanXs: boolean; + isWiderThanSm: boolean; + isWiderThanMd: boolean; + isWiderThanLg: boolean; + isWiderThanXl: boolean; + isWiderThan2Xl: boolean; +} & WindowSize; + +export const breakpoints = { + xs: 425, + sm: 640, + md: 768, + lg: 1024, + xl: 1280, + '2xl': 1536 +}; + +const ViewportContext = createContext({} as never); + +const ViewportProvider: FunctionComponentWithChildren = ({ children }) => { + const [windowSize, setWindowSize] = useState({ + height: 0, + width: 0 + }); + + const { width, height } = windowSize; + + const isWiderThanXs: boolean = width >= breakpoints.xs; + const isWiderThanSm: boolean = width >= breakpoints.sm; + const isWiderThanMd: boolean = width >= breakpoints.md; + const isWiderThanLg: boolean = width >= breakpoints.lg; + const isWiderThanXl: boolean = width >= breakpoints.xl; + const isWiderThan2Xl: boolean = width >= breakpoints['2xl']; + + // TODO: Add debounce, maybe through lodash, don't wanna maintain own version + useEffect(() => { + const debouncedHandleResize = () => { + setWindowSize({ + width: window.innerWidth, + height: window.innerHeight, + }); + }; + + debouncedHandleResize(); + window.addEventListener('resize', debouncedHandleResize); + return () => window.removeEventListener('resize', debouncedHandleResize); + }, []); + + const value = useMemo(() => { + return { + width, + height, + isWiderThanXs, + isWiderThanSm, + isWiderThanMd, + isWiderThanLg, + isWiderThanXl, + isWiderThan2Xl, + }; + }, [windowSize]); + + return {children}; +}; + +const useViewport = () => useContext(ViewportContext); + +export { ViewportProvider, useViewport }; diff --git a/src/assets/_buildings.json b/src/assets/_buildings.json deleted file mode 100644 index 06ee194..0000000 --- a/src/assets/_buildings.json +++ /dev/null @@ -1,9694 +0,0 @@ -[ - { - "id": "BAKERY", - "maxLevel": 5, - "baseBuildTimePerLevel": { - "1": 3680, - "2": 6720, - "3": 11280, - "4": 18120, - "5": 28380 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2 - }, - "effects": [ - { - "effectId": "wheatProductionBonus", - "valuesPerLevel": { - "1": 5, - "2": 10, - "3": 15, - "4": 20, - "5": 25 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "CROPLAND", - "level": 10 - }, - "2": { - "requirementType": "building", - "buildingId": "GRAIN_MILL", - "level": 5 - }, - "3": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - } - }, - "wood": { - "1": 1200, - "2": 2160, - "3": 3890, - "4": 7000, - "5": 12595 - }, - "clay": { - "1": 1480, - "2": 2665, - "3": 4795, - "4": 8630, - "5": 15535 - }, - "iron": { - "1": 870, - "2": 1565, - "3": 2820, - "4": 5075, - "5": 9135 - }, - "wheat": { - "1": 1600, - "2": 2880, - "3": 5185, - "4": 9330, - "5": 16795 - }, - "totalResourceCostPerLevel": { - "1": 5150, - "2": 9270, - "3": 16690, - "4": 30035, - "5": 54060 - } - }, - { - "id": "BRICKYARD", - "maxLevel": 5, - "baseBuildTimePerLevel": { - "1": 2840, - "2": 5460, - "3": 9390, - "4": 15290, - "5": 24130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2 - }, - "cropConsumptionPerLevel": { - "1": 3, - "2": 2, - "3": 2, - "4": 2, - "5": 2 - }, - "effects": [ - { - "effectId": "clayProductionBonus", - "valuesPerLevel": { - "1": 5, - "2": 10, - "3": 15, - "4": 20, - "5": 25 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "CLAY_PIT", - "level": 10 - }, - "2": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - } - }, - "wood": { - "1": 440, - "2": 790, - "3": 1425, - "4": 2565, - "5": 4620 - }, - "clay": { - "1": 480, - "2": 865, - "3": 1555, - "4": 2800, - "5": 5040 - }, - "iron": { - "1": 320, - "2": 575, - "3": 1035, - "4": 1865, - "5": 3360 - }, - "wheat": { - "1": 50, - "2": 90, - "3": 160, - "4": 290, - "5": 525 - }, - "totalResourceCostPerLevel": { - "1": 1290, - "2": 2320, - "3": 4175, - "4": 7520, - "5": 13545 - } - }, - { - "id": "CLAY_PIT", - "maxLevel": 22, - "baseBuildTimePerLevel": { - "1": 220, - "2": 550, - "3": 1080, - "4": 1930, - "5": 3290, - "6": 5470, - "7": 8950, - "8": 14520, - "9": 23430, - "10": 37690, - "11": 60510, - "12": 97010, - "13": 155420, - "14": 248870, - "15": 398390, - "16": 637620, - "17": 1020390, - "18": 1632820, - "19": 2612710, - "20": 4180540, - "21": 6689060, - "22": 10702690 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38, - "21": 46, - "22": 55 - }, - "cropConsumptionPerLevel": { - "1": 2, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3, - "21": 3, - "22": 3 - }, - "buildingRequirements": {}, - "effects": [ - { - "effectId": "clayProduction", - "valuesPerLevel": { - "1": 4, - "2": 7, - "3": 13, - "4": 21, - "5": 31, - "6": 46, - "7": 70, - "8": 98, - "9": 140, - "10": 203, - "11": 280, - "12": 392, - "13": 525, - "14": 693, - "15": 889, - "16": 1120, - "17": 1400, - "18": 1820, - "19": 2240, - "20": 2800, - "21": 3430 - } - } - ], - "wood": { - "1": 80, - "2": 135, - "3": 225, - "4": 375, - "5": 620, - "6": 1040, - "7": 1735, - "8": 2900, - "9": 4840, - "10": 8080, - "11": 13500, - "12": 22540, - "13": 37645, - "14": 62865, - "15": 104985, - "16": 175320, - "17": 292790, - "18": 488955, - "19": 816555, - "20": 1363650, - "21": 2277295, - "22": 3803085 - }, - "clay": { - "1": 40, - "2": 65, - "3": 110, - "4": 185, - "5": 310, - "6": 520, - "7": 870, - "8": 1450, - "9": 2420, - "10": 4040, - "11": 6750, - "12": 11270, - "13": 18820, - "14": 31430, - "15": 52490, - "16": 87660, - "17": 146395, - "18": 244480, - "19": 408280, - "20": 681825, - "21": 1138650, - "22": 1901540 - }, - "iron": { - "1": 80, - "2": 135, - "3": 225, - "4": 375, - "5": 620, - "6": 1040, - "7": 1735, - "8": 2900, - "9": 4840, - "10": 8080, - "11": 13500, - "12": 22540, - "13": 37645, - "14": 62865, - "15": 104985, - "16": 175320, - "17": 292790, - "18": 488955, - "19": 816555, - "20": 1363650, - "21": 2277295, - "22": 3803085 - }, - "wheat": { - "1": 50, - "2": 85, - "3": 140, - "4": 235, - "5": 390, - "6": 650, - "7": 1085, - "8": 1810, - "9": 3025, - "10": 5050, - "11": 8435, - "12": 14090, - "13": 23525, - "14": 39290, - "15": 65615, - "16": 109575, - "17": 182995, - "18": 305600, - "19": 510350, - "20": 852280, - "21": 1423310, - "22": 2376925 - }, - "totalResourceCostPerLevel": { - "1": 250, - "2": 420, - "3": 700, - "4": 1170, - "5": 1940, - "6": 3250, - "7": 5425, - "8": 9060, - "9": 15125, - "10": 25250, - "11": 42185, - "12": 70440, - "13": 117635, - "14": 196450, - "15": 328075, - "16": 547875, - "17": 914970, - "18": 1527990, - "19": 2551740, - "20": 4261405, - "21": 7116550, - "22": 11884635 - } - }, - { - "id": "CROPLAND", - "maxLevel": 22, - "baseBuildTimePerLevel": { - "1": 150, - "2": 440, - "3": 900, - "4": 1650, - "5": 2830, - "6": 4730, - "7": 7780, - "8": 12640, - "9": 20430, - "10": 32880, - "11": 52810, - "12": 84700, - "13": 135710, - "14": 217340, - "15": 347950, - "16": 556910, - "17": 891260, - "18": 1426210, - "19": 2282140, - "20": 3651630, - "21": 5842810, - "22": 9348690 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38, - "21": 46, - "22": 55 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2 - }, - "buildingRequirements": {}, - "effects": [ - { - "effectId": "wheatProduction", - "valuesPerLevel": { - "1": 4, - "2": 7, - "3": 13, - "4": 21, - "5": 31, - "6": 46, - "7": 70, - "8": 98, - "9": 140, - "10": 203, - "11": 280, - "12": 392, - "13": 525, - "14": 693, - "15": 889, - "16": 1120, - "17": 1400, - "18": 1820, - "19": 2240, - "20": 2800, - "21": 3430 - } - } - ], - "wood": { - "1": 70, - "2": 115, - "3": 195, - "4": 325, - "5": 545, - "6": 910, - "7": 1520, - "8": 2535, - "9": 4235, - "10": 7070, - "11": 11810, - "12": 19725, - "13": 32940, - "14": 55005, - "15": 91860, - "16": 153405, - "17": 256190, - "18": 427835, - "19": 714485, - "20": 1193195, - "21": 1992635, - "22": 3327700 - }, - "clay": { - "1": 90, - "2": 150, - "3": 250, - "4": 420, - "5": 700, - "6": 1170, - "7": 1950, - "8": 3260, - "9": 5445, - "10": 9095, - "11": 15185, - "12": 25360, - "13": 42350, - "14": 70720, - "15": 118105, - "16": 197240, - "17": 329385, - "18": 550075, - "19": 918625, - "20": 1534105, - "21": 2561960, - "22": 4278470 - }, - "iron": { - "1": 70, - "2": 115, - "3": 195, - "4": 325, - "5": 545, - "6": 910, - "7": 1520, - "8": 2535, - "9": 4235, - "10": 7070, - "11": 11810, - "12": 19725, - "13": 32940, - "14": 55005, - "15": 91860, - "16": 153405, - "17": 256190, - "18": 427835, - "19": 714485, - "20": 1193195, - "21": 1992635, - "22": 3327700 - }, - "wheat": { - "1": 20, - "2": 35, - "3": 55, - "4": 95, - "5": 155, - "6": 260, - "7": 435, - "8": 725, - "9": 1210, - "10": 2020, - "11": 3375, - "12": 5635, - "13": 9410, - "14": 15715, - "15": 26245, - "16": 43830, - "17": 73195, - "18": 122240, - "19": 204140, - "20": 340915, - "21": 569325, - "22": 950770 - }, - "totalResourceCostPerLevel": { - "1": 250, - "2": 415, - "3": 695, - "4": 1165, - "5": 1945, - "6": 3250, - "7": 5425, - "8": 9055, - "9": 15125, - "10": 25255, - "11": 42180, - "12": 70445, - "13": 117640, - "14": 196445, - "15": 328070, - "16": 547880, - "17": 914960, - "18": 1527985, - "19": 2551735, - "20": 4261410, - "21": 7116555, - "22": 11884640 - } - }, - { - "id": "GRAIN_MILL", - "maxLevel": 5, - "baseBuildTimePerLevel": { - "1": 1840, - "2": 3960, - "3": 7140, - "4": 11910, - "5": 19070 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2 - }, - "cropConsumptionPerLevel": { - "1": 3, - "2": 2, - "3": 2, - "4": 2, - "5": 2 - }, - "effects": [ - { - "effectId": "wheatProductionBonus", - "valuesPerLevel": { - "1": 5, - "2": 10, - "3": 15, - "4": 20, - "5": 25 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "CROPLAND", - "level": 5 - } - }, - "wood": { - "1": 500, - "2": 900, - "3": 1620, - "4": 2915, - "5": 5250 - }, - "clay": { - "1": 440, - "2": 790, - "3": 1425, - "4": 2565, - "5": 4620 - }, - "iron": { - "1": 380, - "2": 685, - "3": 1230, - "4": 2215, - "5": 3990 - }, - "wheat": { - "1": 1240, - "2": 2230, - "3": 4020, - "4": 7230, - "5": 13015 - }, - "totalResourceCostPerLevel": { - "1": 2560, - "2": 4605, - "3": 8295, - "4": 14925, - "5": 26875 - } - }, - { - "id": "GRANARY", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 1600, - "2": 2160, - "3": 2800, - "4": 3550, - "5": 4420, - "6": 5420, - "7": 6590, - "8": 7950, - "9": 9520, - "10": 11340, - "11": 13450, - "12": 15910, - "13": 18750, - "14": 22050, - "15": 25880, - "16": 30320, - "17": 35470, - "18": 41450, - "19": 48380, - "20": 56420 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "granaryCapacity", - "valuesPerLevel": { - "1": 1200, - "2": 1700, - "3": 2300, - "4": 3100, - "5": 4000, - "6": 5000, - "7": 6300, - "8": 7800, - "9": 9600, - "10": 11800, - "11": 14400, - "12": 17600, - "13": 21400, - "14": 25900, - "15": 31300, - "16": 37900, - "17": 45700, - "18": 55100, - "19": 66400, - "20": 80000 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 1 - } - }, - "wood": { - "1": 80, - "2": 100, - "3": 130, - "4": 170, - "5": 215, - "6": 275, - "7": 350, - "8": 450, - "9": 575, - "10": 740, - "11": 945, - "12": 1210, - "13": 1545, - "14": 1980, - "15": 2535, - "16": 3245, - "17": 4155, - "18": 5315, - "19": 6805, - "20": 8710 - }, - "clay": { - "1": 100, - "2": 130, - "3": 165, - "4": 210, - "5": 270, - "6": 345, - "7": 440, - "8": 565, - "9": 720, - "10": 920, - "11": 1180, - "12": 1510, - "13": 1935, - "14": 2475, - "15": 3170, - "16": 4055, - "17": 5190, - "18": 6645, - "19": 8505, - "20": 10890 - }, - "iron": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "wheat": { - "1": 20, - "2": 25, - "3": 35, - "4": 40, - "5": 55, - "6": 70, - "7": 90, - "8": 115, - "9": 145, - "10": 185, - "11": 235, - "12": 300, - "13": 385, - "14": 495, - "15": 635, - "16": 810, - "17": 1040, - "18": 1330, - "19": 1700, - "20": 2180 - }, - "totalResourceCostPerLevel": { - "1": 270, - "2": 345, - "3": 445, - "4": 565, - "5": 730, - "6": 930, - "7": 1190, - "8": 1525, - "9": 1945, - "10": 2490, - "11": 3185, - "12": 4080, - "13": 5220, - "14": 6685, - "15": 8560, - "16": 10950, - "17": 14020, - "18": 17940, - "19": 22965, - "20": 29400 - } - }, - { - "id": "GREAT_GRANARY", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 7000, - "2": 8420, - "3": 10070, - "4": 11980, - "5": 14190, - "6": 16770, - "7": 19750, - "8": 23210, - "9": 27220, - "10": 31880, - "11": 37280, - "12": 43540, - "13": 50810, - "14": 59240, - "15": 69010, - "16": 80360, - "17": 93510, - "18": 108780, - "19": 126480, - "20": 147020 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "granaryCapacity", - "valuesPerLevel": { - "1": 3600, - "2": 5100, - "3": 6900, - "4": 9300, - "5": 12000, - "6": 15000, - "7": 18900, - "8": 23400, - "9": 28800, - "10": 35400, - "11": 43200, - "12": 52800, - "13": 64200, - "14": 77700, - "15": 93900, - "16": 113700, - "17": 137100, - "18": 165300, - "19": 199200, - "20": 240000 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 10 - } - }, - "wood": { - "1": 400, - "2": 510, - "3": 655, - "4": 840, - "5": 1075, - "6": 1375, - "7": 1760, - "8": 2250, - "9": 2880, - "10": 3690, - "11": 4720, - "12": 6045, - "13": 7735, - "14": 9905, - "15": 12675, - "16": 16225, - "17": 20770, - "18": 26585, - "19": 34030, - "20": 43555 - }, - "clay": { - "1": 500, - "2": 640, - "3": 820, - "4": 1050, - "5": 1340, - "6": 1720, - "7": 2200, - "8": 2815, - "9": 3605, - "10": 4610, - "11": 5905, - "12": 7555, - "13": 9670, - "14": 12380, - "15": 15845, - "16": 20280, - "17": 25960, - "18": 33230, - "19": 42535, - "20": 54445 - }, - "iron": { - "1": 350, - "2": 450, - "3": 575, - "4": 735, - "5": 940, - "6": 1205, - "7": 1540, - "8": 1970, - "9": 2520, - "10": 3230, - "11": 4130, - "12": 5290, - "13": 6770, - "14": 8665, - "15": 11090, - "16": 14200, - "17": 18175, - "18": 23260, - "19": 29775, - "20": 38110 - }, - "wheat": { - "1": 100, - "2": 130, - "3": 165, - "4": 210, - "5": 270, - "6": 345, - "7": 440, - "8": 565, - "9": 720, - "10": 920, - "11": 1180, - "12": 1510, - "13": 1935, - "14": 2475, - "15": 3170, - "16": 4055, - "17": 5190, - "18": 6645, - "19": 8505, - "20": 10890 - }, - "totalResourceCostPerLevel": { - "1": 1350, - "2": 1730, - "3": 2215, - "4": 2835, - "5": 3625, - "6": 4645, - "7": 5940, - "8": 7600, - "9": 9725, - "10": 12450, - "11": 15935, - "12": 20400, - "13": 26110, - "14": 33425, - "15": 42780, - "16": 54760, - "17": 70095, - "18": 89720, - "19": 114845, - "20": 147000 - } - }, - { - "id": "GREAT_WAREHOUSE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 9000, - "2": 10740, - "3": 12760, - "4": 15100, - "5": 17820, - "6": 20970, - "7": 24620, - "8": 28860, - "9": 33780, - "10": 39480, - "11": 46100, - "12": 53780, - "13": 62680, - "14": 73010, - "15": 84990, - "16": 98890, - "17": 115010, - "18": 133710, - "19": 155400, - "20": 180570 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "warehouseCapacity", - "valuesPerLevel": { - "1": 3600, - "2": 5100, - "3": 6900, - "4": 9300, - "5": 12000, - "6": 15000, - "7": 18900, - "8": 23400, - "9": 28800, - "10": 35400, - "11": 43200, - "12": 52800, - "13": 64200, - "14": 77700, - "15": 93900, - "16": 113700, - "17": 137100, - "18": 165300, - "19": 199200, - "20": 240000 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 10 - } - }, - "wood": { - "1": 650, - "2": 830, - "3": 1065, - "4": 1365, - "5": 1745, - "6": 2235, - "7": 2860, - "8": 3660, - "9": 4685, - "10": 5995, - "11": 7675, - "12": 9825, - "13": 12575, - "14": 16095, - "15": 20600, - "16": 26365, - "17": 33750, - "18": 43200, - "19": 55295, - "20": 70780 - }, - "clay": { - "1": 800, - "2": 1025, - "3": 1310, - "4": 1680, - "5": 2145, - "6": 2750, - "7": 3520, - "8": 4505, - "9": 5765, - "10": 7380, - "11": 9445, - "12": 12090, - "13": 15475, - "14": 19805, - "15": 25355, - "16": 32450, - "17": 41540, - "18": 53170, - "19": 68055, - "20": 87110 - }, - "iron": { - "1": 450, - "2": 575, - "3": 735, - "4": 945, - "5": 1210, - "6": 1545, - "7": 1980, - "8": 2535, - "9": 3245, - "10": 4150, - "11": 5315, - "12": 6800, - "13": 8705, - "14": 11140, - "15": 14260, - "16": 18255, - "17": 23365, - "18": 29910, - "19": 38280, - "20": 49000 - }, - "wheat": { - "1": 200, - "2": 255, - "3": 330, - "4": 420, - "5": 535, - "6": 685, - "7": 880, - "8": 1125, - "9": 1440, - "10": 1845, - "11": 2360, - "12": 3020, - "13": 3870, - "14": 4950, - "15": 6340, - "16": 8115, - "17": 10385, - "18": 13290, - "19": 17015, - "20": 21780 - }, - "totalResourceCostPerLevel": { - "1": 2100, - "2": 2685, - "3": 3440, - "4": 4410, - "5": 5635, - "6": 7215, - "7": 9240, - "8": 11825, - "9": 15135, - "10": 19370, - "11": 24795, - "12": 31735, - "13": 40625, - "14": 51990, - "15": 66555, - "16": 85185, - "17": 109040, - "18": 139570, - "19": 178645, - "20": 228670 - } - }, - { - "id": "IRON_FOUNDRY", - "maxLevel": 5, - "baseBuildTimePerLevel": { - "1": 4080, - "2": 7320, - "3": 12180, - "4": 19470, - "5": 30410 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2 - }, - "cropConsumptionPerLevel": { - "1": 6, - "2": 3, - "3": 3, - "4": 3, - "5": 3 - }, - "effects": [ - { - "effectId": "ironProductionBonus", - "valuesPerLevel": { - "1": 5, - "2": 10, - "3": 15, - "4": 20, - "5": 25 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "IRON_MINE", - "level": 10 - }, - "2": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - } - }, - "wood": { - "1": 200, - "2": 360, - "3": 650, - "4": 1165, - "5": 2100 - }, - "clay": { - "1": 450, - "2": 810, - "3": 1460, - "4": 2625, - "5": 4725 - }, - "iron": { - "1": 510, - "2": 920, - "3": 1650, - "4": 2975, - "5": 5355 - }, - "wheat": { - "1": 120, - "2": 215, - "3": 390, - "4": 700, - "5": 1260 - }, - "totalResourceCostPerLevel": { - "1": 1280, - "2": 2305, - "3": 4150, - "4": 7465, - "5": 13440 - } - }, - { - "id": "IRON_MINE", - "maxLevel": 22, - "baseBuildTimePerLevel": { - "1": 450, - "2": 920, - "3": 1670, - "4": 2880, - "5": 4800, - "6": 7880, - "7": 12810, - "8": 20690, - "9": 33310, - "10": 53500, - "11": 85800, - "12": 137470, - "13": 220160, - "14": 352450, - "15": 564120, - "16": 902790, - "17": 1444660, - "18": 2311660, - "19": 3698850, - "20": 5918370, - "21": 9469590, - "22": 15151540 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38, - "21": 46, - "22": 55 - }, - "cropConsumptionPerLevel": { - "1": 3, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3, - "21": 4, - "22": 4 - }, - "buildingRequirements": {}, - "effects": [ - { - "effectId": "ironProduction", - "valuesPerLevel": { - "1": 4, - "2": 7, - "3": 13, - "4": 21, - "5": 31, - "6": 46, - "7": 70, - "8": 98, - "9": 140, - "10": 203, - "11": 280, - "12": 392, - "13": 525, - "14": 693, - "15": 889, - "16": 1120, - "17": 1400, - "18": 1820, - "19": 2240, - "20": 2800, - "21": 3430 - } - } - ], - "wood": { - "1": 100, - "2": 165, - "3": 280, - "4": 465, - "5": 780, - "6": 1300, - "7": 2170, - "8": 3625, - "9": 6050, - "10": 10105, - "11": 16870, - "12": 28175, - "13": 47055, - "14": 78580, - "15": 131230, - "16": 219155, - "17": 365985, - "18": 611195, - "19": 1020695, - "20": 1704565, - "21": 2846620, - "22": 4753855 - }, - "clay": { - "1": 80, - "2": 135, - "3": 225, - "4": 375, - "5": 620, - "6": 1040, - "7": 1735, - "8": 2900, - "9": 4840, - "10": 8080, - "11": 13500, - "12": 22540, - "13": 37645, - "14": 62865, - "15": 104985, - "16": 175320, - "17": 292790, - "18": 488955, - "19": 816555, - "20": 1363650, - "21": 2277295, - "22": 3803085 - }, - "iron": { - "1": 30, - "2": 50, - "3": 85, - "4": 140, - "5": 235, - "6": 390, - "7": 650, - "8": 1085, - "9": 1815, - "10": 3030, - "11": 5060, - "12": 8455, - "13": 14115, - "14": 23575, - "15": 39370, - "16": 65745, - "17": 109795, - "18": 183360, - "19": 306210, - "20": 511370, - "21": 853985, - "22": 1426155 - }, - "wheat": { - "1": 60, - "2": 100, - "3": 165, - "4": 280, - "5": 465, - "6": 780, - "7": 1300, - "8": 2175, - "9": 3630, - "10": 6060, - "11": 10125, - "12": 16905, - "13": 28230, - "14": 47150, - "15": 78740, - "16": 131490, - "17": 219590, - "18": 366715, - "19": 612420, - "20": 1022740, - "21": 1707970, - "22": 2852315 - }, - "totalResourceCostPerLevel": { - "1": 270, - "2": 450, - "3": 755, - "4": 1260, - "5": 2100, - "6": 3510, - "7": 5855, - "8": 9785, - "9": 16335, - "10": 27275, - "11": 45555, - "12": 76075, - "13": 127045, - "14": 212170, - "15": 354325, - "16": 591710, - "17": 988160, - "18": 1650225, - "19": 2755880, - "20": 4602325, - "21": 7685870, - "22": 12835410 - } - }, - { - "id": "SAWMILL", - "maxLevel": 5, - "baseBuildTimePerLevel": { - "1": 3000, - "2": 5700, - "3": 9750, - "4": 15830, - "5": 24940 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2 - }, - "effects": [ - { - "effectId": "woodProductionBonus", - "valuesPerLevel": { - "1": 5, - "2": 10, - "3": 15, - "4": 20, - "5": 25 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "WOODCUTTER", - "level": 10 - }, - "2": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - } - }, - "wood": { - "1": 520, - "2": 935, - "3": 1685, - "4": 3035, - "5": 5460 - }, - "clay": { - "1": 380, - "2": 685, - "3": 1230, - "4": 2215, - "5": 3990 - }, - "iron": { - "1": 290, - "2": 520, - "3": 940, - "4": 1690, - "5": 3045 - }, - "wheat": { - "1": 90, - "2": 160, - "3": 290, - "4": 525, - "5": 945 - }, - "totalResourceCostPerLevel": { - "1": 1280, - "2": 2300, - "3": 4145, - "4": 7465, - "5": 13440 - } - }, - { - "id": "WAREHOUSE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "warehouseCapacity", - "valuesPerLevel": { - "1": 1200, - "2": 1700, - "3": 2300, - "4": 3100, - "5": 4000, - "6": 5000, - "7": 6300, - "8": 7800, - "9": 9600, - "10": 11800, - "11": 14400, - "12": 17600, - "13": 21400, - "14": 25900, - "15": 31300, - "16": 37900, - "17": 45700, - "18": 55100, - "19": 66400, - "20": 80000 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 1 - } - }, - "wood": { - "1": 130, - "2": 165, - "3": 215, - "4": 275, - "5": 350, - "6": 445, - "7": 570, - "8": 730, - "9": 935, - "10": 1200, - "11": 1535, - "12": 1965, - "13": 2515, - "14": 3220, - "15": 4120, - "16": 5275, - "17": 6750, - "18": 8640, - "19": 11060, - "20": 14155 - }, - "clay": { - "1": 160, - "2": 205, - "3": 260, - "4": 335, - "5": 430, - "6": 550, - "7": 705, - "8": 900, - "9": 1155, - "10": 1475, - "11": 1890, - "12": 2420, - "13": 3095, - "14": 3960, - "15": 5070, - "16": 6490, - "17": 8310, - "18": 10635, - "19": 13610, - "20": 17420 - }, - "iron": { - "1": 90, - "2": 115, - "3": 145, - "4": 190, - "5": 240, - "6": 310, - "7": 395, - "8": 505, - "9": 650, - "10": 830, - "11": 1065, - "12": 1360, - "13": 1740, - "14": 2230, - "15": 2850, - "16": 3650, - "17": 4675, - "18": 5980, - "19": 7655, - "20": 9800 - }, - "wheat": { - "1": 40, - "2": 50, - "3": 65, - "4": 85, - "5": 105, - "6": 135, - "7": 175, - "8": 225, - "9": 290, - "10": 370, - "11": 470, - "12": 605, - "13": 775, - "14": 990, - "15": 1270, - "16": 1625, - "17": 2075, - "18": 2660, - "19": 3405, - "20": 4355 - }, - "totalResourceCostPerLevel": { - "1": 420, - "2": 535, - "3": 685, - "4": 885, - "5": 1125, - "6": 1440, - "7": 1845, - "8": 2360, - "9": 3030, - "10": 3875, - "11": 4960, - "12": 6350, - "13": 8125, - "14": 10400, - "15": 13310, - "16": 17040, - "17": 21810, - "18": 27915, - "19": 35730, - "20": 45730 - } - }, - { - "id": "WATERWORKS", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 2, - "2": 3, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 9, - "9": 10, - "10": 12, - "11": 15, - "12": 18, - "13": 21, - "14": 26, - "15": 31, - "16": 37, - "17": 44, - "18": 53, - "19": 64, - "20": 77 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "oasisProductionBonus", - "valuesPerLevel": { - "1": 1.05, - "2": 1.1, - "3": 1.15, - "4": 1.2, - "5": 1.25, - "6": 1.3, - "7": 1.35, - "8": 1.4, - "9": 1.45, - "10": 1.5, - "11": 1.55, - "12": 1.6, - "13": 1.65, - "14": 1.7, - "15": 1.75, - "16": 1.8, - "17": 1.85, - "18": 1.9, - "19": 1.95, - "20": 2 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "HEROS_MANSION", - "level": 10 - }, - "2": { - "requirementType": "tribe", - "tribe": "egyptians" - } - }, - "wood": { - "1": 910, - "2": 1190, - "3": 1560, - "4": 2045, - "5": 2680, - "6": 3510, - "7": 4600, - "8": 6025, - "9": 7890, - "10": 10340, - "11": 13545, - "12": 17745, - "13": 23245, - "14": 30450, - "15": 39890, - "16": 52255, - "17": 68450, - "18": 89670, - "19": 117470, - "20": 153885 - }, - "clay": { - "1": 945, - "2": 1240, - "3": 1620, - "4": 2125, - "5": 2785, - "6": 3645, - "7": 4775, - "8": 6255, - "9": 8195, - "10": 10735, - "11": 14065, - "12": 18425, - "13": 24135, - "14": 31620, - "15": 41420, - "16": 54265, - "17": 71085, - "18": 93120, - "19": 121985, - "20": 159805 - }, - "iron": { - "1": 910, - "2": 1190, - "3": 1560, - "4": 2045, - "5": 2680, - "6": 3510, - "7": 4600, - "8": 6025, - "9": 7890, - "10": 10340, - "11": 13545, - "12": 17745, - "13": 23245, - "14": 30450, - "15": 39890, - "16": 52255, - "17": 68450, - "18": 89670, - "19": 117470, - "20": 153885 - }, - "wheat": { - "1": 340, - "2": 445, - "3": 585, - "4": 765, - "5": 1000, - "6": 1310, - "7": 1720, - "8": 2250, - "9": 2950, - "10": 3865, - "11": 5060, - "12": 6630, - "13": 8685, - "14": 11375, - "15": 14905, - "16": 19525, - "17": 25575, - "18": 33505, - "19": 43890, - "20": 57495 - }, - "totalResourceCostPerLevel": { - "1": 3105, - "2": 4065, - "3": 5325, - "4": 6980, - "5": 9145, - "6": 11975, - "7": 15695, - "8": 20555, - "9": 26925, - "10": 35280, - "11": 46215, - "12": 60545, - "13": 79310, - "14": 103895, - "15": 136105, - "16": 178300, - "17": 233560, - "18": 305965, - "19": 400815, - "20": 525070 - } - }, - { - "id": "WOODCUTTER", - "maxLevel": 22, - "baseBuildTimePerLevel": { - "1": 260, - "2": 620, - "3": 1190, - "4": 2100, - "5": 3560, - "6": 5890, - "7": 9620, - "8": 15590, - "9": 25150, - "10": 40440, - "11": 64900, - "12": 104050, - "13": 166680, - "14": 266880, - "15": 427210, - "16": 683730, - "17": 1094170, - "18": 1750880, - "19": 2801600, - "20": 4482770, - "21": 7172630, - "22": 11476400 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38, - "21": 46, - "22": 55 - }, - "cropConsumptionPerLevel": { - "1": 2, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3, - "21": 3, - "22": 3 - }, - "buildingRequirements": {}, - "effects": [ - { - "effectId": "woodProduction", - "valuesPerLevel": { - "1": 4, - "2": 7, - "3": 13, - "4": 21, - "5": 31, - "6": 46, - "7": 70, - "8": 98, - "9": 140, - "10": 203, - "11": 280, - "12": 392, - "13": 525, - "14": 693, - "15": 889, - "16": 1120, - "17": 1400, - "18": 1820, - "19": 2240, - "20": 2800, - "21": 3430 - } - } - ], - "wood": { - "1": 40, - "2": 65, - "3": 110, - "4": 185, - "5": 310, - "6": 520, - "7": 870, - "8": 1450, - "9": 2420, - "10": 4040, - "11": 6750, - "12": 11270, - "13": 18820, - "14": 31430, - "15": 52490, - "16": 87660, - "17": 146395, - "18": 244480, - "19": 408280, - "20": 681825, - "21": 1138650, - "22": 1901540 - }, - "clay": { - "1": 100, - "2": 165, - "3": 280, - "4": 465, - "5": 780, - "6": 1300, - "7": 2170, - "8": 3625, - "9": 6050, - "10": 10105, - "11": 16870, - "12": 28175, - "13": 47055, - "14": 78580, - "15": 131230, - "16": 219155, - "17": 365985, - "18": 611195, - "19": 1020695, - "20": 1704565, - "21": 2846620, - "22": 4753855 - }, - "iron": { - "1": 50, - "2": 85, - "3": 140, - "4": 235, - "5": 390, - "6": 650, - "7": 1085, - "8": 1810, - "9": 3025, - "10": 5050, - "11": 8435, - "12": 14090, - "13": 23525, - "14": 39290, - "15": 65615, - "16": 109575, - "17": 182995, - "18": 305600, - "19": 510350, - "20": 852280, - "21": 1423310, - "22": 2376925 - }, - "wheat": { - "1": 60, - "2": 100, - "3": 165, - "4": 280, - "5": 465, - "6": 780, - "7": 1300, - "8": 2175, - "9": 3630, - "10": 6060, - "11": 10125, - "12": 16905, - "13": 28230, - "14": 47150, - "15": 78740, - "16": 131490, - "17": 219590, - "18": 366715, - "19": 612420, - "20": 1022740, - "21": 1707970, - "22": 2852315 - }, - "totalResourceCostPerLevel": { - "1": 250, - "2": 415, - "3": 695, - "4": 1165, - "5": 1945, - "6": 3250, - "7": 5425, - "8": 9060, - "9": 15125, - "10": 25255, - "11": 42180, - "12": 70440, - "13": 117630, - "14": 196450, - "15": 328075, - "16": 547880, - "17": 914965, - "18": 1527990, - "19": 2551745, - "20": 4261410, - "21": 7116550, - "22": 11884635 - } - }, - { - "id": "ACADEMY", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 5, - "2": 6, - "3": 7, - "4": 8, - "5": 10, - "6": 12, - "7": 14, - "8": 17, - "9": 21, - "10": 25, - "11": 30, - "12": 36, - "13": 43, - "14": 51, - "15": 62, - "16": 74, - "17": 89, - "18": 106, - "19": 128, - "20": 153 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 3 - }, - "2": { - "requirementType": "building", - "buildingId": "BARRACKS", - "level": 3 - } - }, - "wood": { - "1": 220, - "2": 280, - "3": 360, - "4": 460, - "5": 590, - "6": 755, - "7": 970, - "8": 1240, - "9": 1585, - "10": 2030, - "11": 2595, - "12": 3325, - "13": 4255, - "14": 5445, - "15": 6970, - "16": 8925, - "17": 11425, - "18": 14620, - "19": 18715, - "20": 23955 - }, - "clay": { - "1": 160, - "2": 205, - "3": 260, - "4": 335, - "5": 430, - "6": 550, - "7": 705, - "8": 900, - "9": 1155, - "10": 1475, - "11": 1890, - "12": 2420, - "13": 3095, - "14": 3960, - "15": 5070, - "16": 6490, - "17": 8310, - "18": 10635, - "19": 13610, - "20": 17420 - }, - "iron": { - "1": 90, - "2": 115, - "3": 145, - "4": 190, - "5": 240, - "6": 310, - "7": 395, - "8": 505, - "9": 650, - "10": 830, - "11": 1065, - "12": 1360, - "13": 1740, - "14": 2230, - "15": 2850, - "16": 3650, - "17": 4675, - "18": 5980, - "19": 7655, - "20": 9800 - }, - "wheat": { - "1": 40, - "2": 50, - "3": 65, - "4": 85, - "5": 105, - "6": 135, - "7": 175, - "8": 225, - "9": 290, - "10": 370, - "11": 470, - "12": 605, - "13": 775, - "14": 990, - "15": 1270, - "16": 1625, - "17": 2075, - "18": 2660, - "19": 3405, - "20": 4355 - }, - "totalResourceCostPerLevel": { - "1": 510, - "2": 650, - "3": 830, - "4": 1070, - "5": 1365, - "6": 1750, - "7": 2245, - "8": 2870, - "9": 3680, - "10": 4705, - "11": 6020, - "12": 7710, - "13": 9865, - "14": 12625, - "15": 16160, - "16": 20690, - "17": 26485, - "18": 33895, - "19": 43385, - "20": 55530 - } - }, - { - "id": "SMITHY", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 2, - "2": 3, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 9, - "9": 10, - "10": 12, - "11": 15, - "12": 18, - "13": 21, - "14": 26, - "15": 31, - "16": 37, - "17": 44, - "18": 53, - "19": 64, - "20": 77 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "effects": [ - { - "effectId": "amountOfUnlockedUnitResearchLevels", - "valuesPerLevel": { - "1": 1, - "2": 2, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 8, - "9": 9, - "10": 10, - "11": 11, - "12": 12, - "13": 13, - "14": 14, - "15": 15, - "16": 16, - "17": 17, - "18": 18, - "19": 19, - "20": 20 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 3 - }, - "2": { - "requirementType": "building", - "buildingId": "ACADEMY", - "level": 1 - } - }, - "wood": { - "1": 180, - "2": 230, - "3": 295, - "4": 375, - "5": 485, - "6": 620, - "7": 790, - "8": 1015, - "9": 1295, - "10": 1660, - "11": 2125, - "12": 2720, - "13": 3480, - "14": 4455, - "15": 5705, - "16": 7300, - "17": 9345, - "18": 11965, - "19": 15315, - "20": 19600 - }, - "clay": { - "1": 250, - "2": 320, - "3": 410, - "4": 525, - "5": 670, - "6": 860, - "7": 1100, - "8": 1405, - "9": 1800, - "10": 2305, - "11": 2950, - "12": 3780, - "13": 4835, - "14": 6190, - "15": 7925, - "16": 10140, - "17": 12980, - "18": 16615, - "19": 21270, - "20": 27225 - }, - "iron": { - "1": 500, - "2": 640, - "3": 820, - "4": 1050, - "5": 1340, - "6": 1720, - "7": 2200, - "8": 2815, - "9": 3605, - "10": 4610, - "11": 5905, - "12": 7555, - "13": 9670, - "14": 12380, - "15": 15845, - "16": 20280, - "17": 25960, - "18": 33230, - "19": 42535, - "20": 54445 - }, - "wheat": { - "1": 160, - "2": 205, - "3": 260, - "4": 335, - "5": 430, - "6": 550, - "7": 705, - "8": 900, - "9": 1155, - "10": 1475, - "11": 1890, - "12": 2420, - "13": 3095, - "14": 3960, - "15": 5070, - "16": 6490, - "17": 8310, - "18": 10635, - "19": 13610, - "20": 17420 - }, - "totalResourceCostPerLevel": { - "1": 1090, - "2": 1395, - "3": 1785, - "4": 2285, - "5": 2925, - "6": 3750, - "7": 4795, - "8": 6135, - "9": 7855, - "10": 10050, - "11": 12870, - "12": 16475, - "13": 21080, - "14": 26985, - "15": 34545, - "16": 44210, - "17": 56595, - "18": 72445, - "19": 92730, - "20": 118690 - } - }, - { - "id": "BARRACKS", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "effects": [ - { - "effectId": "barracksTrainingDuration", - "valuesPerLevel": { - "1": 1, - "2": 0.9091, - "3": 0.8333, - "4": 0.7143, - "5": 0.6667, - "6": 0.5882, - "7": 0.5263, - "8": 0.4762, - "9": 0.4348, - "10": 0.3846, - "11": 0.3448, - "12": 0.3125, - "13": 0.2857, - "14": 0.2564, - "15": 0.2273, - "16": 0.2041, - "17": 0.1852, - "18": 0.1667, - "19": 0.1493, - "20": 0.1351 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 3 - }, - "2": { - "requirementType": "building", - "buildingId": "RALLY_POINT", - "level": 1 - } - }, - "wood": { - "1": 210, - "2": 270, - "3": 345, - "4": 440, - "5": 565, - "6": 720, - "7": 925, - "8": 1180, - "9": 1515, - "10": 1935, - "11": 2480, - "12": 3175, - "13": 4060, - "14": 5200, - "15": 6655, - "16": 8520, - "17": 10905, - "18": 13955, - "19": 17865, - "20": 22865 - }, - "clay": { - "1": 140, - "2": 180, - "3": 230, - "4": 295, - "5": 375, - "6": 480, - "7": 615, - "8": 790, - "9": 1010, - "10": 1290, - "11": 1655, - "12": 2115, - "13": 2710, - "14": 3465, - "15": 4435, - "16": 5680, - "17": 7270, - "18": 9305, - "19": 11910, - "20": 15245 - }, - "iron": { - "1": 260, - "2": 335, - "3": 425, - "4": 545, - "5": 700, - "6": 895, - "7": 1145, - "8": 1465, - "9": 1875, - "10": 2400, - "11": 3070, - "12": 3930, - "13": 5030, - "14": 6435, - "15": 8240, - "16": 10545, - "17": 13500, - "18": 17280, - "19": 22120, - "20": 28310 - }, - "wheat": { - "1": 120, - "2": 155, - "3": 195, - "4": 250, - "5": 320, - "6": 410, - "7": 530, - "8": 675, - "9": 865, - "10": 1105, - "11": 1415, - "12": 1815, - "13": 2320, - "14": 2970, - "15": 3805, - "16": 4870, - "17": 6230, - "18": 7975, - "19": 10210, - "20": 13065 - }, - "totalResourceCostPerLevel": { - "1": 730, - "2": 940, - "3": 1195, - "4": 1530, - "5": 1960, - "6": 2505, - "7": 3215, - "8": 4110, - "9": 5265, - "10": 6730, - "11": 8620, - "12": 11035, - "13": 14120, - "14": 18070, - "15": 23135, - "16": 29615, - "17": 37905, - "18": 48515, - "19": 62105, - "20": 79485 - } - }, - { - "id": "CITY_WALL", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2 - }, - "effects": [ - { - "effectId": "villageDefenceValue", - "valuesPerLevel": { - "1": 10, - "2": 20, - "3": 30, - "4": 40, - "5": 50, - "6": 60, - "7": 70, - "8": 80, - "9": 90, - "10": 100, - "11": 110, - "12": 120, - "13": 130, - "14": 140, - "15": 150, - "16": 160, - "17": 170, - "18": 180, - "19": 190, - "20": 200 - } - }, - { - "effectId": "villageDefenceBonus", - "valuesPerLevel": { - "1": 10, - "2": 20, - "3": 30, - "4": 40, - "5": 50, - "6": 60, - "7": 70, - "8": 80, - "9": 90, - "10": 100, - "11": 110, - "12": 120, - "13": 130, - "14": 140, - "15": 150, - "16": 160, - "17": 170, - "18": 180, - "19": 190, - "20": 200 - } - }, - { - "effectId": "villageDefenceBonus", - "valuesPerLevel": { - "1": [ - 1.03, - 1.061, - 1.093, - 1.126, - 1.159, - 1.194, - 1.23, - 1.267, - 1.305, - 1.344, - 1.384, - 1.426, - 1.469, - 1.513, - 1.558, - 1.605, - 1.653, - 1.702, - 1.754, - 1.806 - ] - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "tribe", - "tribe": "romans" - } - }, - "wood": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "clay": { - "1": 90, - "2": 115, - "3": 145, - "4": 190, - "5": 240, - "6": 310, - "7": 395, - "8": 505, - "9": 650, - "10": 830, - "11": 1065, - "12": 1360, - "13": 1740, - "14": 2230, - "15": 2850, - "16": 3650, - "17": 4675, - "18": 5980, - "19": 7655, - "20": 9800 - }, - "iron": { - "1": 170, - "2": 220, - "3": 280, - "4": 355, - "5": 455, - "6": 585, - "7": 750, - "8": 955, - "9": 1225, - "10": 1570, - "11": 2005, - "12": 2570, - "13": 3290, - "14": 4210, - "15": 5390, - "16": 6895, - "17": 8825, - "18": 11300, - "19": 14460, - "20": 18510 - }, - "wheat": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "totalResourceCostPerLevel": { - "1": 400, - "2": 515, - "3": 655, - "4": 835, - "5": 1075, - "6": 1375, - "7": 1765, - "8": 2250, - "9": 2885, - "10": 3690, - "11": 4720, - "12": 6050, - "13": 7740, - "14": 9910, - "15": 12680, - "16": 16225, - "17": 20770, - "18": 26580, - "19": 34025, - "20": 43550 - } - }, - { - "id": "EARTH_WALL", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2 - }, - "effects": [ - { - "effectId": "villageDefenceValue", - "valuesPerLevel": { - "1": 6, - "2": 12, - "3": 18, - "4": 24, - "5": 30, - "6": 36, - "7": 42, - "8": 48, - "9": 54, - "10": 60, - "11": 66, - "12": 72, - "13": 78, - "14": 84, - "15": 90, - "16": 96, - "17": 102, - "18": 108, - "19": 114, - "20": 120 - } - }, - { - "effectId": "villageDefenceBonus", - "valuesPerLevel": { - "1": 1.02, - "2": 1.04, - "3": 1.061, - "4": 1.082, - "5": 1.104, - "6": 1.126, - "7": 1.149, - "8": 1.172, - "9": 1.195, - "10": 1.219, - "11": 1.243, - "12": 1.268, - "13": 1.294, - "14": 1.319, - "15": 1.346, - "16": 1.373, - "17": 1.4, - "18": 1.428, - "19": 1.457, - "20": 1.486 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "tribe", - "tribe": "teutons" - } - }, - "wood": { - "1": 120, - "2": 155, - "3": 195, - "4": 250, - "5": 320, - "6": 410, - "7": 530, - "8": 675, - "9": 865, - "10": 1105, - "11": 1415, - "12": 1815, - "13": 2320, - "14": 2970, - "15": 3805, - "16": 4870, - "17": 6230, - "18": 7975, - "19": 10210, - "20": 13065 - }, - "clay": { - "1": 200, - "2": 255, - "3": 330, - "4": 420, - "5": 535, - "6": 685, - "7": 880, - "8": 1125, - "9": 1440, - "10": 1845, - "11": 2360, - "12": 3020, - "13": 3870, - "14": 4950, - "15": 6340, - "16": 8115, - "17": 10385, - "18": 13290, - "19": 17015, - "20": 21780 - }, - "iron": { - "1": 0, - "2": 0, - "3": 0, - "4": 0, - "5": 0, - "6": 0, - "7": 0, - "8": 0, - "9": 0, - "10": 0, - "11": 0, - "12": 0, - "13": 0, - "14": 0, - "15": 0, - "16": 0, - "17": 0, - "18": 0, - "19": 0, - "20": 0 - }, - "wheat": { - "1": 80, - "2": 100, - "3": 130, - "4": 170, - "5": 215, - "6": 275, - "7": 350, - "8": 450, - "9": 575, - "10": 740, - "11": 945, - "12": 1210, - "13": 1545, - "14": 1980, - "15": 2535, - "16": 3245, - "17": 4155, - "18": 5315, - "19": 6805, - "20": 8710 - }, - "totalResourceCostPerLevel": { - "1": 400, - "2": 510, - "3": 655, - "4": 840, - "5": 1070, - "6": 1370, - "7": 1760, - "8": 2250, - "9": 2880, - "10": 3690, - "11": 4720, - "12": 6045, - "13": 7735, - "14": 9900, - "15": 12680, - "16": 16230, - "17": 20770, - "18": 26580, - "19": 34030, - "20": 43555 - } - }, - { - "id": "GREAT_BARRACKS", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "effects": [ - { - "effectId": "greatBarracksTrainingDuration", - "valuesPerLevel": { - "1": 1, - "2": 0.9091, - "3": 0.8333, - "4": 0.7143, - "5": 0.6667, - "6": 0.5882, - "7": 0.5263, - "8": 0.4762, - "9": 0.4348, - "10": 0.3846, - "11": 0.3448, - "12": 0.3125, - "13": 0.2857, - "14": 0.2564, - "15": 0.2273, - "16": 0.2041, - "17": 0.1852, - "18": 0.1667, - "19": 0.1493, - "20": 0.1351 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "BARRACKS", - "level": 20 - }, - "2": { - "requirementType": "capital", - "notBuildableInCapital": true - } - }, - "wood": { - "1": 630, - "2": 805, - "3": 1030, - "4": 1320, - "5": 1690, - "6": 2165, - "7": 2770, - "8": 3545, - "9": 4540, - "10": 5810, - "11": 7440, - "12": 9520, - "13": 12185, - "14": 15600, - "15": 19965, - "16": 25555, - "17": 32710, - "18": 41870, - "19": 53595, - "20": 68600 - }, - "clay": { - "1": 420, - "2": 540, - "3": 690, - "4": 880, - "5": 1125, - "6": 1445, - "7": 1845, - "8": 2365, - "9": 3025, - "10": 3875, - "11": 4960, - "12": 6345, - "13": 8125, - "14": 10400, - "15": 13310, - "16": 17035, - "17": 21810, - "18": 27915, - "19": 35730, - "20": 45735 - }, - "iron": { - "1": 780, - "2": 1000, - "3": 1280, - "4": 1635, - "5": 2095, - "6": 2680, - "7": 3430, - "8": 4390, - "9": 5620, - "10": 7195, - "11": 9210, - "12": 11785, - "13": 15085, - "14": 19310, - "15": 24720, - "16": 31640, - "17": 40500, - "18": 51840, - "19": 66355, - "20": 84935 - }, - "wheat": { - "1": 360, - "2": 460, - "3": 590, - "4": 755, - "5": 965, - "6": 1235, - "7": 1585, - "8": 2025, - "9": 2595, - "10": 3320, - "11": 4250, - "12": 5440, - "13": 6965, - "14": 8915, - "15": 11410, - "16": 14605, - "17": 18690, - "18": 23925, - "19": 30625, - "20": 39200 - }, - "totalResourceCostPerLevel": { - "1": 2190, - "2": 2805, - "3": 3590, - "4": 4590, - "5": 5875, - "6": 7525, - "7": 9630, - "8": 12325, - "9": 15780, - "10": 20200, - "11": 25860, - "12": 33090, - "13": 42360, - "14": 54225, - "15": 69405, - "16": 88835, - "17": 113710, - "18": 145550, - "19": 186305, - "20": 238470 - } - }, - { - "id": "GREAT_STABLE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2200, - "2": 2850, - "3": 3610, - "4": 4490, - "5": 5500, - "6": 6680, - "7": 8050, - "8": 9640, - "9": 11480, - "10": 13620, - "11": 16100, - "12": 18980, - "13": 22310, - "14": 26180, - "15": 30670, - "16": 35880, - "17": 41920, - "18": 48930, - "19": 57060, - "20": 66490 - }, - "culturePointsPerLevel": { - "1": 2, - "2": 3, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 9, - "9": 10, - "10": 12, - "11": 15, - "12": 18, - "13": 21, - "14": 26, - "15": 31, - "16": 37, - "17": 44, - "18": 53, - "19": 64, - "20": 77 - }, - "cropConsumptionPerLevel": { - "1": 5, - "2": 3, - "3": 3, - "4": 3, - "5": 3, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 4, - "12": 4, - "13": 4, - "14": 4, - "15": 4, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "effects": [ - { - "effectId": "greatStableTrainingDuration", - "valuesPerLevel": { - "1": 1, - "2": 0.9091, - "3": 0.8333, - "4": 0.7143, - "5": 0.6667, - "6": 0.5882, - "7": 0.5263, - "8": 0.4762, - "9": 0.4348, - "10": 0.3846, - "11": 0.3448, - "12": 0.3125, - "13": 0.2857, - "14": 0.2564, - "15": 0.2273, - "16": 0.2041, - "17": 0.1852, - "18": 0.1667, - "19": 0.1493, - "20": 0.1351 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "STABLE", - "level": 20 - }, - "2": { - "requirementType": "capital", - "notBuildableInCapital": true - } - }, - "wood": { - "1": 780, - "2": 1000, - "3": 1280, - "4": 1635, - "5": 2095, - "6": 2680, - "7": 3430, - "8": 4390, - "9": 5620, - "10": 7195, - "11": 9210, - "12": 11785, - "13": 15085, - "14": 19310, - "15": 24720, - "16": 31640, - "17": 40500, - "18": 51840, - "19": 66355, - "20": 84935 - }, - "clay": { - "1": 420, - "2": 540, - "3": 690, - "4": 880, - "5": 1125, - "6": 1445, - "7": 1845, - "8": 2365, - "9": 3025, - "10": 3875, - "11": 4960, - "12": 6345, - "13": 8125, - "14": 10400, - "15": 13310, - "16": 17035, - "17": 21810, - "18": 27915, - "19": 35730, - "20": 45735 - }, - "iron": { - "1": 660, - "2": 845, - "3": 1080, - "4": 1385, - "5": 1770, - "6": 2270, - "7": 2905, - "8": 3715, - "9": 4755, - "10": 6085, - "11": 7790, - "12": 9975, - "13": 12765, - "14": 16340, - "15": 20915, - "16": 26775, - "17": 34270, - "18": 43865, - "19": 56145, - "20": 71870 - }, - "wheat": { - "1": 300, - "2": 385, - "3": 490, - "4": 630, - "5": 805, - "6": 1030, - "7": 1320, - "8": 1690, - "9": 2160, - "10": 2765, - "11": 3540, - "12": 4535, - "13": 5805, - "14": 7430, - "15": 9505, - "16": 12170, - "17": 15575, - "18": 19940, - "19": 25520, - "20": 32665 - }, - "totalResourceCostPerLevel": { - "1": 2160, - "2": 2770, - "3": 3540, - "4": 4530, - "5": 5795, - "6": 7425, - "7": 9500, - "8": 12160, - "9": 15560, - "10": 19920, - "11": 25500, - "12": 32640, - "13": 41780, - "14": 53480, - "15": 68450, - "16": 87620, - "17": 112155, - "18": 143560, - "19": 183750, - "20": 235205 - } - }, - { - "id": "HERO'S_MANSION", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2300, - "2": 2670, - "3": 3090, - "4": 3590, - "5": 4160, - "6": 4830, - "7": 5600, - "8": 6500, - "9": 7540, - "10": 8750, - "11": 10150, - "12": 11770, - "13": 13650, - "14": 15840, - "15": 18370, - "16": 21310, - "17": 24720, - "18": 28680, - "19": 33260, - "20": 38590 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 2, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3 - }, - "effects": [ - { - "effectId": "oasisExpansionSlot", - "valuesPerLevel": { - "1": 0, - "2": 0, - "3": 0, - "4": 0, - "5": 0, - "6": 0, - "7": 0, - "8": 0, - "9": 0, - "10": 1, - "11": 1, - "12": 1, - "13": 1, - "14": 1, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 3 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 3 - }, - "2": { - "requirementType": "building", - "buildingId": "RALLY_POINT", - "level": 1 - } - }, - "wood": { - "1": 700, - "2": 930, - "3": 1240, - "4": 1645, - "5": 2190, - "6": 2915, - "7": 3875, - "8": 5155, - "9": 6855, - "10": 9115, - "11": 12125, - "12": 16125, - "13": 21445, - "14": 28520, - "15": 37935, - "16": 50450, - "17": 67100, - "18": 89245, - "19": 118695, - "20": 157865 - }, - "clay": { - "1": 670, - "2": 890, - "3": 1185, - "4": 1575, - "5": 2095, - "6": 2790, - "7": 3710, - "8": 4930, - "9": 6560, - "10": 8725, - "11": 11605, - "12": 15435, - "13": 20525, - "14": 27300, - "15": 36310, - "16": 48290, - "17": 64225, - "18": 85420, - "19": 113605, - "20": 151095 - }, - "iron": { - "1": 700, - "2": 930, - "3": 1240, - "4": 1645, - "5": 2190, - "6": 2915, - "7": 3875, - "8": 5155, - "9": 6855, - "10": 9115, - "11": 12125, - "12": 16125, - "13": 21445, - "14": 28520, - "15": 37935, - "16": 50450, - "17": 67100, - "18": 89245, - "19": 118695, - "20": 157865 - }, - "wheat": { - "1": 240, - "2": 320, - "3": 425, - "4": 565, - "5": 750, - "6": 1000, - "7": 1330, - "8": 1765, - "9": 2350, - "10": 3125, - "11": 4155, - "12": 5530, - "13": 7350, - "14": 9780, - "15": 13005, - "16": 17300, - "17": 23005, - "18": 30600, - "19": 40695, - "20": 54125 - }, - "totalResourceCostPerLevel": { - "1": 2310, - "2": 3070, - "3": 4090, - "4": 5430, - "5": 7225, - "6": 9620, - "7": 12790, - "8": 17005, - "9": 22620, - "10": 30080, - "11": 40010, - "12": 53215, - "13": 70765, - "14": 94120, - "15": 125185, - "16": 166490, - "17": 221430, - "18": 294510, - "19": 391690, - "20": 520950 - } - }, - { - "id": "HOSPITAL", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 3000, - "2": 3780, - "3": 4680, - "4": 5730, - "5": 6950, - "6": 8360, - "7": 10000, - "8": 11900, - "9": 14110, - "10": 16660, - "11": 19630, - "12": 23070, - "13": 27060, - "14": 31690, - "15": 37060, - "16": 43290, - "17": 50520, - "18": 58900, - "19": 68630, - "20": 79910 - }, - "culturePointsPerLevel": { - "1": 5, - "2": 6, - "3": 7, - "4": 8, - "5": 10, - "6": 12, - "7": 14, - "8": 17, - "9": 21, - "10": 25, - "11": 30, - "12": 36, - "13": 43, - "14": 51, - "15": 62, - "16": 74, - "17": 89, - "18": 106, - "19": 128, - "20": 153 - }, - "cropConsumptionPerLevel": { - "1": 3, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3 - }, - "effects": [ - { - "effectId": "hospitalTrainingDuration", - "valuesPerLevel": { - "1": 1, - "2": 0.9091, - "3": 0.8333, - "4": 0.7143, - "5": 0.6667, - "6": 0.5882, - "7": 0.5263, - "8": 0.4762, - "9": 0.4348, - "10": 0.3846, - "11": 0.3448, - "12": 0.3125, - "13": 0.2857, - "14": 0.2564, - "15": 0.2273, - "16": 0.2041, - "17": 0.1852, - "18": 0.1667, - "19": 0.1493, - "20": 0.1351 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 10 - }, - "2": { - "requirementType": "building", - "buildingId": "ACADEMY", - "level": 15 - } - }, - "wood": { - "1": 320, - "2": 410, - "3": 525, - "4": 670, - "5": 860, - "6": 1100, - "7": 1405, - "8": 1800, - "9": 2305, - "10": 2950, - "11": 3780, - "12": 4835, - "13": 6190, - "14": 7925, - "15": 10140, - "16": 12980, - "17": 16615, - "18": 21270, - "19": 27225, - "20": 34845 - }, - "clay": { - "1": 280, - "2": 360, - "3": 460, - "4": 585, - "5": 750, - "6": 960, - "7": 1230, - "8": 1575, - "9": 2020, - "10": 2585, - "11": 3305, - "12": 4230, - "13": 5415, - "14": 6930, - "15": 8875, - "16": 11360, - "17": 14540, - "18": 18610, - "19": 23820, - "20": 30490 - }, - "iron": { - "1": 420, - "2": 540, - "3": 690, - "4": 880, - "5": 1125, - "6": 1445, - "7": 1845, - "8": 2365, - "9": 3025, - "10": 3875, - "11": 4960, - "12": 6345, - "13": 8125, - "14": 10400, - "15": 13310, - "16": 17035, - "17": 21810, - "18": 27915, - "19": 35730, - "20": 45735 - }, - "wheat": { - "1": 360, - "2": 460, - "3": 590, - "4": 755, - "5": 965, - "6": 1235, - "7": 1585, - "8": 2025, - "9": 2595, - "10": 3320, - "11": 4250, - "12": 5440, - "13": 6965, - "14": 8915, - "15": 11410, - "16": 14605, - "17": 18690, - "18": 23925, - "19": 30625, - "20": 39200 - }, - "totalResourceCostPerLevel": { - "1": 1380, - "2": 1770, - "3": 2265, - "4": 2890, - "5": 3700, - "6": 4740, - "7": 6065, - "8": 7765, - "9": 9945, - "10": 12730, - "11": 16295, - "12": 20850, - "13": 26695, - "14": 34170, - "15": 43735, - "16": 55980, - "17": 71655, - "18": 91720, - "19": 117400, - "20": 150270 - } - }, - { - "id": "MAKESHIFT_WALL", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2 - }, - "effects": [ - { - "effectId": "villageDefenceValue", - "valuesPerLevel": { - "1": 6, - "2": 12, - "3": 18, - "4": 24, - "5": 30, - "6": 36, - "7": 42, - "8": 48, - "9": 54, - "10": 60, - "11": 66, - "12": 72, - "13": 78, - "14": 84, - "15": 90, - "16": 96, - "17": 102, - "18": 108, - "19": 114, - "20": 120 - } - }, - { - "effectId": "villageDefenceBonus", - "valuesPerLevel": { - "1": 1.015, - "2": 1.03, - "3": 1.046, - "4": 1.061, - "5": 1.077, - "6": 1.093, - "7": 1.11, - "8": 1.126, - "9": 1.143, - "10": 1.161, - "11": 1.178, - "12": 1.196, - "13": 1.214, - "14": 1.232, - "15": 1.25, - "16": 1.27, - "17": 1.288, - "18": 1.307, - "19": 1.327, - "20": 1.347 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "tribe", - "tribe": true - } - }, - "wood": { - "1": 50, - "2": 65, - "3": 80, - "4": 105, - "5": 135, - "6": 170, - "7": 220, - "8": 280, - "9": 360, - "10": 460, - "11": 590, - "12": 755, - "13": 965, - "14": 1240, - "15": 1585, - "16": 2030, - "17": 2595, - "18": 3325, - "19": 4255, - "20": 5445 - }, - "clay": { - "1": 80, - "2": 100, - "3": 130, - "4": 170, - "5": 215, - "6": 275, - "7": 350, - "8": 450, - "9": 575, - "10": 740, - "11": 945, - "12": 1210, - "13": 1545, - "14": 1980, - "15": 2535, - "16": 3245, - "17": 4155, - "18": 5315, - "19": 6805, - "20": 8710 - }, - "iron": { - "1": 40, - "2": 50, - "3": 65, - "4": 85, - "5": 105, - "6": 135, - "7": 175, - "8": 225, - "9": 290, - "10": 370, - "11": 470, - "12": 605, - "13": 775, - "14": 990, - "15": 1270, - "16": 1625, - "17": 2075, - "18": 2660, - "19": 3405, - "20": 4355 - }, - "wheat": { - "1": 30, - "2": 40, - "3": 50, - "4": 65, - "5": 80, - "6": 105, - "7": 130, - "8": 170, - "9": 215, - "10": 275, - "11": 355, - "12": 455, - "13": 580, - "14": 745, - "15": 950, - "16": 1215, - "17": 1560, - "18": 1995, - "19": 2550, - "20": 3265 - }, - "totalResourceCostPerLevel": { - "1": 200, - "2": 255, - "3": 325, - "4": 425, - "5": 535, - "6": 685, - "7": 875, - "8": 1125, - "9": 1440, - "10": 1845, - "11": 2360, - "12": 3025, - "13": 3865, - "14": 4955, - "15": 6340, - "16": 8115, - "17": 10385, - "18": 13295, - "19": 17015, - "20": 21775 - } - }, - { - "id": "PALISADE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2 - }, - "effects": [ - { - "effectId": "villageDefenceValue", - "valuesPerLevel": { - "1": 8, - "2": 16, - "3": 24, - "4": 32, - "5": 40, - "6": 48, - "7": 56, - "8": 64, - "9": 72, - "10": 80, - "11": 88, - "12": 96, - "13": 104, - "14": 112, - "15": 120, - "16": 128, - "17": 136, - "18": 144, - "19": 152, - "20": 160 - } - }, - { - "effectId": "villageDefenceBonus", - "valuesPerLevel": { - "1": 1.025, - "2": 1.051, - "3": 1.077, - "4": 1.104, - "5": 1.131, - "6": 1.16, - "7": 1.189, - "8": 1.218, - "9": 1.249, - "10": 1.28, - "11": 1.312, - "12": 1.345, - "13": 1.379, - "14": 1.413, - "15": 1.448, - "16": 1.485, - "17": 1.522, - "18": 1.56, - "19": 1.599, - "20": 1.639 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "tribe", - "tribe": "gauls" - } - }, - "wood": { - "1": 160, - "2": 205, - "3": 260, - "4": 335, - "5": 430, - "6": 550, - "7": 705, - "8": 900, - "9": 1155, - "10": 1475, - "11": 1890, - "12": 2420, - "13": 3095, - "14": 3960, - "15": 5070, - "16": 6490, - "17": 8310, - "18": 10635, - "19": 13610, - "20": 17420 - }, - "clay": { - "1": 100, - "2": 130, - "3": 165, - "4": 210, - "5": 270, - "6": 345, - "7": 440, - "8": 565, - "9": 720, - "10": 920, - "11": 1180, - "12": 1510, - "13": 1935, - "14": 2475, - "15": 3170, - "16": 4055, - "17": 5190, - "18": 6645, - "19": 8505, - "20": 10890 - }, - "iron": { - "1": 80, - "2": 100, - "3": 130, - "4": 170, - "5": 215, - "6": 275, - "7": 350, - "8": 450, - "9": 575, - "10": 740, - "11": 945, - "12": 1210, - "13": 1545, - "14": 1980, - "15": 2535, - "16": 3245, - "17": 4155, - "18": 5315, - "19": 6805, - "20": 8710 - }, - "wheat": { - "1": 60, - "2": 75, - "3": 100, - "4": 125, - "5": 160, - "6": 205, - "7": 265, - "8": 340, - "9": 430, - "10": 555, - "11": 710, - "12": 905, - "13": 1160, - "14": 1485, - "15": 1900, - "16": 2435, - "17": 3115, - "18": 3990, - "19": 5105, - "20": 6535 - }, - "totalResourceCostPerLevel": { - "1": 400, - "2": 510, - "3": 655, - "4": 840, - "5": 1075, - "6": 1375, - "7": 1760, - "8": 2255, - "9": 2880, - "10": 3690, - "11": 4725, - "12": 6045, - "13": 7735, - "14": 9900, - "15": 12675, - "16": 16225, - "17": 20770, - "18": 26585, - "19": 34025, - "20": 43555 - } - }, - { - "id": "RALLY_POINT", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "amountOfUncoveredAttackingUnits", - "valuesPerLevel": { - "1": 1, - "2": 2, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 8, - "9": 9, - "10": 10, - "11": 11, - "12": 12, - "13": 13, - "14": 14, - "15": 15, - "16": 16, - "17": 17, - "18": 18, - "19": 19, - "20": 20 - } - } - ], - "buildingRequirements": {}, - "wood": { - "1": 110, - "2": 140, - "3": 180, - "4": 230, - "5": 295, - "6": 380, - "7": 485, - "8": 620, - "9": 795, - "10": 1015, - "11": 1300, - "12": 1660, - "13": 2130, - "14": 2725, - "15": 3485, - "16": 4460, - "17": 5710, - "18": 7310, - "19": 9360, - "20": 11980 - }, - "clay": { - "1": 160, - "2": 205, - "3": 260, - "4": 335, - "5": 430, - "6": 550, - "7": 705, - "8": 900, - "9": 1155, - "10": 1475, - "11": 1890, - "12": 2420, - "13": 3095, - "14": 3960, - "15": 5070, - "16": 6490, - "17": 8310, - "18": 10635, - "19": 13610, - "20": 17420 - }, - "iron": { - "1": 90, - "2": 115, - "3": 145, - "4": 190, - "5": 240, - "6": 310, - "7": 395, - "8": 505, - "9": 650, - "10": 830, - "11": 1065, - "12": 1360, - "13": 1740, - "14": 2230, - "15": 2850, - "16": 3650, - "17": 4675, - "18": 5980, - "19": 7655, - "20": 9800 - }, - "wheat": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "totalResourceCostPerLevel": { - "1": 430, - "2": 550, - "3": 700, - "4": 900, - "5": 1155, - "6": 1480, - "7": 1895, - "8": 2420, - "9": 3105, - "10": 3965, - "11": 5080, - "12": 6500, - "13": 8320, - "14": 10650, - "15": 13625, - "16": 17440, - "17": 22330, - "18": 28575, - "19": 36580, - "20": 46820 - } - }, - { - "id": "STABLE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2200, - "2": 2850, - "3": 3610, - "4": 4490, - "5": 5500, - "6": 6680, - "7": 8050, - "8": 9640, - "9": 11480, - "10": 13620, - "11": 16100, - "12": 18980, - "13": 22310, - "14": 26180, - "15": 30670, - "16": 35880, - "17": 41920, - "18": 48930, - "19": 57060, - "20": 66490 - }, - "culturePointsPerLevel": { - "1": 2, - "2": 3, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 9, - "9": 10, - "10": 12, - "11": 15, - "12": 18, - "13": 21, - "14": 26, - "15": 31, - "16": 37, - "17": 44, - "18": 53, - "19": 64, - "20": 77 - }, - "cropConsumptionPerLevel": { - "1": 5, - "2": 3, - "3": 3, - "4": 3, - "5": 3, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 4, - "12": 4, - "13": 4, - "14": 4, - "15": 4, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "effects": [ - { - "effectId": "stableTrainingDuration", - "valuesPerLevel": { - "1": 1, - "2": 0.9091, - "3": 0.8333, - "4": 0.7143, - "5": 0.6667, - "6": 0.5882, - "7": 0.5263, - "8": 0.4762, - "9": 0.4348, - "10": 0.3846, - "11": 0.3448, - "12": 0.3125, - "13": 0.2857, - "14": 0.2564, - "15": 0.2273, - "16": 0.2041, - "17": 0.1852, - "18": 0.1667, - "19": 0.1493, - "20": 0.1351 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "SMITHY", - "level": 3 - }, - "2": { - "requirementType": "building", - "buildingId": "ACADEMY", - "level": 5 - } - }, - "wood": { - "1": 260, - "2": 335, - "3": 425, - "4": 545, - "5": 700, - "6": 895, - "7": 1145, - "8": 1465, - "9": 1875, - "10": 2400, - "11": 3070, - "12": 3930, - "13": 5030, - "14": 6435, - "15": 8240, - "16": 10545, - "17": 13500, - "18": 17280, - "19": 22120, - "20": 28310 - }, - "clay": { - "1": 140, - "2": 180, - "3": 230, - "4": 295, - "5": 375, - "6": 480, - "7": 615, - "8": 790, - "9": 1010, - "10": 1290, - "11": 1655, - "12": 2115, - "13": 2710, - "14": 3465, - "15": 4435, - "16": 5680, - "17": 7270, - "18": 9305, - "19": 11910, - "20": 15245 - }, - "iron": { - "1": 220, - "2": 280, - "3": 360, - "4": 460, - "5": 590, - "6": 755, - "7": 970, - "8": 1240, - "9": 1585, - "10": 2030, - "11": 2595, - "12": 3325, - "13": 4255, - "14": 5445, - "15": 6970, - "16": 8925, - "17": 11425, - "18": 14620, - "19": 18715, - "20": 23955 - }, - "wheat": { - "1": 100, - "2": 130, - "3": 165, - "4": 210, - "5": 270, - "6": 345, - "7": 440, - "8": 565, - "9": 720, - "10": 920, - "11": 1180, - "12": 1510, - "13": 1935, - "14": 2475, - "15": 3170, - "16": 4055, - "17": 5190, - "18": 6645, - "19": 8505, - "20": 10890 - }, - "totalResourceCostPerLevel": { - "1": 720, - "2": 925, - "3": 1180, - "4": 1510, - "5": 1935, - "6": 2475, - "7": 3170, - "8": 4060, - "9": 5190, - "10": 6640, - "11": 8500, - "12": 10880, - "13": 13930, - "14": 17820, - "15": 22815, - "16": 29205, - "17": 37385, - "18": 47850, - "19": 61250, - "20": 78400 - } - }, - { - "id": "STONE_WALL", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2 - }, - "effects": [ - { - "effectId": "villageDefenceValue", - "valuesPerLevel": { - "1": 8, - "2": 16, - "3": 24, - "4": 32, - "5": 40, - "6": 48, - "7": 56, - "8": 64, - "9": 72, - "10": 80, - "11": 88, - "12": 96, - "13": 104, - "14": 112, - "15": 120, - "16": 128, - "17": 136, - "18": 144, - "19": 152, - "20": 160 - } - }, - { - "effectId": "villageDefenceBonus", - "valuesPerLevel": { - "1": 1.025, - "2": 1.051, - "3": 1.077, - "4": 1.104, - "5": 1.131, - "6": 1.16, - "7": 1.189, - "8": 1.218, - "9": 1.249, - "10": 1.28, - "11": 1.312, - "12": 1.345, - "13": 1.379, - "14": 1.413, - "15": 1.448, - "16": 1.485, - "17": 1.522, - "18": 1.56, - "19": 1.599, - "20": 1.639 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "tribe", - "tribe": "egyptians" - } - }, - "wood": { - "1": 110, - "2": 140, - "3": 180, - "4": 230, - "5": 295, - "6": 380, - "7": 485, - "8": 620, - "9": 795, - "10": 1015, - "11": 1300, - "12": 1660, - "13": 2130, - "14": 2725, - "15": 3485, - "16": 4460, - "17": 5710, - "18": 7310, - "19": 9360, - "20": 11980 - }, - "clay": { - "1": 160, - "2": 205, - "3": 260, - "4": 335, - "5": 430, - "6": 550, - "7": 705, - "8": 900, - "9": 1155, - "10": 1475, - "11": 1890, - "12": 2420, - "13": 3095, - "14": 3960, - "15": 5070, - "16": 6490, - "17": 8310, - "18": 10635, - "19": 13610, - "20": 17420 - }, - "iron": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "wheat": { - "1": 60, - "2": 75, - "3": 100, - "4": 125, - "5": 160, - "6": 205, - "7": 265, - "8": 340, - "9": 430, - "10": 555, - "11": 710, - "12": 905, - "13": 1160, - "14": 1485, - "15": 1900, - "16": 2435, - "17": 3115, - "18": 3990, - "19": 5105, - "20": 6535 - }, - "totalResourceCostPerLevel": { - "1": 400, - "2": 510, - "3": 655, - "4": 835, - "5": 1075, - "6": 1375, - "7": 1765, - "8": 2255, - "9": 2885, - "10": 3690, - "11": 4725, - "12": 6045, - "13": 7740, - "14": 9905, - "15": 12675, - "16": 16225, - "17": 20770, - "18": 26585, - "19": 34030, - "20": 43555 - } - }, - { - "id": "STONEMASON", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2200, - "2": 3150, - "3": 4260, - "4": 5540, - "5": 7020, - "6": 8750, - "7": 10750, - "8": 13070, - "9": 15760, - "10": 18880, - "11": 22500, - "12": 26700, - "13": 31570, - "14": 37220, - "15": 43780, - "16": 51380, - "17": 60200, - "18": 70430, - "19": 82300, - "20": 96070 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 2, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3 - }, - "effects": [ - { - "effectId": "buildingDurabilityBonus", - "valuesPerLevel": { - "1": 1.1, - "2": 1.2, - "3": 1.3, - "4": 1.4, - "5": 1.5, - "6": 1.6, - "7": 1.7, - "8": 1.8, - "9": 1.9, - "10": 2, - "11": 2.1, - "12": 2.2, - "13": 2.3, - "14": 2.4, - "15": 2.5, - "16": 2.6, - "17": 2.7, - "18": 2.8, - "19": 2.9, - "20": 3 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - }, - "2": { - "requirementType": "capital", - "onlyBuildableInCapital": true - } - }, - "wood": { - "1": 155, - "2": 200, - "3": 255, - "4": 325, - "5": 415, - "6": 535, - "7": 680, - "8": 875, - "9": 1115, - "10": 1430, - "11": 1830, - "12": 2340, - "13": 3000, - "14": 3840, - "15": 4910, - "16": 6290, - "17": 8050, - "18": 10300, - "19": 13185, - "20": 16880 - }, - "clay": { - "1": 130, - "2": 165, - "3": 215, - "4": 275, - "5": 350, - "6": 445, - "7": 570, - "8": 730, - "9": 935, - "10": 1200, - "11": 1535, - "12": 1965, - "13": 2515, - "14": 3220, - "15": 4120, - "16": 5275, - "17": 6750, - "18": 8640, - "19": 11060, - "20": 14155 - }, - "iron": { - "1": 125, - "2": 160, - "3": 205, - "4": 260, - "5": 335, - "6": 430, - "7": 550, - "8": 705, - "9": 900, - "10": 1155, - "11": 1475, - "12": 1890, - "13": 2420, - "14": 3095, - "15": 3960, - "16": 5070, - "17": 6490, - "18": 8310, - "19": 10635, - "20": 13610 - }, - "wheat": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "totalResourceCostPerLevel": { - "1": 480, - "2": 615, - "3": 790, - "4": 1005, - "5": 1290, - "6": 1650, - "7": 2110, - "8": 2705, - "9": 3455, - "10": 4430, - "11": 5665, - "12": 7255, - "13": 9290, - "14": 11890, - "15": 15210, - "16": 19475, - "17": 24925, - "18": 31900, - "19": 40835, - "20": 52265 - } - }, - { - "id": "TOURNAMENT_SQUARE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 3500, - "2": 4360, - "3": 5360, - "4": 6510, - "5": 7860, - "6": 9410, - "7": 11220, - "8": 13320, - "9": 15750, - "10": 18570, - "11": 21840, - "12": 25630, - "13": 30030, - "14": 35140, - "15": 41060, - "16": 47930, - "17": 55900, - "18": 65140, - "19": 75860, - "20": 88300 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "unitSpeedAfter20TilesBonus", - "valuesPerLevel": { - "1": 1.2, - "2": 1.4, - "3": 1.6, - "4": 1.8, - "5": 2, - "6": 2.2, - "7": 2.4, - "8": 2.6, - "9": 2.8, - "10": 3, - "11": 3.2, - "12": 3.4, - "13": 3.6, - "14": 3.8, - "15": 4, - "16": 4.2, - "17": 4.4, - "18": 4.6, - "19": 4.8, - "20": 5 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "RALLY_POINT", - "level": 15 - } - }, - "wood": { - "1": 1750, - "2": 2240, - "3": 2865, - "4": 3670, - "5": 4700, - "6": 6015, - "7": 7695, - "8": 9850, - "9": 12610, - "10": 16140, - "11": 20660, - "12": 26445, - "13": 33850, - "14": 43330, - "15": 55460, - "16": 70990, - "17": 90865, - "18": 116305, - "19": 148875, - "20": 190560 - }, - "clay": { - "1": 2250, - "2": 2880, - "3": 3685, - "4": 4720, - "5": 6040, - "6": 7730, - "7": 9895, - "8": 12665, - "9": 16215, - "10": 20755, - "11": 26565, - "12": 34000, - "13": 43520, - "14": 55705, - "15": 71305, - "16": 91270, - "17": 116825, - "18": 149540, - "19": 191410, - "20": 245005 - }, - "iron": { - "1": 1530, - "2": 1960, - "3": 2505, - "4": 3210, - "5": 4105, - "6": 5255, - "7": 6730, - "8": 8615, - "9": 11025, - "10": 14110, - "11": 18065, - "12": 23120, - "13": 29595, - "14": 37880, - "15": 48490, - "16": 62065, - "17": 79440, - "18": 101685, - "19": 130160, - "20": 166600 - }, - "wheat": { - "1": 240, - "2": 305, - "3": 395, - "4": 505, - "5": 645, - "6": 825, - "7": 1055, - "8": 1350, - "9": 1730, - "10": 2215, - "11": 2835, - "12": 3625, - "13": 4640, - "14": 5940, - "15": 7605, - "16": 9735, - "17": 12460, - "18": 15950, - "19": 20415, - "20": 26135 - }, - "totalResourceCostPerLevel": { - "1": 5770, - "2": 7385, - "3": 9450, - "4": 12105, - "5": 15490, - "6": 19825, - "7": 25375, - "8": 32480, - "9": 41580, - "10": 53220, - "11": 68125, - "12": 87190, - "13": 111605, - "14": 142855, - "15": 182860, - "16": 234060, - "17": 299590, - "18": 383480, - "19": 490860, - "20": 628300 - } - }, - { - "id": "TRAPPER", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2320, - "3": 2690, - "4": 3120, - "5": 3620, - "6": 4200, - "7": 4870, - "8": 5650, - "9": 6560, - "10": 7610, - "11": 8820, - "12": 10230, - "13": 11870, - "14": 13770, - "15": 15980, - "16": 18530, - "17": 21500, - "18": 24940, - "19": 28930, - "20": 33550 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6, - "11": 7, - "12": 9, - "13": 11, - "14": 13, - "15": 15, - "16": 18, - "17": 22, - "18": 27, - "19": 32, - "20": 38 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "effects": [ - { - "effectId": "trapperCapacity", - "valuesPerLevel": { - "1": 10, - "2": 22, - "3": 35, - "4": 49, - "5": 64, - "6": 80, - "7": 97, - "8": 115, - "9": 134, - "10": 154, - "11": 175, - "12": 196, - "13": 218, - "14": 241, - "15": 265, - "16": 290, - "17": 316, - "18": 343, - "19": 371, - "20": 400 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "RALLY_POINT", - "level": 1 - }, - "2": { - "requirementType": "tribe", - "notBuildableInCapital": "gauls" - } - }, - "wood": { - "1": 80, - "2": 100, - "3": 130, - "4": 170, - "5": 215, - "6": 275, - "7": 350, - "8": 450, - "9": 575, - "10": 740, - "11": 945, - "12": 1210, - "13": 1545, - "14": 1980, - "15": 2535, - "16": 3245, - "17": 4155, - "18": 5315, - "19": 6805, - "20": 8710 - }, - "clay": { - "1": 120, - "2": 155, - "3": 195, - "4": 250, - "5": 320, - "6": 410, - "7": 530, - "8": 675, - "9": 865, - "10": 1105, - "11": 1415, - "12": 1815, - "13": 2320, - "14": 2970, - "15": 3805, - "16": 4870, - "17": 6230, - "18": 7975, - "19": 10210, - "20": 13065 - }, - "iron": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "wheat": { - "1": 90, - "2": 115, - "3": 145, - "4": 190, - "5": 240, - "6": 310, - "7": 395, - "8": 505, - "9": 650, - "10": 830, - "11": 1065, - "12": 1360, - "13": 1740, - "14": 2230, - "15": 2850, - "16": 3650, - "17": 4675, - "18": 5980, - "19": 7655, - "20": 9800 - }, - "totalResourceCostPerLevel": { - "1": 360, - "2": 460, - "3": 585, - "4": 755, - "5": 965, - "6": 1235, - "7": 1585, - "8": 2025, - "9": 2595, - "10": 3320, - "11": 4250, - "12": 5445, - "13": 6960, - "14": 8915, - "15": 11410, - "16": 14605, - "17": 18695, - "18": 23920, - "19": 30625, - "20": 39195 - } - }, - { - "id": "WORKSHOP", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 3000, - "2": 3780, - "3": 4680, - "4": 5730, - "5": 6950, - "6": 8360, - "7": 10000, - "8": 11900, - "9": 14110, - "10": 16660, - "11": 19630, - "12": 23070, - "13": 27060, - "14": 31690, - "15": 37060, - "16": 43290, - "17": 50520, - "18": 58900, - "19": 68630, - "20": 79910 - }, - "culturePointsPerLevel": { - "1": 4, - "2": 4, - "3": 5, - "4": 6, - "5": 7, - "6": 9, - "7": 11, - "8": 13, - "9": 15, - "10": 19, - "11": 22, - "12": 27, - "13": 32, - "14": 39, - "15": 46, - "16": 55, - "17": 67, - "18": 80, - "19": 96, - "20": 115 - }, - "cropConsumptionPerLevel": { - "1": 3, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3 - }, - "effects": [ - { - "effectId": "workshopTrainingDuration", - "valuesPerLevel": { - "1": 1, - "2": 0.9091, - "3": 0.8333, - "4": 0.7143, - "5": 0.6667, - "6": 0.5882, - "7": 0.5263, - "8": 0.4762, - "9": 0.4348, - "10": 0.3846, - "11": 0.3448, - "12": 0.3125, - "13": 0.2857, - "14": 0.2564, - "15": 0.2273, - "16": 0.2041, - "17": 0.1852, - "18": 0.1667, - "19": 0.1493, - "20": 0.1351 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - }, - "2": { - "requirementType": "building", - "buildingId": "ACADEMY", - "level": 10 - } - }, - "wood": { - "1": 460, - "2": 590, - "3": 755, - "4": 965, - "5": 1235, - "6": 1580, - "7": 2025, - "8": 2590, - "9": 3315, - "10": 4245, - "11": 5430, - "12": 6950, - "13": 8900, - "14": 11390, - "15": 14580, - "16": 18660, - "17": 23885, - "18": 30570, - "19": 39130, - "20": 50090 - }, - "clay": { - "1": 510, - "2": 655, - "3": 835, - "4": 1070, - "5": 1370, - "6": 1750, - "7": 2245, - "8": 2870, - "9": 3675, - "10": 4705, - "11": 6020, - "12": 7705, - "13": 9865, - "14": 12625, - "15": 16165, - "16": 20690, - "17": 26480, - "18": 33895, - "19": 43385, - "20": 55535 - }, - "iron": { - "1": 600, - "2": 770, - "3": 985, - "4": 1260, - "5": 1610, - "6": 2060, - "7": 2640, - "8": 3380, - "9": 4325, - "10": 5535, - "11": 7085, - "12": 9065, - "13": 11605, - "14": 14855, - "15": 19015, - "16": 24340, - "17": 31155, - "18": 39875, - "19": 51040, - "20": 65335 - }, - "wheat": { - "1": 320, - "2": 410, - "3": 525, - "4": 670, - "5": 860, - "6": 1100, - "7": 1405, - "8": 1800, - "9": 2305, - "10": 2950, - "11": 3780, - "12": 4835, - "13": 6190, - "14": 7925, - "15": 10140, - "16": 12980, - "17": 16615, - "18": 21270, - "19": 27225, - "20": 34845 - }, - "totalResourceCostPerLevel": { - "1": 1890, - "2": 2425, - "3": 3100, - "4": 3965, - "5": 5075, - "6": 6490, - "7": 8315, - "8": 10640, - "9": 13620, - "10": 17435, - "11": 22315, - "12": 28555, - "13": 36560, - "14": 46795, - "15": 59900, - "16": 76670, - "17": 98135, - "18": 125610, - "19": 160780, - "20": 205805 - } - }, - { - "id": "BREWERY", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 8000, - "2": 9880, - "3": 12060, - "4": 14590, - "5": 17530, - "6": 20930, - "7": 24880, - "8": 29460, - "9": 34770, - "10": 40930, - "11": 48080, - "12": 56380, - "13": 66000, - "14": 77160, - "15": 90100, - "16": 105120, - "17": 122540, - "18": 142750, - "19": 166180, - "20": 193370 - }, - "culturePointsPerLevel": { - "1": 5, - "2": 6, - "3": 7, - "4": 8, - "5": 10, - "6": 12, - "7": 14, - "8": 17, - "9": 21, - "10": 25, - "11": 30, - "12": 36, - "13": 43, - "14": 51, - "15": 62, - "16": 74, - "17": 89, - "18": 106, - "19": 128, - "20": 153 - }, - "cropConsumptionPerLevel": { - "1": 6, - "2": 3, - "3": 3, - "4": 3, - "5": 3, - "6": 4, - "7": 4, - "8": 4, - "9": 4, - "10": 4, - "11": 4, - "12": 4, - "13": 4, - "14": 4, - "15": 4, - "16": 5, - "17": 5, - "18": 5, - "19": 5, - "20": 5 - }, - "effects": [ - { - "effectId": "breweryAttackBonus", - "valuesPerLevel": { - "1": 1.01, - "2": 1.02, - "3": 1.03, - "4": 1.04, - "5": 1.05, - "6": 1.06, - "7": 1.07, - "8": 1.08, - "9": 1.09, - "10": 1.1, - "11": 1.11, - "12": 1.12, - "13": 1.13, - "14": 1.14, - "15": 1.15, - "16": 1.16, - "17": 1.17, - "18": 1.18, - "19": 1.19, - "20": 1.2 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "GRANARY", - "level": 20 - }, - "2": { - "requirementType": "building", - "buildingId": "RALLY_POINT", - "level": 10 - }, - "3": { - "requirementType": "tribe", - "tribe": "teutons" - } - }, - "wood": { - "1": 3210, - "2": 3980, - "3": 4935, - "4": 6120, - "5": 7590, - "6": 9410, - "7": 11670, - "8": 14470, - "9": 17940, - "10": 22250, - "11": 27590, - "12": 34210, - "13": 42420, - "14": 52600, - "15": 65225, - "16": 80880, - "17": 100290, - "18": 124360, - "19": 154205, - "20": 191215 - }, - "clay": { - "1": 2050, - "2": 2540, - "3": 3150, - "4": 3910, - "5": 4845, - "6": 6010, - "7": 7450, - "8": 9240, - "9": 11460, - "10": 14210, - "11": 17620, - "12": 21845, - "13": 27090, - "14": 33590, - "15": 41655, - "16": 51650, - "17": 64045, - "18": 79420, - "19": 98480, - "20": 122115 - }, - "iron": { - "1": 2750, - "2": 3410, - "3": 4230, - "4": 5245, - "5": 6500, - "6": 8060, - "7": 9995, - "8": 12395, - "9": 15370, - "10": 19060, - "11": 23635, - "12": 29305, - "13": 36340, - "14": 45060, - "15": 55875, - "16": 69290, - "17": 85915, - "18": 106535, - "19": 132105, - "20": 163810 - }, - "wheat": { - "1": 3830, - "2": 4750, - "3": 5890, - "4": 7300, - "5": 9055, - "6": 11230, - "7": 13925, - "8": 17265, - "9": 21410, - "10": 26545, - "11": 32915, - "12": 40815, - "13": 50615, - "14": 62760, - "15": 77820, - "16": 96500, - "17": 119660, - "18": 148375, - "19": 183990, - "20": 228145 - }, - "totalResourceCostPerLevel": { - "1": 11840, - "2": 14680, - "3": 18205, - "4": 22575, - "5": 27990, - "6": 34710, - "7": 43040, - "8": 53370, - "9": 66180, - "10": 82065, - "11": 101760, - "12": 126175, - "13": 156465, - "14": 194010, - "15": 240575, - "16": 298320, - "17": 369910, - "18": 458690, - "19": 568780, - "20": 705285 - } - }, - { - "id": "COMMAND_CENTER", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 2, - "2": 3, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 9, - "9": 10, - "10": 12, - "11": 15, - "12": 18, - "13": 21, - "14": 26, - "15": 31, - "16": 37, - "17": 44, - "18": 53, - "19": 64, - "20": 77 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "villageDefenceValue", - "valuesPerLevel": { - "1": 2, - "2": 8, - "3": 18, - "4": 32, - "5": 50, - "6": 72, - "7": 98, - "8": 128, - "9": 162, - "10": 200, - "11": 242, - "12": 288, - "13": 338, - "14": 392, - "15": 450, - "16": 512, - "17": 578, - "18": 648, - "19": 722, - "20": 800 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - }, - "2": { - "requirementType": "tribe", - "tribe": "huns" - } - }, - "wood": { - "1": 1600, - "2": 1950, - "3": 2380, - "4": 2905, - "5": 3545, - "6": 4325, - "7": 5275, - "8": 6435, - "9": 7850, - "10": 9580, - "11": 11685, - "12": 14260, - "13": 17395, - "14": 21225, - "15": 25890, - "16": 31590, - "17": 38535, - "18": 47015, - "19": 57360, - "20": 69975 - }, - "clay": { - "1": 1250, - "2": 1525, - "3": 1860, - "4": 2270, - "5": 2770, - "6": 3380, - "7": 4120, - "8": 5030, - "9": 6135, - "10": 7485, - "11": 9130, - "12": 11140, - "13": 13590, - "14": 16580, - "15": 20230, - "16": 24680, - "17": 30105, - "18": 36730, - "19": 44810, - "20": 54670 - }, - "iron": { - "1": 1050, - "2": 1280, - "3": 1565, - "4": 1905, - "5": 2325, - "6": 2840, - "7": 3460, - "8": 4225, - "9": 5155, - "10": 6285, - "11": 7670, - "12": 9355, - "13": 11415, - "14": 13925, - "15": 16990, - "16": 20730, - "17": 25290, - "18": 30855, - "19": 37640, - "20": 45925 - }, - "wheat": { - "1": 200, - "2": 245, - "3": 300, - "4": 365, - "5": 445, - "6": 540, - "7": 660, - "8": 805, - "9": 980, - "10": 1195, - "11": 1460, - "12": 1780, - "13": 2175, - "14": 2655, - "15": 3235, - "16": 3950, - "17": 4815, - "18": 5875, - "19": 7170, - "20": 8745 - }, - "totalResourceCostPerLevel": { - "1": 4100, - "2": 5000, - "3": 6105, - "4": 7445, - "5": 9085, - "6": 11085, - "7": 13515, - "8": 16495, - "9": 20120, - "10": 24545, - "11": 29945, - "12": 36535, - "13": 44575, - "14": 54385, - "15": 66345, - "16": 80950, - "17": 98745, - "18": 120475, - "19": 146980, - "20": 179315 - } - }, - { - "id": "CRANNY", - "maxLevel": 10, - "baseBuildTimePerLevel": { - "1": 300, - "2": 650, - "3": 1050, - "4": 1520, - "5": 2060, - "6": 2690, - "7": 3420, - "8": 4270, - "9": 5260, - "10": 6400 - }, - "culturePointsPerLevel": { - "1": 1, - "2": 1, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 4, - "8": 4, - "9": 5, - "10": 6 - }, - "cropConsumptionPerLevel": { - "1": 0, - "2": 0, - "3": 0, - "4": 0, - "5": 0, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1 - }, - "effects": [ - { - "effectId": "crannyCapacity", - "valuesPerLevel": { - "1": 100, - "2": 130, - "3": 170, - "4": 220, - "5": 280, - "6": 360, - "7": 460, - "8": 600, - "9": 770, - "10": 1000 - } - } - ], - "buildingRequirements": {}, - "wood": { - "1": 40, - "2": 50, - "3": 65, - "4": 85, - "5": 105, - "6": 135, - "7": 175, - "8": 225, - "9": 290, - "10": 370 - }, - "clay": { - "1": 50, - "2": 65, - "3": 80, - "4": 105, - "5": 135, - "6": 170, - "7": 220, - "8": 280, - "9": 360, - "10": 460 - }, - "iron": { - "1": 30, - "2": 40, - "3": 50, - "4": 65, - "5": 80, - "6": 105, - "7": 130, - "8": 170, - "9": 215, - "10": 275 - }, - "wheat": { - "1": 10, - "2": 15, - "3": 15, - "4": 20, - "5": 25, - "6": 35, - "7": 45, - "8": 55, - "9": 70, - "10": 90 - }, - "totalResourceCostPerLevel": { - "1": 130, - "2": 170, - "3": 210, - "4": 275, - "5": 345, - "6": 445, - "7": 570, - "8": 730, - "9": 935, - "10": 1195 - } - }, - { - "id": "EMBASSY", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 5, - "2": 6, - "3": 7, - "4": 8, - "5": 10, - "6": 12, - "7": 14, - "8": 17, - "9": 21, - "10": 25, - "11": 30, - "12": 36, - "13": 43, - "14": 51, - "15": 62, - "16": 74, - "17": 89, - "18": 106, - "19": 128, - "20": 153 - }, - "cropConsumptionPerLevel": { - "1": 3, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3 - }, - "effects": [ - { - "effectId": "embassyCapacity", - "valuesPerLevel": { - "1": 0, - "2": 0, - "3": 9, - "4": 12, - "5": 15, - "6": 18, - "7": 21, - "8": 24, - "9": 27, - "10": 30, - "11": 33, - "12": 36, - "13": 39, - "14": 42, - "15": 45, - "16": 48, - "17": 51, - "18": 54, - "19": 57, - "20": 60 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 1 - } - }, - "wood": { - "1": 180, - "2": 230, - "3": 295, - "4": 375, - "5": 485, - "6": 620, - "7": 790, - "8": 1015, - "9": 1295, - "10": 1660, - "11": 2125, - "12": 2720, - "13": 3480, - "14": 4455, - "15": 5705, - "16": 7300, - "17": 9345, - "18": 11965, - "19": 15315, - "20": 19600 - }, - "clay": { - "1": 130, - "2": 165, - "3": 215, - "4": 275, - "5": 350, - "6": 445, - "7": 570, - "8": 730, - "9": 935, - "10": 1200, - "11": 1535, - "12": 1965, - "13": 2515, - "14": 3220, - "15": 4120, - "16": 5275, - "17": 6750, - "18": 8640, - "19": 11060, - "20": 14155 - }, - "iron": { - "1": 150, - "2": 190, - "3": 245, - "4": 315, - "5": 405, - "6": 515, - "7": 660, - "8": 845, - "9": 1080, - "10": 1385, - "11": 1770, - "12": 2265, - "13": 2900, - "14": 3715, - "15": 4755, - "16": 6085, - "17": 7790, - "18": 9970, - "19": 12760, - "20": 16335 - }, - "wheat": { - "1": 80, - "2": 100, - "3": 130, - "4": 170, - "5": 215, - "6": 275, - "7": 350, - "8": 450, - "9": 575, - "10": 740, - "11": 945, - "12": 1210, - "13": 1545, - "14": 1980, - "15": 2535, - "16": 3245, - "17": 4155, - "18": 5315, - "19": 6805, - "20": 8710 - }, - "totalResourceCostPerLevel": { - "1": 540, - "2": 685, - "3": 885, - "4": 1135, - "5": 1455, - "6": 1855, - "7": 2370, - "8": 3040, - "9": 3885, - "10": 4985, - "11": 6375, - "12": 8160, - "13": 10440, - "14": 13370, - "15": 17115, - "16": 21905, - "17": 28040, - "18": 35890, - "19": 45940, - "20": 58800 - } - }, - { - "id": "HORSE_DRINKING_TROUGH", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2200, - "2": 3150, - "3": 4260, - "4": 5540, - "5": 7020, - "6": 8750, - "7": 10750, - "8": 13070, - "9": 15760, - "10": 18880, - "11": 22500, - "12": 26700, - "13": 31570, - "14": 37220, - "15": 43780, - "16": 51380, - "17": 60200, - "18": 70430, - "19": 82300, - "20": 96070 - }, - "culturePointsPerLevel": { - "1": 4, - "2": 4, - "3": 5, - "4": 6, - "5": 7, - "6": 9, - "7": 11, - "8": 13, - "9": 15, - "10": 19, - "11": 22, - "12": 27, - "13": 32, - "14": 39, - "15": 46, - "16": 55, - "17": 67, - "18": 80, - "19": 96, - "20": 115 - }, - "cropConsumptionPerLevel": { - "1": 5, - "2": 3, - "3": 3, - "4": 3, - "5": 3, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 4, - "12": 4, - "13": 4, - "14": 4, - "15": 4, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "effects": [ - { - "effectId": "stableTrainingDuration", - "valuesPerLevel": { - "1": 0.99, - "2": 0.98, - "3": 0.97, - "4": 0.96, - "5": 0.95, - "6": 0.94, - "7": 0.93, - "8": 0.92, - "9": 0.91, - "10": 0.9, - "11": 0.89, - "12": 0.88, - "13": 0.86, - "14": 0.85, - "15": 0.84, - "16": 0.83, - "17": 0.82, - "18": 0.81, - "19": 0.8, - "20": 0.78, - "21": 0.77, - "22": 0.75 - } - }, - { - "effectId": "greatStableTrainingDuration", - "valuesPerLevel": { - "1": 0.99, - "2": 0.98, - "3": 0.97, - "4": 0.96, - "5": 0.95, - "6": 0.94, - "7": 0.93, - "8": 0.92, - "9": 0.91, - "10": 0.9, - "11": 0.89, - "12": 0.88, - "13": 0.86, - "14": 0.85, - "15": 0.84, - "16": 0.83, - "17": 0.82, - "18": 0.81, - "19": 0.8, - "20": 0.78, - "21": 0.77, - "22": 0.75 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "RALLY_POINT", - "level": 10 - }, - "2": { - "requirementType": "building", - "buildingId": "STABLE", - "level": 20 - }, - "3": { - "requirementType": "tribe", - "tribe": "romans" - } - }, - "wood": { - "1": 780, - "2": 1000, - "3": 1280, - "4": 1635, - "5": 2095, - "6": 2680, - "7": 3430, - "8": 4390, - "9": 5620, - "10": 7195, - "11": 9210, - "12": 11785, - "13": 15085, - "14": 19310, - "15": 24720, - "16": 31640, - "17": 40500, - "18": 51840, - "19": 66355, - "20": 84935 - }, - "clay": { - "1": 420, - "2": 540, - "3": 690, - "4": 880, - "5": 1125, - "6": 1445, - "7": 1845, - "8": 2365, - "9": 3025, - "10": 3875, - "11": 4960, - "12": 6345, - "13": 8125, - "14": 10400, - "15": 13310, - "16": 17035, - "17": 21810, - "18": 27915, - "19": 35730, - "20": 45735 - }, - "iron": { - "1": 660, - "2": 845, - "3": 1080, - "4": 1385, - "5": 1770, - "6": 2270, - "7": 2905, - "8": 3715, - "9": 4755, - "10": 6085, - "11": 7790, - "12": 9975, - "13": 12765, - "14": 16340, - "15": 20915, - "16": 26775, - "17": 34270, - "18": 43865, - "19": 56145, - "20": 71870 - }, - "wheat": { - "1": 540, - "2": 690, - "3": 885, - "4": 1130, - "5": 1450, - "6": 1855, - "7": 2375, - "8": 3040, - "9": 3890, - "10": 4980, - "11": 6375, - "12": 8160, - "13": 10445, - "14": 13370, - "15": 17115, - "16": 21905, - "17": 28040, - "18": 35890, - "19": 45940, - "20": 58800 - }, - "totalResourceCostPerLevel": { - "1": 2400, - "2": 3075, - "3": 3935, - "4": 5030, - "5": 6440, - "6": 8250, - "7": 10555, - "8": 13510, - "9": 17290, - "10": 22135, - "11": 28335, - "12": 36265, - "13": 46420, - "14": 59420, - "15": 76060, - "16": 97355, - "17": 124620, - "18": 159510, - "19": 204170, - "20": 261340 - } - }, - { - "id": "MAIN_BUILDING", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2500, - "2": 2620, - "3": 3220, - "4": 3880, - "5": 4610, - "6": 5410, - "7": 6300, - "8": 7280, - "9": 8380, - "10": 9590, - "11": 10940, - "12": 12440, - "13": 14120, - "14": 15980, - "15": 18050, - "16": 20370, - "17": 22950, - "18": 25830, - "19": 29040, - "20": 32630 - }, - "culturePointsPerLevel": { - "1": 2, - "2": 3, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 9, - "9": 10, - "10": 12, - "11": 15, - "12": 18, - "13": 21, - "14": 26, - "15": 31, - "16": 37, - "17": 44, - "18": 53, - "19": 64, - "20": 77 - }, - "cropConsumptionPerLevel": { - "1": 2, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3 - }, - "effects": [ - { - "effectId": "buildingDuration", - "valuesPerLevel": { - "1": [ - 1, - 0.98, - 0.96, - 0.94, - 0.92, - 0.9, - 0.88, - 0.86, - 0.83, - 0.81, - 0.78, - 0.75, - 0.73, - 0.7, - 0.67, - 0.64, - 0.6, - 0.57, - 0.54, - 0.5 - ] - } - } - ], - "buildingRequirements": {}, - "wood": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "clay": { - "1": 40, - "2": 50, - "3": 65, - "4": 85, - "5": 105, - "6": 135, - "7": 175, - "8": 225, - "9": 290, - "10": 370, - "11": 470, - "12": 605, - "13": 775, - "14": 990, - "15": 1270, - "16": 1625, - "17": 2075, - "18": 2660, - "19": 3405, - "20": 4355 - }, - "iron": { - "1": 60, - "2": 75, - "3": 100, - "4": 125, - "5": 160, - "6": 205, - "7": 265, - "8": 340, - "9": 430, - "10": 555, - "11": 710, - "12": 905, - "13": 1160, - "14": 1485, - "15": 1900, - "16": 2435, - "17": 3115, - "18": 3990, - "19": 5105, - "20": 6535 - }, - "wheat": { - "1": 20, - "2": 25, - "3": 35, - "4": 40, - "5": 55, - "6": 70, - "7": 90, - "8": 115, - "9": 145, - "10": 185, - "11": 235, - "12": 300, - "13": 385, - "14": 495, - "15": 635, - "16": 810, - "17": 1040, - "18": 1330, - "19": 1700, - "20": 2180 - }, - "totalResourceCostPerLevel": { - "1": 190, - "2": 240, - "3": 315, - "4": 395, - "5": 510, - "6": 650, - "7": 840, - "8": 1075, - "9": 1370, - "10": 1755, - "11": 2240, - "12": 2870, - "13": 3675, - "14": 4705, - "15": 6025, - "16": 7710, - "17": 9865, - "18": 12630, - "19": 16165, - "20": 20690 - } - }, - { - "id": "MARKETPLACE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 1800, - "2": 2390, - "3": 3070, - "4": 3860, - "5": 4780, - "6": 5840, - "7": 7080, - "8": 8510, - "9": 10170, - "10": 12100, - "11": 14340, - "12": 16930, - "13": 19940, - "14": 23430, - "15": 27480, - "16": 32180, - "17": 37620, - "18": 43940, - "19": 51270, - "20": 59780 - }, - "culturePointsPerLevel": { - "1": 4, - "2": 4, - "3": 5, - "4": 6, - "5": 7, - "6": 9, - "7": 11, - "8": 13, - "9": 15, - "10": 19, - "11": 22, - "12": 27, - "13": 32, - "14": 39, - "15": 46, - "16": 55, - "17": 67, - "18": 80, - "19": 96, - "20": 115 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "effects": [ - { - "effectId": "merchantAmount", - "valuesPerLevel": { - "1": 1, - "2": 2, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 8, - "9": 9, - "10": 10, - "11": 11, - "12": 12, - "13": 13, - "14": 14, - "15": 15, - "16": 16, - "17": 17, - "18": 18, - "19": 19, - "20": 20 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "WAREHOUSE", - "level": 1 - }, - "2": { - "requirementType": "building", - "buildingId": "GRANARY", - "level": 1 - }, - "3": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 3 - } - }, - "wood": { - "1": 80, - "2": 100, - "3": 130, - "4": 170, - "5": 215, - "6": 275, - "7": 350, - "8": 450, - "9": 575, - "10": 740, - "11": 945, - "12": 1210, - "13": 1545, - "14": 1980, - "15": 2535, - "16": 3245, - "17": 4155, - "18": 5315, - "19": 6805, - "20": 8710 - }, - "clay": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "iron": { - "1": 120, - "2": 155, - "3": 195, - "4": 250, - "5": 320, - "6": 410, - "7": 530, - "8": 675, - "9": 865, - "10": 1105, - "11": 1415, - "12": 1815, - "13": 2320, - "14": 2970, - "15": 3805, - "16": 4870, - "17": 6230, - "18": 7975, - "19": 10210, - "20": 13065 - }, - "wheat": { - "1": 70, - "2": 90, - "3": 115, - "4": 145, - "5": 190, - "6": 240, - "7": 310, - "8": 395, - "9": 505, - "10": 645, - "11": 825, - "12": 1060, - "13": 1355, - "14": 1735, - "15": 2220, - "16": 2840, - "17": 3635, - "18": 4650, - "19": 5955, - "20": 7620 - }, - "totalResourceCostPerLevel": { - "1": 340, - "2": 435, - "3": 555, - "4": 710, - "5": 915, - "6": 1165, - "7": 1500, - "8": 1915, - "9": 2450, - "10": 3135, - "11": 4010, - "12": 5145, - "13": 6575, - "14": 8420, - "15": 10780, - "16": 13795, - "17": 17655, - "18": 22590, - "19": 28925, - "20": 37015 - } - }, - { - "id": "PALACE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 5000, - "2": 6100, - "3": 7380, - "4": 8860, - "5": 10570, - "6": 12560, - "7": 14880, - "8": 17560, - "9": 20660, - "10": 24270, - "11": 28450, - "12": 33310, - "13": 38940, - "14": 45460, - "15": 53040, - "16": 61830, - "17": 72020, - "18": 83840, - "19": 97550, - "20": 113460 - }, - "culturePointsPerLevel": { - "1": 6, - "2": 7, - "3": 9, - "4": 10, - "5": 12, - "6": 15, - "7": 18, - "8": 21, - "9": 26, - "10": 31, - "11": 37, - "12": 45, - "13": 53, - "14": 64, - "15": 77, - "16": 92, - "17": 111, - "18": 133, - "19": 160, - "20": 192 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "villageDefenceValue", - "valuesPerLevel": { - "1": 2, - "2": 8, - "3": 18, - "4": 32, - "5": 50, - "6": 72, - "7": 98, - "8": 128, - "9": 162, - "10": 200, - "11": 242, - "12": 288, - "13": 338, - "14": 392, - "15": 450, - "16": 512, - "17": 578, - "18": 648, - "19": 722, - "20": 800 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - }, - "2": { - "requirementType": "building", - "buildingId": "EMBASSY", - "level": 1 - } - }, - "wood": { - "1": 550, - "2": 705, - "3": 900, - "4": 1155, - "5": 1475, - "6": 1890, - "7": 2420, - "8": 3095, - "9": 3965, - "10": 5075, - "11": 6495, - "12": 8310, - "13": 10640, - "14": 13615, - "15": 17430, - "16": 22310, - "17": 28560, - "18": 36555, - "19": 46790, - "20": 59890 - }, - "clay": { - "1": 800, - "2": 1025, - "3": 1310, - "4": 1680, - "5": 2145, - "6": 2750, - "7": 3520, - "8": 4505, - "9": 5765, - "10": 7380, - "11": 9445, - "12": 12090, - "13": 15475, - "14": 19805, - "15": 25355, - "16": 32450, - "17": 41540, - "18": 53170, - "19": 68055, - "20": 87110 - }, - "iron": { - "1": 750, - "2": 960, - "3": 1230, - "4": 1575, - "5": 2015, - "6": 2575, - "7": 3300, - "8": 4220, - "9": 5405, - "10": 6920, - "11": 8855, - "12": 11335, - "13": 14505, - "14": 18570, - "15": 23770, - "16": 30425, - "17": 38940, - "18": 49845, - "19": 63805, - "20": 81670 - }, - "wheat": { - "1": 250, - "2": 320, - "3": 410, - "4": 525, - "5": 670, - "6": 860, - "7": 1100, - "8": 1405, - "9": 1800, - "10": 2305, - "11": 2950, - "12": 3780, - "13": 4835, - "14": 6190, - "15": 7925, - "16": 10140, - "17": 12980, - "18": 16615, - "19": 21270, - "20": 27225 - }, - "totalResourceCostPerLevel": { - "1": 2350, - "2": 3010, - "3": 3850, - "4": 4935, - "5": 6305, - "6": 8075, - "7": 10340, - "8": 13225, - "9": 16935, - "10": 21680, - "11": 27745, - "12": 35515, - "13": 45455, - "14": 58180, - "15": 74480, - "16": 95325, - "17": 122020, - "18": 156185, - "19": 199920, - "20": 255895 - } - }, - { - "id": "RESIDENCE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 2000, - "2": 2620, - "3": 3340, - "4": 4170, - "5": 5140, - "6": 6260, - "7": 7570, - "8": 9080, - "9": 10830, - "10": 12860, - "11": 15220, - "12": 17950, - "13": 21130, - "14": 24810, - "15": 29080, - "16": 34030, - "17": 39770, - "18": 46440, - "19": 54170, - "20": 63130 - }, - "culturePointsPerLevel": { - "1": 2, - "2": 3, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7, - "8": 9, - "9": 10, - "10": 12, - "11": 15, - "12": 18, - "13": 21, - "14": 26, - "15": 31, - "16": 37, - "17": 44, - "18": 53, - "19": 64, - "20": 77 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2 - }, - "effects": [ - { - "effectId": "villageDefenceValue", - "valuesPerLevel": { - "1": 2, - "2": 8, - "3": 18, - "4": 32, - "5": 50, - "6": 72, - "7": 98, - "8": 128, - "9": 162, - "10": 200, - "11": 242, - "12": 288, - "13": 338, - "14": 392, - "15": 450, - "16": 512, - "17": 578, - "18": 648, - "19": 722, - "20": 800 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 5 - } - }, - "wood": { - "1": 580, - "2": 740, - "3": 950, - "4": 1215, - "5": 1555, - "6": 1995, - "7": 2550, - "8": 3265, - "9": 4180, - "10": 5350, - "11": 6845, - "12": 8765, - "13": 11220, - "14": 14360, - "15": 18380, - "16": 23530, - "17": 30115, - "18": 38550, - "19": 49340, - "20": 63155 - }, - "clay": { - "1": 460, - "2": 590, - "3": 755, - "4": 965, - "5": 1235, - "6": 1580, - "7": 2025, - "8": 2590, - "9": 3315, - "10": 4245, - "11": 5430, - "12": 6950, - "13": 8900, - "14": 11390, - "15": 14580, - "16": 18660, - "17": 23885, - "18": 30570, - "19": 39130, - "20": 50090 - }, - "iron": { - "1": 350, - "2": 450, - "3": 575, - "4": 735, - "5": 940, - "6": 1205, - "7": 1540, - "8": 1970, - "9": 2520, - "10": 3230, - "11": 4130, - "12": 5290, - "13": 6770, - "14": 8665, - "15": 11090, - "16": 14200, - "17": 18175, - "18": 23260, - "19": 29775, - "20": 38110 - }, - "wheat": { - "1": 180, - "2": 230, - "3": 295, - "4": 375, - "5": 485, - "6": 620, - "7": 790, - "8": 1015, - "9": 1295, - "10": 1660, - "11": 2125, - "12": 2720, - "13": 3480, - "14": 4455, - "15": 5705, - "16": 7300, - "17": 9345, - "18": 11965, - "19": 15315, - "20": 19600 - }, - "totalResourceCostPerLevel": { - "1": 1570, - "2": 2010, - "3": 2575, - "4": 3290, - "5": 4215, - "6": 5400, - "7": 6905, - "8": 8840, - "9": 11310, - "10": 14485, - "11": 18530, - "12": 23725, - "13": 30370, - "14": 38870, - "15": 49755, - "16": 63690, - "17": 81520, - "18": 104345, - "19": 133560, - "20": 170955 - } - }, - { - "id": "TOWN_HALL", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 12500, - "2": 14800, - "3": 17470, - "4": 20560, - "5": 24150, - "6": 28320, - "7": 33150, - "8": 38750, - "9": 45250, - "10": 52790, - "11": 61540, - "12": 71690, - "13": 83460, - "14": 97110, - "15": 112950, - "16": 131320, - "17": 152630, - "18": 177350, - "19": 206020, - "20": 239290 - }, - "culturePointsPerLevel": { - "1": 6, - "2": 7, - "3": 9, - "4": 10, - "5": 12, - "6": 15, - "7": 18, - "8": 21, - "9": 26, - "10": 31, - "11": 37, - "12": 45, - "13": 53, - "14": 64, - "15": 77, - "16": 92, - "17": 111, - "18": 133, - "19": 160, - "20": 192 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 10 - }, - "2": { - "requirementType": "building", - "buildingId": "ACADEMY", - "level": 10 - } - }, - "wood": { - "1": 1250, - "2": 1600, - "3": 2050, - "4": 2620, - "5": 3355, - "6": 4295, - "7": 5500, - "8": 7035, - "9": 9005, - "10": 11530, - "11": 14755, - "12": 18890, - "13": 24180, - "14": 30950, - "15": 39615, - "16": 50705, - "17": 64905, - "18": 83075, - "19": 106340, - "20": 136115 - }, - "clay": { - "1": 1110, - "2": 1420, - "3": 1820, - "4": 2330, - "5": 2980, - "6": 3815, - "7": 4880, - "8": 6250, - "9": 8000, - "10": 10240, - "11": 13105, - "12": 16775, - "13": 21470, - "14": 27480, - "15": 35175, - "16": 45025, - "17": 57635, - "18": 73770, - "19": 94430, - "20": 120870 - }, - "iron": { - "1": 1260, - "2": 1615, - "3": 2065, - "4": 2640, - "5": 3380, - "6": 4330, - "7": 5540, - "8": 7095, - "9": 9080, - "10": 11620, - "11": 14875, - "12": 19040, - "13": 24370, - "14": 31195, - "15": 39930, - "16": 51110, - "17": 65425, - "18": 83740, - "19": 107190, - "20": 137200 - }, - "wheat": { - "1": 600, - "2": 770, - "3": 985, - "4": 1260, - "5": 1610, - "6": 2060, - "7": 2640, - "8": 3380, - "9": 4325, - "10": 5535, - "11": 7085, - "12": 9065, - "13": 11605, - "14": 14855, - "15": 19015, - "16": 24340, - "17": 31155, - "18": 39875, - "19": 51040, - "20": 65335 - }, - "totalResourceCostPerLevel": { - "1": 4220, - "2": 5405, - "3": 6920, - "4": 8850, - "5": 11325, - "6": 14500, - "7": 18560, - "8": 23760, - "9": 30410, - "10": 38925, - "11": 49820, - "12": 63770, - "13": 81625, - "14": 104480, - "15": 133735, - "16": 171180, - "17": 219120, - "18": 280460, - "19": 359000, - "20": 459520 - } - }, - { - "id": "TRADE_OFFICE", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 3000, - "2": 3780, - "3": 4680, - "4": 5730, - "5": 6950, - "6": 8360, - "7": 10000, - "8": 11900, - "9": 14110, - "10": 16660, - "11": 19630, - "12": 23070, - "13": 27060, - "14": 31690, - "15": 37060, - "16": 43290, - "17": 50520, - "18": 58900, - "19": 68630, - "20": 79910 - }, - "culturePointsPerLevel": { - "1": 4, - "2": 4, - "3": 5, - "4": 6, - "5": 7, - "6": 9, - "7": 11, - "8": 13, - "9": 15, - "10": 19, - "11": 22, - "12": 27, - "13": 32, - "14": 39, - "15": 46, - "16": 55, - "17": 67, - "18": 80, - "19": 96, - "20": 115 - }, - "cropConsumptionPerLevel": { - "1": 3, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 2, - "7": 2, - "8": 2, - "9": 2, - "10": 2, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 3, - "17": 3, - "18": 3, - "19": 3, - "20": 3 - }, - "effects": [ - { - "effectId": "merchantAmountBonus", - "valuesPerLevel": { - "1": 120, - "2": 140, - "3": 160, - "4": 180, - "5": 200, - "6": 220, - "7": 240, - "8": 260, - "9": 280, - "10": 300, - "11": 320, - "12": 340, - "13": 360, - "14": 380, - "15": 400, - "16": 420, - "17": 440, - "18": 460, - "19": 480, - "20": 500 - } - } - ], - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MARKETPLACE", - "level": 20 - }, - "2": { - "requirementType": "building", - "buildingId": "STABLE", - "level": 10 - } - }, - "wood": { - "1": 1400, - "2": 1790, - "3": 2295, - "4": 2935, - "5": 3760, - "6": 4810, - "7": 6155, - "8": 7880, - "9": 10090, - "10": 12915, - "11": 16530, - "12": 21155, - "13": 27080, - "14": 34660, - "15": 44370, - "16": 56790, - "17": 72690, - "18": 93045, - "19": 119100, - "20": 152445 - }, - "clay": { - "1": 1330, - "2": 1700, - "3": 2180, - "4": 2790, - "5": 3570, - "6": 4570, - "7": 5850, - "8": 7485, - "9": 9585, - "10": 12265, - "11": 15700, - "12": 20100, - "13": 25725, - "14": 32930, - "15": 42150, - "16": 53950, - "17": 69060, - "18": 88395, - "19": 113145, - "20": 144825 - }, - "iron": { - "1": 1200, - "2": 1535, - "3": 1965, - "4": 2515, - "5": 3220, - "6": 4125, - "7": 5280, - "8": 6755, - "9": 8645, - "10": 11070, - "11": 14165, - "12": 18135, - "13": 23210, - "14": 29710, - "15": 38030, - "16": 48680, - "17": 62310, - "18": 79755, - "19": 102085, - "20": 130670 - }, - "wheat": { - "1": 400, - "2": 510, - "3": 655, - "4": 840, - "5": 1075, - "6": 1375, - "7": 1760, - "8": 2250, - "9": 2880, - "10": 3690, - "11": 4720, - "12": 6045, - "13": 7735, - "14": 9905, - "15": 12675, - "16": 16225, - "17": 20770, - "18": 26585, - "19": 34030, - "20": 43555 - }, - "totalResourceCostPerLevel": { - "1": 4330, - "2": 5535, - "3": 7095, - "4": 9080, - "5": 11625, - "6": 14880, - "7": 19045, - "8": 24370, - "9": 31200, - "10": 39940, - "11": 51115, - "12": 65435, - "13": 83750, - "14": 107205, - "15": 137225, - "16": 175645, - "17": 224830, - "18": 287780, - "19": 368360, - "20": 471495 - } - }, - { - "id": "TREASURY", - "maxLevel": 20, - "baseBuildTimePerLevel": { - "1": 8000, - "2": 9580, - "3": 11410, - "4": 13540, - "5": 16010, - "6": 18870, - "7": 22180, - "8": 26030, - "9": 30500, - "10": 35680, - "11": 41690, - "12": 48660, - "13": 56740, - "14": 66120, - "15": 77000, - "16": 89620, - "17": 104260, - "18": 121240, - "19": 140940, - "20": 163790 - }, - "culturePointsPerLevel": { - "1": 7, - "2": 9, - "3": 10, - "4": 12, - "5": 15, - "6": 18, - "7": 21, - "8": 26, - "9": 31, - "10": 37, - "11": 45, - "12": 53, - "13": 64, - "14": 77, - "15": 92, - "16": 111, - "17": 133, - "18": 160, - "19": 192, - "20": 230 - }, - "cropConsumptionPerLevel": { - "1": 4, - "2": 2, - "3": 2, - "4": 2, - "5": 2, - "6": 3, - "7": 3, - "8": 3, - "9": 3, - "10": 3, - "11": 3, - "12": 3, - "13": 3, - "14": 3, - "15": 3, - "16": 4, - "17": 4, - "18": 4, - "19": 4, - "20": 4 - }, - "buildingRequirements": { - "1": { - "requirementType": "building", - "buildingId": "MAIN_BUILDING", - "level": 10 - } - }, - "wood": { - "1": 2880, - "2": 3630, - "3": 4570, - "4": 5760, - "5": 7260, - "6": 9145, - "7": 11525, - "8": 14520, - "9": 18295, - "10": 23055, - "11": 29045, - "12": 36600, - "13": 46115, - "14": 58105, - "15": 73210, - "16": 92245, - "17": 116230, - "18": 146450, - "19": 184530, - "20": 232505 - }, - "clay": { - "1": 2740, - "2": 3450, - "3": 4350, - "4": 5480, - "5": 6905, - "6": 8700, - "7": 10965, - "8": 13815, - "9": 17405, - "10": 21930, - "11": 27635, - "12": 34820, - "13": 43875, - "14": 55280, - "15": 69655, - "16": 87760, - "17": 110580, - "18": 139330, - "19": 175560, - "20": 221205 - }, - "iron": { - "1": 2580, - "2": 3250, - "3": 4095, - "4": 5160, - "5": 6505, - "6": 8195, - "7": 10325, - "8": 13010, - "9": 16390, - "10": 20650, - "11": 26020, - "12": 32785, - "13": 41310, - "14": 52050, - "15": 65585, - "16": 82640, - "17": 104125, - "18": 131195, - "19": 165305, - "20": 208285 - }, - "wheat": { - "1": 990, - "2": 1245, - "3": 1570, - "4": 1980, - "5": 2495, - "6": 3145, - "7": 3960, - "8": 4990, - "9": 6290, - "10": 7925, - "11": 9985, - "12": 12580, - "13": 15850, - "14": 19975, - "15": 25165, - "16": 31710, - "17": 39955, - "18": 50340, - "19": 63430, - "20": 79925 - }, - "totalResourceCostPerLevel": { - "1": 9190, - "2": 11575, - "3": 14585, - "4": 18380, - "5": 23165, - "6": 29185, - "7": 36775, - "8": 46335, - "9": 58380, - "10": 73560, - "11": 92685, - "12": 116785, - "13": 147150, - "14": 185410, - "15": 233615, - "16": 294355, - "17": 370890, - "18": 467315, - "19": 588825, - "20": 741920 - } - }, - { - "id": "WONDER_OF_THE_WORLD", - "maxLevel": 100, - "baseBuildTimePerLevel": { - "1": 9000, - "2": 9430, - "3": 9860, - "4": 10300, - "5": 10740, - "6": 11190, - "7": 11650, - "8": 12110, - "9": 12580, - "10": 13060, - "11": 13540, - "12": 14030, - "13": 14520, - "14": 15030, - "15": 15540, - "16": 16060, - "17": 16580, - "18": 17110, - "19": 17650, - "20": 18200, - "21": 18750, - "22": 19320, - "23": 19890, - "24": 20470, - "25": 21050, - "26": 21650, - "27": 22250, - "28": 22860, - "29": 23480, - "30": 24110, - "31": 24750, - "32": 25390, - "33": 26050, - "34": 26710, - "35": 27390, - "36": 28070, - "37": 28760, - "38": 29470, - "39": 30180, - "40": 30900, - "41": 31640, - "42": 32380, - "43": 33130, - "44": 33900, - "45": 34670, - "46": 35460, - "47": 36250, - "48": 37060, - "49": 37880, - "50": 38710, - "51": 39550, - "52": 40400, - "53": 41270, - "54": 42150, - "55": 43040, - "56": 43940, - "57": 44860, - "58": 45780, - "59": 46720, - "60": 47680, - "61": 48650, - "62": 49630, - "63": 50620, - "64": 51630, - "65": 52650, - "66": 53690, - "67": 54740, - "68": 55810, - "69": 56890, - "70": 57990, - "71": 59100, - "72": 60230, - "73": 61370, - "74": 62530, - "75": 63700, - "76": 64890, - "77": 66100, - "78": 67330, - "79": 68570, - "80": 69830, - "81": 71110, - "82": 72400, - "83": 73720, - "84": 75050, - "85": 76400, - "86": 77770, - "87": 79160, - "88": 80570, - "89": 82000, - "90": 83440, - "91": 84910, - "92": 86400, - "93": 87910, - "94": 89440, - "95": 90990, - "96": 92570, - "97": 94160, - "98": 95780, - "99": 97420, - "100": 99090 - }, - "culturePointsPerLevel": { - "1": 0, - "2": 0, - "3": 0, - "4": 0, - "5": 0, - "6": 0, - "7": 0, - "8": 0, - "9": 0, - "10": 0, - "11": 0, - "12": 0, - "13": 0, - "14": 0, - "15": 0, - "16": 0, - "17": 0, - "18": 0, - "19": 0, - "20": 0, - "21": 0, - "22": 0, - "23": 0, - "24": 0, - "25": 0, - "26": 0, - "27": 0, - "28": 0, - "29": 0, - "30": 0, - "31": 0, - "32": 0, - "33": 0, - "34": 0, - "35": 0, - "36": 0, - "37": 0, - "38": 0, - "39": 0, - "40": 0, - "41": 0, - "42": 0, - "43": 0, - "44": 0, - "45": 0, - "46": 0, - "47": 0, - "48": 0, - "49": 0, - "50": 0, - "51": 0, - "52": 0, - "53": 0, - "54": 0, - "55": 0, - "56": 0, - "57": 0, - "58": 0, - "59": 0, - "60": 0, - "61": 0, - "62": 0, - "63": 0, - "64": 0, - "65": 0, - "66": 0, - "67": 0, - "68": 0, - "69": 0, - "70": 0, - "71": 0, - "72": 0, - "73": 0, - "74": 0, - "75": 0, - "76": 0, - "77": 0, - "78": 0, - "79": 0, - "80": 0, - "81": 0, - "82": 0, - "83": 0, - "84": 0, - "85": 0, - "86": 0, - "87": 0, - "88": 0, - "89": 0, - "90": 0, - "91": 0, - "92": 0, - "93": 0, - "94": 0, - "95": 0, - "96": 0, - "97": 0, - "98": 0, - "99": 0, - "100": 0 - }, - "cropConsumptionPerLevel": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1, - "6": 1, - "7": 1, - "8": 1, - "9": 1, - "10": 1, - "11": 2, - "12": 2, - "13": 2, - "14": 2, - "15": 2, - "16": 2, - "17": 2, - "18": 2, - "19": 2, - "20": 2, - "21": 3, - "22": 3, - "23": 3, - "24": 3, - "25": 3, - "26": 3, - "27": 3, - "28": 3, - "29": 3, - "30": 3, - "31": 4, - "32": 4, - "33": 4, - "34": 4, - "35": 4, - "36": 4, - "37": 4, - "38": 4, - "39": 4, - "40": 4, - "41": 5, - "42": 5, - "43": 5, - "44": 5, - "45": 5, - "46": 5, - "47": 5, - "48": 5, - "49": 5, - "50": 5, - "51": 6, - "52": 6, - "53": 6, - "54": 6, - "55": 6, - "56": 6, - "57": 6, - "58": 6, - "59": 6, - "60": 6, - "61": 7, - "62": 7, - "63": 7, - "64": 7, - "65": 7, - "66": 7, - "67": 7, - "68": 7, - "69": 7, - "70": 7, - "71": 8, - "72": 8, - "73": 8, - "74": 8, - "75": 8, - "76": 8, - "77": 8, - "78": 8, - "79": 8, - "80": 8, - "81": 9, - "82": 9, - "83": 9, - "84": 9, - "85": 9, - "86": 9, - "87": 9, - "88": 9, - "89": 9, - "90": 9, - "91": 10, - "92": 10, - "93": 10, - "94": 10, - "95": 10, - "96": 10, - "97": 10, - "98": 10, - "99": 10, - "100": 10 - }, - "buildingRequirements": {}, - "wood": { - "1": 66700, - "2": 68535, - "3": 70420, - "4": 72355, - "5": 74345, - "6": 76390, - "7": 78490, - "8": 80650, - "9": 82865, - "10": 85145, - "11": 87485, - "12": 89895, - "13": 92365, - "14": 94905, - "15": 97515, - "16": 100195, - "17": 102950, - "18": 105785, - "19": 108690, - "20": 111680, - "21": 114755, - "22": 117910, - "23": 121150, - "24": 124480, - "25": 127905, - "26": 131425, - "27": 135035, - "28": 138750, - "29": 142565, - "30": 146485, - "31": 150515, - "32": 154655, - "33": 158910, - "34": 163275, - "35": 167770, - "36": 172380, - "37": 177120, - "38": 181995, - "39": 186995, - "40": 192140, - "41": 197425, - "42": 202855, - "43": 208430, - "44": 214165, - "45": 220055, - "46": 226105, - "47": 232320, - "48": 238710, - "49": 245275, - "50": 252020, - "51": 258950, - "52": 266070, - "53": 273390, - "54": 280905, - "55": 288630, - "56": 296570, - "57": 304725, - "58": 313105, - "59": 321715, - "60": 330565, - "61": 339655, - "62": 348995, - "63": 358590, - "64": 368450, - "65": 378585, - "66": 388995, - "67": 399695, - "68": 410685, - "69": 421980, - "70": 433585, - "71": 445505, - "72": 457760, - "73": 470345, - "74": 483280, - "75": 496570, - "76": 510225, - "77": 524260, - "78": 538675, - "79": 553490, - "80": 568710, - "81": 584350, - "82": 600420, - "83": 616930, - "84": 633895, - "85": 651330, - "86": 669240, - "87": 687645, - "88": 706555, - "89": 725985, - "90": 745950, - "91": 766460, - "92": 787540, - "93": 809195, - "94": 831450, - "95": 854315, - "96": 877810, - "97": 901950, - "98": 926750, - "99": 952235, - "100": 1000000 - }, - "clay": { - "1": 69050, - "2": 70950, - "3": 72900, - "4": 74905, - "5": 76965, - "6": 79080, - "7": 81255, - "8": 83490, - "9": 85785, - "10": 88145, - "11": 90570, - "12": 93060, - "13": 95620, - "14": 98250, - "15": 100950, - "16": 103725, - "17": 106580, - "18": 109510, - "19": 112520, - "20": 115615, - "21": 118795, - "22": 122060, - "23": 125420, - "24": 128870, - "25": 132410, - "26": 136055, - "27": 139795, - "28": 143640, - "29": 147590, - "30": 151650, - "31": 155820, - "32": 160105, - "33": 164505, - "34": 169030, - "35": 173680, - "36": 178455, - "37": 183360, - "38": 188405, - "39": 193585, - "40": 198910, - "41": 204380, - "42": 210000, - "43": 215775, - "44": 221710, - "45": 227805, - "46": 234070, - "47": 240505, - "48": 247120, - "49": 253915, - "50": 260900, - "51": 268075, - "52": 275445, - "53": 283020, - "54": 290805, - "55": 298800, - "56": 307020, - "57": 315460, - "58": 324135, - "59": 333050, - "60": 342210, - "61": 351620, - "62": 361290, - "63": 371225, - "64": 381435, - "65": 391925, - "66": 402700, - "67": 413775, - "68": 425155, - "69": 436845, - "70": 448860, - "71": 461205, - "72": 473885, - "73": 486920, - "74": 500310, - "75": 514065, - "76": 528205, - "77": 542730, - "78": 557655, - "79": 572990, - "80": 588745, - "81": 604935, - "82": 621575, - "83": 638665, - "84": 656230, - "85": 674275, - "86": 692820, - "87": 711870, - "88": 731445, - "89": 751560, - "90": 772230, - "91": 793465, - "92": 815285, - "93": 837705, - "94": 860745, - "95": 884415, - "96": 908735, - "97": 933725, - "98": 959405, - "99": 985785, - "100": 1000000 - }, - "iron": { - "1": 72200, - "2": 74185, - "3": 76225, - "4": 78320, - "5": 80475, - "6": 82690, - "7": 84965, - "8": 87300, - "9": 89700, - "10": 92165, - "11": 94700, - "12": 97305, - "13": 99980, - "14": 102730, - "15": 105555, - "16": 108460, - "17": 111440, - "18": 114505, - "19": 117655, - "20": 120890, - "21": 124215, - "22": 127630, - "23": 131140, - "24": 134745, - "25": 138455, - "26": 142260, - "27": 146170, - "28": 150190, - "29": 154320, - "30": 158565, - "31": 162925, - "32": 167405, - "33": 172010, - "34": 176740, - "35": 181600, - "36": 186595, - "37": 191725, - "38": 197000, - "39": 202415, - "40": 207985, - "41": 213705, - "42": 219580, - "43": 225620, - "44": 231825, - "45": 238200, - "46": 244750, - "47": 251480, - "48": 258395, - "49": 265500, - "50": 272800, - "51": 280305, - "52": 288010, - "53": 295930, - "54": 304070, - "55": 312430, - "56": 321025, - "57": 329850, - "58": 338925, - "59": 348245, - "60": 357820, - "61": 367660, - "62": 377770, - "63": 388160, - "64": 398835, - "65": 409800, - "66": 421070, - "67": 432650, - "68": 444550, - "69": 456775, - "70": 469335, - "71": 482240, - "72": 495505, - "73": 509130, - "74": 523130, - "75": 537520, - "76": 552300, - "77": 567490, - "78": 583095, - "79": 599130, - "80": 615605, - "81": 632535, - "82": 649930, - "83": 667800, - "84": 686165, - "85": 705035, - "86": 724425, - "87": 744345, - "88": 764815, - "89": 785850, - "90": 807460, - "91": 829665, - "92": 852480, - "93": 875920, - "94": 900010, - "95": 924760, - "96": 950190, - "97": 976320, - "98": 1000000, - "99": 1000000, - "100": 1000000 - }, - "wheat": { - "1": 13200, - "2": 13565, - "3": 13935, - "4": 14320, - "5": 14715, - "6": 15120, - "7": 15535, - "8": 15960, - "9": 16400, - "10": 16850, - "11": 17315, - "12": 17790, - "13": 18280, - "14": 18780, - "15": 19300, - "16": 19830, - "17": 20375, - "18": 20935, - "19": 21510, - "20": 22100, - "21": 22710, - "22": 23335, - "23": 23975, - "24": 24635, - "25": 25315, - "26": 26010, - "27": 26725, - "28": 27460, - "29": 28215, - "30": 28990, - "31": 29785, - "32": 30605, - "33": 31450, - "34": 32315, - "35": 33200, - "36": 34115, - "37": 35055, - "38": 36015, - "39": 37005, - "40": 38025, - "41": 39070, - "42": 40145, - "43": 41250, - "44": 42385, - "45": 43550, - "46": 44745, - "47": 45975, - "48": 47240, - "49": 48540, - "50": 49875, - "51": 51245, - "52": 52655, - "53": 54105, - "54": 55590, - "55": 57120, - "56": 58690, - "57": 60305, - "58": 61965, - "59": 63670, - "60": 65420, - "61": 67220, - "62": 69065, - "63": 70965, - "64": 72915, - "65": 74920, - "66": 76985, - "67": 79100, - "68": 81275, - "69": 83510, - "70": 85805, - "71": 88165, - "72": 90590, - "73": 93080, - "74": 95640, - "75": 98270, - "76": 100975, - "77": 103750, - "78": 106605, - "79": 109535, - "80": 112550, - "81": 115645, - "82": 118825, - "83": 122090, - "84": 125450, - "85": 128900, - "86": 132445, - "87": 136085, - "88": 139830, - "89": 143675, - "90": 147625, - "91": 151685, - "92": 155855, - "93": 160140, - "94": 164545, - "95": 169070, - "96": 173720, - "97": 178495, - "98": 183405, - "99": 188450, - "100": 193630 - }, - "totalResourceCostPerLevel": { - "1": 221150, - "2": 227235, - "3": 233480, - "4": 239900, - "5": 246500, - "6": 253280, - "7": 260245, - "8": 267400, - "9": 274750, - "10": 282305, - "11": 290070, - "12": 298050, - "13": 306245, - "14": 314665, - "15": 323320, - "16": 332210, - "17": 341345, - "18": 350735, - "19": 360375, - "20": 370285, - "21": 380475, - "22": 390935, - "23": 401685, - "24": 412730, - "25": 424085, - "26": 435750, - "27": 447725, - "28": 460040, - "29": 472690, - "30": 485690, - "31": 499045, - "32": 512770, - "33": 526875, - "34": 541360, - "35": 556250, - "36": 571545, - "37": 587260, - "38": 603415, - "39": 620000, - "40": 637060, - "41": 654580, - "42": 672580, - "43": 691075, - "44": 710085, - "45": 729610, - "46": 749670, - "47": 770280, - "48": 791465, - "49": 813230, - "50": 835595, - "51": 858575, - "52": 882180, - "53": 906445, - "54": 931370, - "55": 956980, - "56": 983305, - "57": 1010340, - "58": 1038130, - "59": 1066680, - "60": 1096015, - "61": 1126155, - "62": 1157120, - "63": 1188940, - "64": 1221635, - "65": 1255230, - "66": 1289750, - "67": 1325220, - "68": 1361665, - "69": 1399110, - "70": 1437585, - "71": 1477115, - "72": 1517740, - "73": 1559475, - "74": 1602360, - "75": 1646425, - "76": 1691705, - "77": 1738230, - "78": 1786030, - "79": 1835145, - "80": 1885610, - "81": 1937465, - "82": 1990750, - "83": 2045485, - "84": 2101740, - "85": 2159540, - "86": 2218930, - "87": 2279945, - "88": 2342645, - "89": 2407070, - "90": 2473265, - "91": 2541275, - "92": 2611160, - "93": 2682960, - "94": 2756750, - "95": 2832560, - "96": 2910455, - "97": 2990490, - "98": 3069560, - "99": 3126470, - "100": 3193630 - } - } -] diff --git a/src/assets/achievements.json b/src/assets/achievements.json deleted file mode 100644 index fe51488..0000000 --- a/src/assets/achievements.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/src/assets/achievements.ts b/src/assets/achievements.ts new file mode 100644 index 0000000..da3b84f --- /dev/null +++ b/src/assets/achievements.ts @@ -0,0 +1,3 @@ +import { Achievement } from 'interfaces/models/game/achievement'; + +export const achievements: Achievement[] = []; diff --git a/src/assets/buildings.json b/src/assets/buildings.json index 681ee49..4ed2c16 100644 --- a/src/assets/buildings.json +++ b/src/assets/buildings.json @@ -1,7 +1,6 @@ [ { "id": "BAKERY", - "maxLevel": 5, "buildingDuration": [ 3680, 6720, @@ -79,18 +78,10 @@ 5185, 9330, 16795 - ], - "totalResourceCost": [ - 5150, - 9270, - 16690, - 30035, - 54060 ] }, { "id": "BRICKYARD", - "maxLevel": 5, "buildingDuration": [ 2840, 5460, @@ -163,18 +154,10 @@ 160, 290, 525 - ], - "totalResourceCost": [ - 1290, - 2320, - 4175, - 7520, - 13545 ] }, { "id": "CLAY_PIT", - "maxLevel": 22, "buildingDuration": [ 220, 550, @@ -371,35 +354,10 @@ 852280, 1423310, 2376925 - ], - "totalResourceCost": [ - 250, - 420, - 700, - 1170, - 1940, - 3250, - 5425, - 9060, - 15125, - 25250, - 42185, - 70440, - 117635, - 196450, - 328075, - 547875, - 914970, - 1527990, - 2551740, - 4261405, - 7116550, - 11884635 ] }, { "id": "CROPLAND", - "maxLevel": 22, "buildingDuration": [ 150, 440, @@ -591,35 +549,10 @@ 340915, 569325, 950770 - ], - "totalResourceCost": [ - 250, - 415, - 695, - 1165, - 1945, - 3250, - 5425, - 9055, - 15125, - 25255, - 42180, - 70445, - 117640, - 196445, - 328070, - 547880, - 914960, - 1527985, - 2551735, - 4261410, - 7116555, - 11884640 ] }, { "id": "GRAIN_MILL", - "maxLevel": 5, "buildingDuration": [ 1840, 3960, @@ -687,18 +620,10 @@ 4020, 7230, 13015 - ], - "totalResourceCost": [ - 2560, - 4605, - 8295, - 14925, - 26875 ] }, { "id": "GRANARY", - "maxLevel": 20, "buildingDuration": [ 1600, 2160, @@ -886,33 +811,10 @@ 1330, 1700, 2180 - ], - "totalResourceCost": [ - 270, - 345, - 445, - 565, - 730, - 930, - 1190, - 1525, - 1945, - 2490, - 3185, - 4080, - 5220, - 6685, - 8560, - 10950, - 14020, - 17940, - 22965, - 29400 ] }, { "id": "GREAT_GRANARY", - "maxLevel": 20, "buildingDuration": [ 7000, 8420, @@ -1100,33 +1002,10 @@ 6645, 8505, 10890 - ], - "totalResourceCost": [ - 1350, - 1730, - 2215, - 2835, - 3625, - 4645, - 5940, - 7600, - 9725, - 12450, - 15935, - 20400, - 26110, - 33425, - 42780, - 54760, - 70095, - 89720, - 114845, - 147000 ] }, { "id": "GREAT_WAREHOUSE", - "maxLevel": 20, "buildingDuration": [ 9000, 10740, @@ -1314,33 +1193,10 @@ 13290, 17015, 21780 - ], - "totalResourceCost": [ - 2100, - 2685, - 3440, - 4410, - 5635, - 7215, - 9240, - 11825, - 15135, - 19370, - 24795, - 31735, - 40625, - 51990, - 66555, - 85185, - 109040, - 139570, - 178645, - 228670 ] }, { "id": "IRON_FOUNDRY", - "maxLevel": 5, "buildingDuration": [ 4080, 7320, @@ -1413,18 +1269,10 @@ 390, 700, 1260 - ], - "totalResourceCost": [ - 1280, - 2305, - 4150, - 7465, - 13440 ] }, { "id": "IRON_MINE", - "maxLevel": 22, "buildingDuration": [ 450, 920, @@ -1621,35 +1469,10 @@ 1022740, 1707970, 2852315 - ], - "totalResourceCost": [ - 270, - 450, - 755, - 1260, - 2100, - 3510, - 5855, - 9785, - 16335, - 27275, - 45555, - 76075, - 127045, - 212170, - 354325, - 591710, - 988160, - 1650225, - 2755880, - 4602325, - 7685870, - 12835410 ] }, { "id": "SAWMILL", - "maxLevel": 5, "buildingDuration": [ 3000, 5700, @@ -1722,18 +1545,10 @@ 290, 525, 945 - ], - "totalResourceCost": [ - 1280, - 2300, - 4145, - 7465, - 13440 ] }, { "id": "WAREHOUSE", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -1921,33 +1736,10 @@ 2660, 3405, 4355 - ], - "totalResourceCost": [ - 420, - 535, - 685, - 885, - 1125, - 1440, - 1845, - 2360, - 3030, - 3875, - 4960, - 6350, - 8125, - 10400, - 13310, - 17040, - 21810, - 27915, - 35730, - 45730 ] }, { "id": "WATERWORKS", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -2139,33 +1931,10 @@ 33505, 43890, 57495 - ], - "totalResourceCost": [ - 3105, - 4065, - 5325, - 6980, - 9145, - 11975, - 15695, - 20555, - 26925, - 35280, - 46215, - 60545, - 79310, - 103895, - 136105, - 178300, - 233560, - 305965, - 400815, - 525070 ] }, { "id": "WOODCUTTER", - "maxLevel": 22, "buildingDuration": [ 260, 620, @@ -2362,35 +2131,10 @@ 1022740, 1707970, 2852315 - ], - "totalResourceCost": [ - 250, - 415, - 695, - 1165, - 1945, - 3250, - 5425, - 9060, - 15125, - 25255, - 42180, - 70440, - 117630, - 196450, - 328075, - 547880, - 914965, - 1527990, - 2551745, - 4261410, - 7116550, - 11884635 ] }, { "id": "ACADEMY", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -2457,6 +2201,7 @@ 4, 4 ], + "effects": [], "buildingRequirements": [ { "requirementType": "building", @@ -2556,33 +2301,10 @@ 2660, 3405, 4355 - ], - "totalResourceCost": [ - 510, - 650, - 830, - 1070, - 1365, - 1750, - 2245, - 2870, - 3680, - 4705, - 6020, - 7710, - 9865, - 12625, - 16160, - 20690, - 26485, - 33895, - 43385, - 55530 ] }, { "id": "SMITHY", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -2775,33 +2497,10 @@ 10635, 13610, 17420 - ], - "totalResourceCost": [ - 1090, - 1395, - 1785, - 2285, - 2925, - 3750, - 4795, - 6135, - 7855, - 10050, - 12870, - 16475, - 21080, - 26985, - 34545, - 44210, - 56595, - 72445, - 92730, - 118690 ] }, { "id": "BARRACKS", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -2994,33 +2693,10 @@ 7975, 10210, 13065 - ], - "totalResourceCost": [ - 730, - 940, - 1195, - 1530, - 1960, - 2505, - 3215, - 4110, - 5265, - 6730, - 8620, - 11035, - 14120, - 18070, - 23135, - 29615, - 37905, - 48515, - 62105, - 79485 ] }, { "id": "CITY_WALL", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -3126,9 +2802,9 @@ 1.47, 1.51, 1.56, - 1.60, + 1.6, 1.65, - 1.70, + 1.7, 1.75, 1.81 ] @@ -3227,33 +2903,10 @@ 4650, 5955, 7620 - ], - "totalResourceCost": [ - 400, - 515, - 655, - 835, - 1075, - 1375, - 1765, - 2250, - 2885, - 3690, - 4720, - 6050, - 7740, - 9910, - 12680, - 16225, - 20770, - 26580, - 34025, - 43550 ] }, { "id": "EARTH_WALL", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -3348,11 +3001,11 @@ 1.04, 1.06, 1.08, - 1.10, + 1.1, 1.13, 1.15, 1.17, - 1.20, + 1.2, 1.22, 1.24, 1.27, @@ -3360,7 +3013,7 @@ 1.32, 1.35, 1.37, - 1.40, + 1.4, 1.43, 1.46, 1.49 @@ -3460,33 +3113,10 @@ 5315, 6805, 8710 - ], - "totalResourceCost": [ - 400, - 510, - 655, - 840, - 1070, - 1370, - 1760, - 2250, - 2880, - 3690, - 4720, - 6045, - 7735, - 9900, - 12680, - 16230, - 20770, - 26580, - 34030, - 43555 ] }, { "id": "GREAT_BARRACKS", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -3678,33 +3308,10 @@ 23925, 30625, 39200 - ], - "totalResourceCost": [ - 2190, - 2805, - 3590, - 4590, - 5875, - 7525, - 9630, - 12325, - 15780, - 20200, - 25860, - 33090, - 42360, - 54225, - 69405, - 88835, - 113710, - 145550, - 186305, - 238470 ] }, { "id": "GREAT_STABLE", - "maxLevel": 20, "buildingDuration": [ 2200, 2850, @@ -3896,33 +3503,10 @@ 19940, 25520, 32665 - ], - "totalResourceCost": [ - 2160, - 2770, - 3540, - 4530, - 5795, - 7425, - 9500, - 12160, - 15560, - 19920, - 25500, - 32640, - 41780, - 53480, - 68450, - 87620, - 112155, - 143560, - 183750, - 235205 ] }, { "id": "HEROS_MANSION", - "maxLevel": 20, "buildingDuration": [ 2300, 2670, @@ -4115,33 +3699,10 @@ 30600, 40695, 54125 - ], - "totalResourceCost": [ - 2310, - 3070, - 4090, - 5430, - 7225, - 9620, - 12790, - 17005, - 22620, - 30080, - 40010, - 53215, - 70765, - 94120, - 125185, - 166490, - 221430, - 294510, - 391690, - 520950 ] }, { "id": "HOSPITAL", - "maxLevel": 20, "buildingDuration": [ 3000, 3780, @@ -4334,33 +3895,10 @@ 23925, 30625, 39200 - ], - "totalResourceCost": [ - 1380, - 1770, - 2265, - 2890, - 3700, - 4740, - 6065, - 7765, - 9945, - 12730, - 16295, - 20850, - 26695, - 34170, - 43735, - 55980, - 71655, - 91720, - 117400, - 150270 ] }, { "id": "MAKESHIFT_WALL", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -4462,7 +4000,7 @@ 1.14, 1.16, 1.18, - 1.20, + 1.2, 1.21, 1.23, 1.25, @@ -4567,33 +4105,10 @@ 1995, 2550, 3265 - ], - "totalResourceCost": [ - 200, - 255, - 325, - 425, - 535, - 685, - 875, - 1125, - 1440, - 1845, - 2360, - 3025, - 3865, - 4955, - 6340, - 8115, - 10385, - 13295, - 17015, - 21775 ] }, { "id": "PALISADE", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -4687,7 +4202,7 @@ 1.03, 1.05, 1.08, - 1.10, + 1.1, 1.13, 1.16, 1.19, @@ -4702,7 +4217,7 @@ 1.49, 1.52, 1.56, - 1.60, + 1.6, 1.64 ] } @@ -4800,33 +4315,10 @@ 3990, 5105, 6535 - ], - "totalResourceCost": [ - 400, - 510, - 655, - 840, - 1075, - 1375, - 1760, - 2255, - 2880, - 3690, - 4725, - 6045, - 7735, - 9900, - 12675, - 16225, - 20770, - 26585, - 34025, - 43555 ] }, { "id": "RALLY_POINT", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -5008,33 +4500,10 @@ 4650, 5955, 7620 - ], - "totalResourceCost": [ - 430, - 550, - 700, - 900, - 1155, - 1480, - 1895, - 2420, - 3105, - 3965, - 5080, - 6500, - 8320, - 10650, - 13625, - 17440, - 22330, - 28575, - 36580, - 46820 ] }, { "id": "STABLE", - "maxLevel": 20, "buildingDuration": [ 2200, 2850, @@ -5227,33 +4696,10 @@ 6645, 8505, 10890 - ], - "totalResourceCost": [ - 720, - 925, - 1180, - 1510, - 1935, - 2475, - 3170, - 4060, - 5190, - 6640, - 8500, - 10880, - 13930, - 17820, - 22815, - 29205, - 37385, - 47850, - 61250, - 78400 ] }, { "id": "STONE_WALL", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -5347,7 +4793,7 @@ 1.03, 1.05, 1.08, - 1.10, + 1.1, 1.13, 1.16, 1.19, @@ -5362,7 +4808,7 @@ 1.49, 1.52, 1.56, - 1.60, + 1.6, 1.64 ] } @@ -5460,33 +4906,10 @@ 3990, 5105, 6535 - ], - "totalResourceCost": [ - 400, - 510, - 655, - 835, - 1075, - 1375, - 1765, - 2255, - 2885, - 3690, - 4725, - 6045, - 7740, - 9905, - 12675, - 16225, - 20770, - 26585, - 34030, - 43555 ] }, { "id": "STONEMASON", - "maxLevel": 20, "buildingDuration": [ 2200, 3150, @@ -5678,33 +5101,10 @@ 4650, 5955, 7620 - ], - "totalResourceCost": [ - 480, - 615, - 790, - 1005, - 1290, - 1650, - 2110, - 2705, - 3455, - 4430, - 5665, - 7255, - 9290, - 11890, - 15210, - 19475, - 24925, - 31900, - 40835, - 52265 ] }, { "id": "TOURNAMENT_SQUARE", - "maxLevel": 20, "buildingDuration": [ 3500, 4360, @@ -5892,33 +5292,10 @@ 15950, 20415, 26135 - ], - "totalResourceCost": [ - 5770, - 7385, - 9450, - 12105, - 15490, - 19825, - 25375, - 32480, - 41580, - 53220, - 68125, - 87190, - 111605, - 142855, - 182860, - 234060, - 299590, - 383480, - 490860, - 628300 ] }, { "id": "TRAPPER", - "maxLevel": 20, "buildingDuration": [ 2000, 2320, @@ -6110,33 +5487,10 @@ 5980, 7655, 9800 - ], - "totalResourceCost": [ - 360, - 460, - 585, - 755, - 965, - 1235, - 1585, - 2025, - 2595, - 3320, - 4250, - 5445, - 6960, - 8915, - 11410, - 14605, - 18695, - 23920, - 30625, - 39195 ] }, { "id": "WORKSHOP", - "maxLevel": 20, "buildingDuration": [ 3000, 3780, @@ -6329,33 +5683,10 @@ 21270, 27225, 34845 - ], - "totalResourceCost": [ - 1890, - 2425, - 3100, - 3965, - 5075, - 6490, - 8315, - 10640, - 13620, - 17435, - 22315, - 28555, - 36560, - 46795, - 59900, - 76670, - 98135, - 125610, - 160780, - 205805 ] }, { "id": "BREWERY", - "maxLevel": 20, "buildingDuration": [ 8000, 9880, @@ -6435,7 +5766,7 @@ 1.07, 1.08, 1.09, - 1.10, + 1.1, 1.11, 1.12, 1.13, @@ -6445,7 +5776,7 @@ 1.17, 1.18, 1.19, - 1.20 + 1.2 ] } ], @@ -6552,33 +5883,10 @@ 148375, 183990, 228145 - ], - "totalResourceCost": [ - 11840, - 14680, - 18205, - 22575, - 27990, - 34710, - 43040, - 53370, - 66180, - 82065, - 101760, - 126175, - 156465, - 194010, - 240575, - 298320, - 369910, - 458690, - 568780, - 705285 ] }, { "id": "COMMAND_CENTER", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -6770,33 +6078,10 @@ 5875, 7170, 8745 - ], - "totalResourceCost": [ - 4100, - 5000, - 6105, - 7445, - 9085, - 11085, - 13515, - 16495, - 20120, - 24545, - 29945, - 36535, - 44575, - 54385, - 66345, - 80950, - 98745, - 120475, - 146980, - 179315 ] }, { "id": "CRANNY", - "maxLevel": 10, "buildingDuration": [ 300, 650, @@ -6898,23 +6183,10 @@ 55, 70, 90 - ], - "totalResourceCost": [ - 130, - 170, - 210, - 275, - 345, - 445, - 570, - 730, - 935, - 1195 ] }, { "id": "EMBASSY", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -7102,33 +6374,10 @@ 5315, 6805, 8710 - ], - "totalResourceCost": [ - 540, - 685, - 885, - 1135, - 1455, - 1855, - 2370, - 3040, - 3885, - 4985, - 6375, - 8160, - 10440, - 13370, - 17115, - 21905, - 28040, - 35890, - 45940, - 58800 ] }, { "id": "HORSE_DRINKING_TROUGH", - "maxLevel": 20, "buildingDuration": [ 2200, 3150, @@ -7208,7 +6457,7 @@ 0.93, 0.92, 0.91, - 0.90, + 0.9, 0.89, 0.88, 0.86, @@ -7217,7 +6466,7 @@ 0.83, 0.82, 0.81, - 0.80, + 0.8, 0.78, 0.77, 0.75 @@ -7235,7 +6484,7 @@ 0.93, 0.92, 0.91, - 0.90, + 0.9, 0.89, 0.88, 0.86, @@ -7244,7 +6493,7 @@ 0.83, 0.82, 0.81, - 0.80, + 0.8, 0.78, 0.77, 0.75 @@ -7354,33 +6603,10 @@ 35890, 45940, 58800 - ], - "totalResourceCost": [ - 2400, - 3075, - 3935, - 5030, - 6440, - 8250, - 10555, - 13510, - 17290, - 22135, - 28335, - 36265, - 46420, - 59420, - 76060, - 97355, - 124620, - 159510, - 204170, - 261340 ] }, { "id": "MAIN_BUILDING", - "maxLevel": 20, "buildingDuration": [ 2500, 2620, @@ -7562,33 +6788,10 @@ 1330, 1700, 2180 - ], - "totalResourceCost": [ - 190, - 240, - 315, - 395, - 510, - 650, - 840, - 1075, - 1370, - 1755, - 2240, - 2870, - 3675, - 4705, - 6025, - 7710, - 9865, - 12630, - 16165, - 20690 ] }, { "id": "MARKETPLACE", - "maxLevel": 20, "buildingDuration": [ 1800, 2390, @@ -7786,33 +6989,10 @@ 4650, 5955, 7620 - ], - "totalResourceCost": [ - 340, - 435, - 555, - 710, - 915, - 1165, - 1500, - 1915, - 2450, - 3135, - 4010, - 5145, - 6575, - 8420, - 10780, - 13795, - 17655, - 22590, - 28925, - 37015 ] }, { "id": "PALACE", - "maxLevel": 20, "buildingDuration": [ 5000, 6100, @@ -8005,33 +7185,10 @@ 16615, 21270, 27225 - ], - "totalResourceCost": [ - 2350, - 3010, - 3850, - 4935, - 6305, - 8075, - 10340, - 13225, - 16935, - 21680, - 27745, - 35515, - 45455, - 58180, - 74480, - 95325, - 122020, - 156185, - 199920, - 255895 ] }, { "id": "RESIDENCE", - "maxLevel": 20, "buildingDuration": [ 2000, 2620, @@ -8219,33 +7376,10 @@ 11965, 15315, 19600 - ], - "totalResourceCost": [ - 1570, - 2010, - 2575, - 3290, - 4215, - 5400, - 6905, - 8840, - 11310, - 14485, - 18530, - 23725, - 30370, - 38870, - 49755, - 63690, - 81520, - 104345, - 133560, - 170955 ] }, { "id": "TOWN_HALL", - "maxLevel": 20, "buildingDuration": [ 12500, 14800, @@ -8312,6 +7446,7 @@ 4, 4 ], + "effects": [], "buildingRequirements": [ { "requirementType": "building", @@ -8411,33 +7546,10 @@ 39875, 51040, 65335 - ], - "totalResourceCost": [ - 4220, - 5405, - 6920, - 8850, - 11325, - 14500, - 18560, - 23760, - 30410, - 38925, - 49820, - 63770, - 81625, - 104480, - 133735, - 171180, - 219120, - 280460, - 359000, - 459520 ] }, { "id": "TRADE_OFFICE", - "maxLevel": 20, "buildingDuration": [ 3000, 3780, @@ -8506,7 +7618,7 @@ ], "effects": [ { - "effectId": "merchantAmountBonus", + "effectId": "merchantCapacityBonus", "valuesPerLevel": [ 120, 140, @@ -8630,33 +7742,10 @@ 26585, 34030, 43555 - ], - "totalResourceCost": [ - 4330, - 5535, - 7095, - 9080, - 11625, - 14880, - 19045, - 24370, - 31200, - 39940, - 51115, - 65435, - 83750, - 107205, - 137225, - 175645, - 224830, - 287780, - 368360, - 471495 ] }, { "id": "TREASURY", - "maxLevel": 20, "buildingDuration": [ 8000, 9580, @@ -8723,6 +7812,7 @@ 4, 4 ], + "effects": [], "buildingRequirements": [ { "requirementType": "building", @@ -8817,849 +7907,6 @@ 50340, 63430, 79925 - ], - "totalResourceCost": [ - 9190, - 11575, - 14585, - 18380, - 23165, - 29185, - 36775, - 46335, - 58380, - 73560, - 92685, - 116785, - 147150, - 185410, - 233615, - 294355, - 370890, - 467315, - 588825, - 741920 - ] - }, - { - "id": "WONDER_OF_THE_WORLD", - "maxLevel": 100, - "buildingDuration": [ - 9000, - 9430, - 9860, - 10300, - 10740, - 11190, - 11650, - 12110, - 12580, - 13060, - 13540, - 14030, - 14520, - 15030, - 15540, - 16060, - 16580, - 17110, - 17650, - 18200, - 18750, - 19320, - 19890, - 20470, - 21050, - 21650, - 22250, - 22860, - 23480, - 24110, - 24750, - 25390, - 26050, - 26710, - 27390, - 28070, - 28760, - 29470, - 30180, - 30900, - 31640, - 32380, - 33130, - 33900, - 34670, - 35460, - 36250, - 37060, - 37880, - 38710, - 39550, - 40400, - 41270, - 42150, - 43040, - 43940, - 44860, - 45780, - 46720, - 47680, - 48650, - 49630, - 50620, - 51630, - 52650, - 53690, - 54740, - 55810, - 56890, - 57990, - 59100, - 60230, - 61370, - 62530, - 63700, - 64890, - 66100, - 67330, - 68570, - 69830, - 71110, - 72400, - 73720, - 75050, - 76400, - 77770, - 79160, - 80570, - 82000, - 83440, - 84910, - 86400, - 87910, - 89440, - 90990, - 92570, - 94160, - 95780, - 97420, - 99090 - ], - "culturePointsProduction": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "cropConsumption": [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 5, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 6, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10 - ], - "buildingRequirements": [], - "wood": [ - 66700, - 68535, - 70420, - 72355, - 74345, - 76390, - 78490, - 80650, - 82865, - 85145, - 87485, - 89895, - 92365, - 94905, - 97515, - 100195, - 102950, - 105785, - 108690, - 111680, - 114755, - 117910, - 121150, - 124480, - 127905, - 131425, - 135035, - 138750, - 142565, - 146485, - 150515, - 154655, - 158910, - 163275, - 167770, - 172380, - 177120, - 181995, - 186995, - 192140, - 197425, - 202855, - 208430, - 214165, - 220055, - 226105, - 232320, - 238710, - 245275, - 252020, - 258950, - 266070, - 273390, - 280905, - 288630, - 296570, - 304725, - 313105, - 321715, - 330565, - 339655, - 348995, - 358590, - 368450, - 378585, - 388995, - 399695, - 410685, - 421980, - 433585, - 445505, - 457760, - 470345, - 483280, - 496570, - 510225, - 524260, - 538675, - 553490, - 568710, - 584350, - 600420, - 616930, - 633895, - 651330, - 669240, - 687645, - 706555, - 725985, - 745950, - 766460, - 787540, - 809195, - 831450, - 854315, - 877810, - 901950, - 926750, - 952235, - 1000000 - ], - "clay": [ - 69050, - 70950, - 72900, - 74905, - 76965, - 79080, - 81255, - 83490, - 85785, - 88145, - 90570, - 93060, - 95620, - 98250, - 100950, - 103725, - 106580, - 109510, - 112520, - 115615, - 118795, - 122060, - 125420, - 128870, - 132410, - 136055, - 139795, - 143640, - 147590, - 151650, - 155820, - 160105, - 164505, - 169030, - 173680, - 178455, - 183360, - 188405, - 193585, - 198910, - 204380, - 210000, - 215775, - 221710, - 227805, - 234070, - 240505, - 247120, - 253915, - 260900, - 268075, - 275445, - 283020, - 290805, - 298800, - 307020, - 315460, - 324135, - 333050, - 342210, - 351620, - 361290, - 371225, - 381435, - 391925, - 402700, - 413775, - 425155, - 436845, - 448860, - 461205, - 473885, - 486920, - 500310, - 514065, - 528205, - 542730, - 557655, - 572990, - 588745, - 604935, - 621575, - 638665, - 656230, - 674275, - 692820, - 711870, - 731445, - 751560, - 772230, - 793465, - 815285, - 837705, - 860745, - 884415, - 908735, - 933725, - 959405, - 985785, - 1000000 - ], - "iron": [ - 72200, - 74185, - 76225, - 78320, - 80475, - 82690, - 84965, - 87300, - 89700, - 92165, - 94700, - 97305, - 99980, - 102730, - 105555, - 108460, - 111440, - 114505, - 117655, - 120890, - 124215, - 127630, - 131140, - 134745, - 138455, - 142260, - 146170, - 150190, - 154320, - 158565, - 162925, - 167405, - 172010, - 176740, - 181600, - 186595, - 191725, - 197000, - 202415, - 207985, - 213705, - 219580, - 225620, - 231825, - 238200, - 244750, - 251480, - 258395, - 265500, - 272800, - 280305, - 288010, - 295930, - 304070, - 312430, - 321025, - 329850, - 338925, - 348245, - 357820, - 367660, - 377770, - 388160, - 398835, - 409800, - 421070, - 432650, - 444550, - 456775, - 469335, - 482240, - 495505, - 509130, - 523130, - 537520, - 552300, - 567490, - 583095, - 599130, - 615605, - 632535, - 649930, - 667800, - 686165, - 705035, - 724425, - 744345, - 764815, - 785850, - 807460, - 829665, - 852480, - 875920, - 900010, - 924760, - 950190, - 976320, - 1000000, - 1000000, - 1000000 - ], - "wheat": [ - 13200, - 13565, - 13935, - 14320, - 14715, - 15120, - 15535, - 15960, - 16400, - 16850, - 17315, - 17790, - 18280, - 18780, - 19300, - 19830, - 20375, - 20935, - 21510, - 22100, - 22710, - 23335, - 23975, - 24635, - 25315, - 26010, - 26725, - 27460, - 28215, - 28990, - 29785, - 30605, - 31450, - 32315, - 33200, - 34115, - 35055, - 36015, - 37005, - 38025, - 39070, - 40145, - 41250, - 42385, - 43550, - 44745, - 45975, - 47240, - 48540, - 49875, - 51245, - 52655, - 54105, - 55590, - 57120, - 58690, - 60305, - 61965, - 63670, - 65420, - 67220, - 69065, - 70965, - 72915, - 74920, - 76985, - 79100, - 81275, - 83510, - 85805, - 88165, - 90590, - 93080, - 95640, - 98270, - 100975, - 103750, - 106605, - 109535, - 112550, - 115645, - 118825, - 122090, - 125450, - 128900, - 132445, - 136085, - 139830, - 143675, - 147625, - 151685, - 155855, - 160140, - 164545, - 169070, - 173720, - 178495, - 183405, - 188450, - 193630 - ], - "totalResourceCost": [ - 221150, - 227235, - 233480, - 239900, - 246500, - 253280, - 260245, - 267400, - 274750, - 282305, - 290070, - 298050, - 306245, - 314665, - 323320, - 332210, - 341345, - 350735, - 360375, - 370285, - 380475, - 390935, - 401685, - 412730, - 424085, - 435750, - 447725, - 460040, - 472690, - 485690, - 499045, - 512770, - 526875, - 541360, - 556250, - 571545, - 587260, - 603415, - 620000, - 637060, - 654580, - 672580, - 691075, - 710085, - 729610, - 749670, - 770280, - 791465, - 813230, - 835595, - 858575, - 882180, - 906445, - 931370, - 956980, - 983305, - 1010340, - 1038130, - 1066680, - 1096015, - 1126155, - 1157120, - 1188940, - 1221635, - 1255230, - 1289750, - 1325220, - 1361665, - 1399110, - 1437585, - 1477115, - 1517740, - 1559475, - 1602360, - 1646425, - 1691705, - 1738230, - 1786030, - 1835145, - 1885610, - 1937465, - 1990750, - 2045485, - 2101740, - 2159540, - 2218930, - 2279945, - 2342645, - 2407070, - 2473265, - 2541275, - 2611160, - 2682960, - 2756750, - 2832560, - 2910455, - 2990490, - 3069560, - 3126470, - 3193630 ] } ] diff --git a/src/assets/effects.ts b/src/assets/effects.ts new file mode 100644 index 0000000..82102ec --- /dev/null +++ b/src/assets/effects.ts @@ -0,0 +1,14 @@ +import { Effect } from 'interfaces/models/game/effect'; + +export const globalEffects: Effect[] = [ + +]; + +export const villageEffects: Effect[] = [ + +]; + +export const effects: Effect[] = [ + ...villageEffects, + ...globalEffects +]; diff --git a/src/assets/quests.json b/src/assets/quests.json deleted file mode 100644 index fe51488..0000000 --- a/src/assets/quests.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/src/assets/quests.ts b/src/assets/quests.ts new file mode 100644 index 0000000..24dd4ee --- /dev/null +++ b/src/assets/quests.ts @@ -0,0 +1,14 @@ +import { Quest } from 'interfaces/models/game/quest'; + +export const globalQuests: Quest[] = [ + +]; + +export const villageQuests: Quest[] = [ + +]; + +export const quests: Quest[] = [ + ...villageQuests, + ...globalQuests, +]; diff --git a/src/assets/village-presets.ts b/src/assets/village-presets.ts new file mode 100644 index 0000000..ad0b28f --- /dev/null +++ b/src/assets/village-presets.ts @@ -0,0 +1,19 @@ +import { VillageBuildingFieldsPreset } from 'interfaces/models/game/village'; + +export const villagePresets: VillageBuildingFieldsPreset[] = [ + { + "preset": "new-village", + "buildingFields": [ + { + "buildingId": "MAIN_BUILDING", + "buildingFieldId": "1", + "level": 1 + }, + { + "buildingId": "RALLY_POINT", + "buildingFieldId": "2", + "level": 1 + } + ] + } +]; diff --git a/src/constants/breakpoints.ts b/src/constants/breakpoints.ts deleted file mode 100644 index 6321b64..0000000 --- a/src/constants/breakpoints.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const breakpoints = { - xs: 425, - sm: 640, - md: 768, - lg: 1024, - xl: 1280, - '2xl': 1536 -}; diff --git a/src/constants/defaults.ts b/src/constants/defaults.ts deleted file mode 100644 index 5e25082..0000000 --- a/src/constants/defaults.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { BuildingFields, ResourceFields } from 'interfaces/models/game/village'; -import { Resources } from 'interfaces/models/game/resource'; - -export const defaultResources: Resources = { - wood: 0, - clay: 0, - iron: 0, - wheat: 0 -}; - -export const defaultResourceFields: ResourceFields = { - 1: null, - 2: null, - 3: null, - 4: null, - 5: null, - 6: null, - 7: null, - 8: null, - 9: null, - 10: null, - 11: null, - 12: null, - 13: null, - 14: null, - 15: null, - 16: null, - 17: null, - 18: null -}; - -export const defaultBuildingFields: BuildingFields = { - 1: null, - 2: null, - 3: null, - 4: null, - 5: null, - 6: null, - 7: null, - 8: null, - 9: null, - 10: null, - 11: null, - 12: null, - 13: null, - 14: null, - 15: null, - 16: null, - 17: null, - 18: null, - 19: null, - 20: null -}; diff --git a/src/constants/effects.ts b/src/constants/effects.ts deleted file mode 100644 index aed76c2..0000000 --- a/src/constants/effects.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { Effect } from 'interfaces/models/game/effect'; - -// These effects effect all villages and multiply with village-specific effects -export const accountEffects: Partial = { - heroSpeedBonus: 1, - heroResourceProductionBonus: 1, - heroStrengthBonus: 1, - heroCarryCapacityBonus: 1, - heroMountedUnitSpeedBonus: 1, - barracksTrainingDuration: 1, - greatBarracksTrainingDuration: 1, - stableTrainingDuration: 1, - greatStableTrainingDuration: 1, - workshopTrainingDuration: 1, - hospitalTrainingDuration: 1, - unitSpeedBonus: 1, - buildingDuration: 1, - woodProductionBonus: 1, - clayProductionBonus: 1, - ironProductionBonus: 1, - wheatProductionBonus: 1, - cropConsumption: 1, - culturePointsProduction: 1, - culturePointsProductionBonus: 1, - crannyCapacityBonus: 1, - amountOfUnlockedUnitResearchLevels: 1 -}; - -// These effects only apply to specific village -export const newVillageEffects: Partial = { - barracksTrainingDuration: 1, - greatBarracksTrainingDuration: 1, - stableTrainingDuration: 1, - greatStableTrainingDuration: 1, - workshopTrainingDuration: 1, - hospitalTrainingDuration: 1, - unitSpeedBonus: 1, - unitSpeedAfter20TilesBonus: 1, - amountOfUncoveredAttackingUnits: 1, - villageDefenceValue: 1, - villageDefenceBonus: 1, - buildingDurabilityBonus: 1, - buildingDuration: 1, - woodProduction: 1, - clayProduction: 1, - ironProduction: 1, - wheatProduction: 1, - woodProductionBonus: 1, - clayProductionBonus: 1, - ironProductionBonus: 1, - wheatProductionBonus: 1, - oasisProductionBonus: 1, - woodOasisProductionBonus: 1, - clayOasisProductionBonus: 1, - ironOasisProductionBonus: 1, - wheatOasisProductionBonus: 1, - oasisExpansionSlot: 1, - cropConsumption: 1, - culturePointsProduction: 1, - culturePointsProductionBonus: 1, - trapperCapacity: 1, - crannyCapacity: 1, - crannyCapacityBonus: 1, - breweryAttackBonus: 1, - embassyCapacity: 1, - merchantAmount: 1, - merchantCapacityBonus: 1, - granaryCapacity: 1, - warehouseCapacity: 1 -}; - -export const effects: Effects = { - heroSpeedBonus: 1, - heroResourceProductionBonus: 1, - heroStrengthBonus: 1, - heroCarryCapacityBonus: 1, - heroMountedUnitSpeedBonus: 1, - barracksTrainingDuration: 1, - greatBarracksTrainingDuration: 1, - stableTrainingDuration: 1, - greatStableTrainingDuration: 1, - workshopTrainingDuration: 1, - hospitalTrainingDuration: 1, - unitSpeedBonus: 1, - unitSpeedAfter20TilesBonus: 1, - amountOfUncoveredAttackingUnits: 1, - amountOfUnlockedUnitResearchLevels: 1, - villageDefenceValue: 1, - villageDefenceBonus: 1, - buildingDurabilityBonus: 1, - buildingDuration: 1, - woodProduction: 1, - clayProduction: 1, - ironProduction: 1, - wheatProduction: 1, - woodProductionBonus: 1, - clayProductionBonus: 1, - ironProductionBonus: 1, - wheatProductionBonus: 1, - oasisProductionBonus: 1, - woodOasisProductionBonus: 1, - clayOasisProductionBonus: 1, - ironOasisProductionBonus: 1, - wheatOasisProductionBonus: 1, - oasisExpansionSlot: 1, - cropConsumption: 1, - culturePointsProduction: 1, - culturePointsProductionBonus: 1, - trapperCapacity: 1, - crannyCapacity: 1, - crannyCapacityBonus: 1, - breweryAttackBonus: 1, - embassyCapacity: 1, - merchantAmount: 1, - merchantCapacityBonus: 1, - granaryCapacity: 1, - warehouseCapacity: 1 -}; diff --git a/src/constants/enums/react-query-keys.ts b/src/constants/enums/react-query-keys.ts deleted file mode 100644 index 30bd212..0000000 --- a/src/constants/enums/react-query-keys.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum ServerKeys { - LIST_AVAILABLE_SERVERS = 'server_keys.list_available_servers', - CREATE_SERVER = 'server_keys.create_server', - DELETE_SERVER = 'server_keys.delete_server', -} diff --git a/src/constants/new-village.ts b/src/constants/new-village.ts deleted file mode 100644 index b82181a..0000000 --- a/src/constants/new-village.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { BuildingFields, ResourceFieldLayout, ResourceFieldLayoutByFieldType, Village } from 'interfaces/models/game/village'; -import { v4 as uuidv4 } from 'uuid'; -import { effects } from 'constants/effects'; -import { - createResourceFieldsFromResourceLayout -} from 'utils/game/create-resource-fields-from-resource-layout'; - -const newVillageBuildingFields: BuildingFields = { - 1: { - buildingId: 'MAIN_BUILDING', - level: 1 - }, - 2: { - buildingId: 'RALLY_POINT', - level: 1 - }, - 3: null, - 4: null, - 5: null, - 6: null, - 7: null, - 8: null, - 9: null, - 10: null, - 11: null, - 12: null, - 13: null, - 14: null, - 15: null, - 16: null, - 17: null, - 18: null, - 19: null, - 20: null -}; - -const fullWheatLayout: ResourceFieldLayout = { - 1: 'wheat', - 2: 'wheat', - 3: 'wheat', - 4: 'wheat', - 5: 'wheat', - 6: 'wheat', - 7: 'wheat', - 8: 'wheat', - 9: 'wheat', - 10: 'wheat', - 11: 'wheat', - 12: 'wheat', - 13: 'wheat', - 14: 'wheat', - 15: 'wheat', - 16: 'wheat', - 17: 'wheat', - 18: 'wheat' -}; - -// We set only non-wheat fields, since wheat field is the most common type -const resourceFields: ResourceFieldLayoutByFieldType = { - '00018': fullWheatLayout, - 3339: { - ...fullWheatLayout - // 1: '', - // 1: '', - // 1: '', - // 1: '', - // 1: '', - // 1: '', - // 1: '', - // 1: '', - // 1: '' - }, - 3447: { - ...fullWheatLayout - // 1: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 3456: { - ...fullWheatLayout - // 1: '', - // 3: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 3546: { - ...fullWheatLayout - // 1: '', - // 3: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 4347: { - ...fullWheatLayout - // 1: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 4356: { - ...fullWheatLayout - // 1: '', - // 3: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 4437: { - ...fullWheatLayout - // 1: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 4446: { - ...fullWheatLayout, - 1: 'wood', - 3: 'wood', - 4: 'iron', - 5: 'clay', - 6: 'clay', - 7: 'iron', - 10: 'iron', - 11: 'iron', - 14: 'wood', - 16: 'clay', - 17: 'wood', - 18: 'clay' - }, - 4536: { - ...fullWheatLayout - // 1: '', - // 3: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 5346: { - ...fullWheatLayout - // 1: '', - // 3: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 5436: { - ...fullWheatLayout - // 1: '', - // 3: '', - // 4: '', - // 5: '', - // 6: '', - // 7: '', - // 10: '', - // 11: '', - // 14: '', - // 16: '', - // 17: '', - // 18: '' - }, - 11115: { - ...fullWheatLayout, - 3: 'wood', - 4: 'iron', - 16: 'clay' - } -}; - -// This is default new (4-4-4-6) village. Some properties need to be changed on creation based on the type of village -export const newVillage: Village = { - id: uuidv4(), - name: 'New village', - lastUpdatedAt: Date.now(), - position: { - x: 0, - y: 0 - }, - resources: { - wood: 750, - clay: 750, - iron: 750, - wheat: 750 - }, - storageCapacity: { - wood: 800, - clay: 800, - iron: 800, - wheat: 800 - }, - hourlyProduction: { - wood: 16, - clay: 16, - iron: 16, - wheat: 24 - }, - effects, - resourceFields: createResourceFieldsFromResourceLayout(resourceFields['4446']), - buildingFields: newVillageBuildingFields, - isCapital: true, - quests: [] -}; diff --git a/src/constants/oasis-shapes.ts b/src/constants/oasis-shapes.ts deleted file mode 100644 index 92aeb7e..0000000 --- a/src/constants/oasis-shapes.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { Resource } from 'interfaces/models/game/resource'; - -type Shape = { - group: number; - shape: number[][]; -}; - -export type OasisShapes = Record; - -export const oasisShapes: OasisShapes = { - wheat: { - backgroundColor: '#FFF600', - shapes: [ - { - group: 1, - shape: [ - [0, 1, 1], - [0, 1, 1] - ] - }, - { - group: 2, - shape: [ - [0, 1, 1] - ] - }, - { - group: 3, - shape: [ - [0, 1, 3], - [0, 1, 2], - [1, 1, 2], - [0, 1, 1] - ] - }, - { - group: 4, - shape: [ - [0, 1, 1], - [0, 1, 2], - [0, 1, 2] - ] - }, - { - group: 5, - shape: [ - [0, 1, 1], - [1, 1, 2], - [1, 1, 1], - [1, 1, 0] - ] - }, - { - group: 6, - shape: [ - [0, 1, 0], - [0, 1, 0] - ] - } - // { - // group: 6, - // shape: [ - // [0, 1, 2], - // [0, 1, 2], - // [0, 1, 3], - // [0, 0, 3] - // ] - // }, - ] - }, - iron: { - backgroundColor: '#7B90A1', - shapes: [ - { - group: 1, - shape: [ - [0, 1, 2], - [0, 1, 2] - ] - }, - { - group: 2, - shape: [ - [0, 1, 0], - [0, 1, 1] - ] - }, - { - group: 3, - shape: [ - [0, 1, 1] - ] - }, - { - group: 4, - shape: [ - [0, 1, 1], - [0, 1, 0], - [0, 1, 0] - ] - }, - { - group: 5, - shape: [ - [0, 1, 1], - [0, 1, 2], - [0, 1, 2] - ] - }, - { - group: 8, - shape: [ - [0, 1, 0], - [0, 1, 0] - ] - } - ] - }, - wood: { - backgroundColor: '#426002', - shapes: [ - { - group: 1, - shape: [ - [0, 1, 1] - ] - }, - { - group: 2, - shape: [ - [0, 1, 1], - [0, 0, 1], - [0, 0, 1] - ] - }, - { - group: 3, - shape: [ - [0, 1, 2], - [0, 0, 1] - ] - }, - { - group: 4, - shape: [ - [0, 1, 1], - [0, 1, 0], - [0, 1, 0] - ] - }, - { - group: 5, - shape: [ - [0, 1, 0], - [0, 1, 0] - ] - }, - { - group: 6, - shape: [ - [1, 0, 1], - [1, 1, 1] - ] - } - ] - }, - clay: { - backgroundColor: '#C29760', - shapes: [ - { - group: 1, - shape: [ - [0, 1, 0], - [0, 1, 1] - ] - }, - { - group: 2, - shape: [ - [0, 1, 1], - [0, 0, 1] - ] - }, - { - group: 3, - shape: [ - [0, 1, 1], - [0, 1, 0] - ] - }, - { - group: 4, - shape: [ - [0, 1, 0], - [0, 1, 1], - [0, 0, 1] - ] - }, - { - group: 5, - shape: [ - [0, 1, 1] - ] - } - ] - } -}; diff --git a/src/database/__tests__/database.test.ts b/src/database/__tests__/database.test.ts index d2bdaf7..6dee531 100644 --- a/src/database/__tests__/database.test.ts +++ b/src/database/__tests__/database.test.ts @@ -1,99 +1,18 @@ import "fake-indexeddb/auto"; -import { database } from 'database/database'; +import { CRYLITE_TABLE_NAMES, database } from 'database/database'; +import { CryliteTableName } from 'interfaces/models/database/crylite-table'; describe('Database', () => { test('Database initializes', () => { expect(database).not.toBe(undefined); }); - // TODO: Currently only testing initialization, it should be expanded to test indexes as well - describe('Tables', () => { - describe('servers', () => { - const table = database.servers; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('maps', () => { - const table = database.maps; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('heroes', () => { - const table = database.heroes; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('villages', () => { - const table = database.villages; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('reports', () => { - const table = database.reports; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('quests', () => { - const table = database.quests; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('achievements', () => { - const table = database.achievements; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('events', () => { - const table = database.events; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('effects', () => { - const table = database.effects; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('banks', () => { - const table = database.banks; - - test('Exists', () => { - expect(table).not.toBe(undefined); - }); - }); - - describe('researchLevels', () => { - const table = database.researchLevels; - - test('Exists', () => { + describe('Check for table existence', () => { + Array.from(CRYLITE_TABLE_NAMES).forEach((tableName: CryliteTableName) => { + test(`Table "${tableName}" exists`, () => { + const table = database[tableName]; expect(table).not.toBe(undefined); }); - }); + }) }); }); diff --git a/src/database/database.ts b/src/database/database.ts index b21fb14..455b621 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -11,12 +11,14 @@ import { Effect } from 'interfaces/models/game/effect'; import { Bank } from 'interfaces/models/game/bank'; import { ResearchLevel } from 'interfaces/models/game/research-level'; import { CryliteTableName } from 'interfaces/models/database/crylite-table'; +import { Player } from 'interfaces/models/game/player'; +import { Reputation } from 'interfaces/models/game/reputation'; type TableIndex = string; const DEFAULT_TABLE_INDEX: TableIndex[] = ['++id']; -const CRYLITE_TABLES = new Map([ +export const CRYLITE_TABLES = new Map([ ['servers', ['slug']], ['maps', ['serverId']], ['heroes', ['serverId']], @@ -27,7 +29,9 @@ const CRYLITE_TABLES = new Map([ ['events', ['serverId']], ['effects', ['serverId']], ['banks', ['serverId']], - ['researchLevels', ['serverId']] + ['researchLevels', ['serverId']], + ['players', ['serverId']], + ['reputations', ['serverId']], ]); export const CRYLITE_TABLE_NAMES = CRYLITE_TABLES.keys(); @@ -45,6 +49,8 @@ export class CryliteDatabase extends Dexie { public effects!: Table; public banks!: Table; public researchLevels!: Table; + public players!: Table; + public reputations!: Table; public amountOfTables: number; diff --git a/src/factories/__tests__/map-factory.test.ts b/src/factories/__tests__/map-factory.test.ts index 6b95433..40a51e0 100644 --- a/src/factories/__tests__/map-factory.test.ts +++ b/src/factories/__tests__/map-factory.test.ts @@ -1,8 +1,9 @@ import { serverMock } from 'mocks/models/game/server-mock'; import { OccupiedFreeTile, Tile } from 'interfaces/models/game/tile'; -import { predefinedVillagesCoordinates100x100Mock } from 'mocks/game/map/predefined-villages-coordinates-100x100-mock'; +import { predefinedVillagesCoordinates100x100Mock } from 'mocks/game/map/predefined-villages-coordinates-mock'; import { Point } from 'interfaces/models/common'; import { mapFactory } from 'factories/map-factory'; +import { playersMock } from 'mocks/models/game/player-mock'; const serverMockSize100 = serverMock; // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -53,7 +54,7 @@ const expectToBeCloseTo = (amount: number, expected: number, amountOfTiles: numb describe('Map factory', () => { describe('100x100 map size', () => { - const tiles = mapFactory({ server: serverMockSize100 }); + const tiles = mapFactory({ server: serverMockSize100, players: playersMock }); describe('Grid generation', () => { test('Creates an array of correct size', () => { @@ -85,10 +86,7 @@ describe('Map factory', () => { describe('Village generation', () => { predefinedVillagesTests('Initial user', tiles.find(({ coordinates }) => coordinates.x === 0 && coordinates.y === 0)! as OccupiedFreeTile); - const { - artifactVillagesCoordinates, - quadrantBaseCoordinates - } = predefinedVillagesCoordinates100x100Mock; + const { artifactVillagesCoordinates } = predefinedVillagesCoordinates100x100Mock; artifactVillagesCoordinates.forEach((mockCoordinates: Point) => { const { @@ -97,14 +95,6 @@ describe('Map factory', () => { } = mockCoordinates; predefinedVillagesTests('Artifact', tiles.find(({ coordinates }) => coordinates.x === x && coordinates.y === y)! as OccupiedFreeTile); }); - - quadrantBaseCoordinates.forEach((mockCoordinates: Point) => { - const { - x, - y - } = mockCoordinates; - predefinedVillagesTests('Quadrant base', tiles.find(({ coordinates }) => coordinates.x === x && coordinates.y === y)! as OccupiedFreeTile); - }); }); }); diff --git a/src/factories/__tests__/server-factory.test.ts b/src/factories/__tests__/server-factory.test.ts index d7d2357..962b9ca 100644 --- a/src/factories/__tests__/server-factory.test.ts +++ b/src/factories/__tests__/server-factory.test.ts @@ -1,5 +1,3 @@ -import { serverMock } from 'mocks/models/game/server-mock'; -import { bankFactory } from 'factories/bank-factory'; import { serverFactory } from 'factories/server-factory'; describe('Server factory', () => { diff --git a/src/factories/achievement-factory.ts b/src/factories/achievement-factory.ts index 7bb451c..5a3b276 100644 --- a/src/factories/achievement-factory.ts +++ b/src/factories/achievement-factory.ts @@ -3,13 +3,11 @@ import { Server } from 'interfaces/models/game/server'; type AchievementFactoryProps = { server: Server; -} -export const achievementFactory = ({ server: { id: serverId } }: AchievementFactoryProps): Achievement => { +} & Omit; +export const achievementFactory = ({ server, ...achievement }: AchievementFactoryProps): Achievement => { return { - serverId, - achievementId: '', - isCompleted: false, - achievedAt: null, + serverId: server.id, + ...achievement } }; diff --git a/src/factories/effect-factory.ts b/src/factories/effect-factory.ts new file mode 100644 index 0000000..ce1f486 --- /dev/null +++ b/src/factories/effect-factory.ts @@ -0,0 +1,27 @@ +import { Server } from 'interfaces/models/game/server'; +import { Reputation, ReputationLevel } from 'interfaces/models/game/reputation'; +import { PlayerFaction } from 'interfaces/models/game/player'; +import { Effect } from 'interfaces/models/game/effect'; +import { globalEffects, villageEffects } from 'assets/effects'; +import { Village } from 'interfaces/models/game/village'; + +type EffectFactoryProps = { + server: Server; +}; + +export const effectFactory = ({ server, ...effect }: EffectFactoryProps & Omit): Effect => { + return { + serverId: server.id, + ...effect + }; +}; + +export const newVillageEffectsFactory = ({ server, village }: EffectFactoryProps & { village: Village; }): Effect[] => { + return villageEffects.map((effect) => effectFactory({ server, ...effect})); +}; + +export const globalEffectsFactory = ({ server }: EffectFactoryProps): Effect[] => { + return globalEffects.map((effect) => effectFactory({ server, ...effect})); +}; + + diff --git a/src/factories/map-factory.ts b/src/factories/map-factory.ts index 241099a..fda295f 100644 --- a/src/factories/map-factory.ts +++ b/src/factories/map-factory.ts @@ -1,5 +1,5 @@ -import { ResourceFieldType } from 'interfaces/models/game/village'; -import { seededRandomArrayElement, seededRandomIntFromInterval } from 'utils/common'; +import { ResourceFieldComposition } from 'interfaces/models/game/village'; +import { seededRandomArrayElement, seededRandomIntFromInterval, seededShuffleArray } from 'utils/common'; import { BaseTile, FreeTile, @@ -12,16 +12,223 @@ import { } from 'interfaces/models/game/tile'; import { Point } from 'interfaces/models/common'; import { Resource, ResourceCombination } from 'interfaces/models/game/resource'; -import { OasisShapes, oasisShapes } from 'constants/oasis-shapes'; import { Server } from 'interfaces/models/game/server'; import { createHash } from 'sha1-uint8array'; +import { Player } from 'interfaces/models/game/player'; + +export type OasisShapes = Record; +}>; + +const oasisShapes: OasisShapes = { + wheat: { + backgroundColor: '#FFF600', + shapes: [ + { + group: 1, + shape: [ + [0, 1, 1], + [0, 1, 1] + ] + }, + { + group: 2, + shape: [ + [0, 1, 1] + ] + }, + { + group: 3, + shape: [ + [0, 1, 3], + [0, 1, 2], + [1, 1, 2], + [0, 1, 1] + ] + }, + { + group: 4, + shape: [ + [0, 1, 1], + [0, 1, 2], + [0, 1, 2] + ] + }, + { + group: 5, + shape: [ + [0, 1, 1], + [1, 1, 2], + [1, 1, 1], + [1, 1, 0] + ] + }, + { + group: 6, + shape: [ + [0, 1, 0], + [0, 1, 0] + ] + } + // { + // group: 6, + // shape: [ + // [0, 1, 2], + // [0, 1, 2], + // [0, 1, 3], + // [0, 0, 3] + // ] + // }, + ] + }, + iron: { + backgroundColor: '#7B90A1', + shapes: [ + { + group: 1, + shape: [ + [0, 1, 2], + [0, 1, 2] + ] + }, + { + group: 2, + shape: [ + [0, 1, 0], + [0, 1, 1] + ] + }, + { + group: 3, + shape: [ + [0, 1, 1] + ] + }, + { + group: 4, + shape: [ + [0, 1, 1], + [0, 1, 0], + [0, 1, 0] + ] + }, + { + group: 5, + shape: [ + [0, 1, 1], + [0, 1, 2], + [0, 1, 2] + ] + }, + { + group: 8, + shape: [ + [0, 1, 0], + [0, 1, 0] + ] + } + ] + }, + wood: { + backgroundColor: '#426002', + shapes: [ + { + group: 1, + shape: [ + [0, 1, 1] + ] + }, + { + group: 2, + shape: [ + [0, 1, 1], + [0, 0, 1], + [0, 0, 1] + ] + }, + { + group: 3, + shape: [ + [0, 1, 2], + [0, 0, 1] + ] + }, + { + group: 4, + shape: [ + [0, 1, 1], + [0, 1, 0], + [0, 1, 0] + ] + }, + { + group: 5, + shape: [ + [0, 1, 0], + [0, 1, 0] + ] + }, + { + group: 6, + shape: [ + [1, 0, 1], + [1, 1, 1] + ] + } + ] + }, + clay: { + backgroundColor: '#C29760', + shapes: [ + { + group: 1, + shape: [ + [0, 1, 0], + [0, 1, 1] + ] + }, + { + group: 2, + shape: [ + [0, 1, 1], + [0, 0, 1] + ] + }, + { + group: 3, + shape: [ + [0, 1, 1], + [0, 1, 0] + ] + }, + { + group: 4, + shape: [ + [0, 1, 0], + [0, 1, 1], + [0, 0, 1] + ] + }, + { + group: 5, + shape: [ + [0, 1, 1] + ] + } + ] + } +}; type Distances = { offset: number; distanceFromCenter: number; }; -const weightedResourceFieldType: Record = { +const weightedResourceFieldComposition: Record = { 1: '00018', 2: '11115', 3: '3339', @@ -36,13 +243,13 @@ const weightedResourceFieldType: Record = { 60: '5436' }; -const generateFreeTileType = (tile: MaybeOccupiedOrOasisBaseTile): ResourceFieldType => { - const randomInt: number = seededRandomIntFromInterval(tile.seed, 1, 80); +const generateFreeTileType = (tile: MaybeOccupiedOrOasisBaseTile): ResourceFieldComposition => { + const randomInt: number = seededRandomIntFromInterval(tile.tileId, 1, 80); // eslint-disable-next-line no-restricted-syntax - for (const weight in weightedResourceFieldType) { + for (const weight in weightedResourceFieldComposition) { if (randomInt <= Number(weight)) { - return weightedResourceFieldType[weight]; + return weightedResourceFieldComposition[weight]; } } @@ -50,7 +257,7 @@ const generateFreeTileType = (tile: MaybeOccupiedOrOasisBaseTile): ResourceField }; const generateOasisTile = (tile: MaybeOccupiedOrOasisBaseTile, shapes: OasisShapes): OasisTile => { - const resourceType = seededRandomArrayElement(tile.seed, ['wheat', 'iron', 'clay', 'wood', 'wood-wheat', 'clay-wheat', 'iron-wheat']); + const resourceType = seededRandomArrayElement(tile.tileId, ['wheat', 'iron', 'clay', 'wood', 'wood-wheat', 'clay-wheat', 'iron-wheat']); const typeForGraphic = (resourceType.includes('-') ? resourceType.split('-')[0] : resourceType) as Resource; return { @@ -120,28 +327,8 @@ const getPredefinedVillagesCoordinates = (server: Server): Record { }; return { - seed: createHash() + tileId: createHash() .update(`${server.seed}${counter}`) .digest('hex'), serverId: id, @@ -185,7 +372,7 @@ const generateGrid = (server: Server): BaseTile[] => { }); }; -const generateInitialUserVillage = (tiles: BaseTile[]): MaybeOccupiedBaseTile[] => { +const generateInitialUserVillage = (tiles: BaseTile[], player: Player): MaybeOccupiedBaseTile[] => { const tilesToUpdate: MaybeOccupiedBaseTile[] = [...tiles]; const initialUserTileFindFunction = ({ coordinates }: BaseTile) => coordinates.x === 0 && coordinates.y === 0; @@ -197,28 +384,23 @@ const generateInitialUserVillage = (tiles: BaseTile[]): MaybeOccupiedBaseTile[] ...initialUserTile, type: 'free-tile', resourceFieldComposition: '4446', - ownedBy: 'player' + ownedBy: player.id, } satisfies OccupiedFreeTile; return tilesToUpdate; }; -const generatePredefinedVillages = (server: Server, tiles: MaybeOccupiedBaseTile[]): MaybeOccupiedBaseTile[] => { +const generatePredefinedVillages = (server: Server, npcPlayers: Player[], tiles: MaybeOccupiedBaseTile[]): MaybeOccupiedBaseTile[] => { const tilesToUpdate = [...tiles]; - const { - artifactVillagesCoordinates, - quadrantBaseCoordinates - } = getPredefinedVillagesCoordinates(server); - - [ - ...artifactVillagesCoordinates, - ...quadrantBaseCoordinates - ].forEach((coordinate: Point) => { - const { - x, - y - } = coordinate; + const { artifactVillagesCoordinates } = getPredefinedVillagesCoordinates(server); + // Used for seeding array shuffling, so we get same result every time + const joinedSeeds: string = tiles.reduce((accumulator: string, tile) => accumulator + tile.tileId, ''); + // Since there's 4 npc players and 8 predefined villages, we just duplicate the npc players array, so each faction gets 2 villages + const players = seededShuffleArray(joinedSeeds, [...npcPlayers, ...npcPlayers]); + + [...artifactVillagesCoordinates,].forEach(({x, y}: Point, index: number) => { + const playerId = players[index].id; const tileFindFunction = ({ coordinates }: BaseTile) => coordinates.x === x && coordinates.y === y; const tileToUpdateIndex = tiles.findIndex(tileFindFunction); @@ -228,31 +410,31 @@ const generatePredefinedVillages = (server: Server, tiles: MaybeOccupiedBaseTile ...tileToUpdate, type: 'free-tile', resourceFieldComposition: '4446', - ownedBy: 'npc' + ownedBy: playerId } satisfies OccupiedFreeTile; }); return tilesToUpdate; }; -const generateShapedOasisFields = (tiles: MaybeOccupiedBaseTile[]): MaybeOccupiedOrOasisBaseTile[] => { - const tilesWithOasisShapes: MaybeOccupiedOrOasisBaseTile[] = [...tiles]; +const generateShapedOasisFields = (tiles: MaybeOccupiedBaseTile[]): MaybeOccupiedBaseTile[] => { + const tilesWithOasisShapes: MaybeOccupiedBaseTile[] = [...tiles]; for (let i = 0; i < tilesWithOasisShapes.length; i += 1) { - const currentTile: MaybeOccupiedOrOasisBaseTile = tilesWithOasisShapes[i]; + const currentTile: MaybeOccupiedBaseTile = tilesWithOasisShapes[i]; if (Object.hasOwn(currentTile, 'type')) { continue; } - const tileWillBeOasis: boolean = seededRandomIntFromInterval(currentTile.seed, 1, 15) === 1; + const tileWillBeOasis: boolean = seededRandomIntFromInterval(currentTile.tileId, 1, 15) === 1; // Determine oasis position and shape if (tileWillBeOasis) { // Surrounding tiles will have to become oasis as well, depending on shape of the oasis const tilesToUpdate: BaseTile[] = []; - const resourceType: Resource = seededRandomArrayElement(currentTile.seed, ['wheat', 'iron', 'clay', 'wood']); + const resourceType: Resource = seededRandomArrayElement(currentTile.tileId, ['wheat', 'iron', 'clay', 'wood']); const { shapes } = oasisShapes[resourceType]; - const selectedOasis = shapes[seededRandomArrayElement(currentTile.seed, [...Array(shapes.length) + const selectedOasis = shapes[seededRandomArrayElement(currentTile.tileId, [...Array(shapes.length) .keys()])]; const { shape: oasisShape @@ -277,8 +459,8 @@ const generateShapedOasisFields = (tiles: MaybeOccupiedBaseTile[]): MaybeOccupie if (!hasMiddleField && j === x) { continue; } - const tile: MaybeOccupiedOrOasisBaseTile | undefined = tilesWithOasisShapes.find( - (cell: MaybeOccupiedOrOasisBaseTile) => cell.coordinates.y === y - k && cell.coordinates.x === j + const tile: MaybeOccupiedBaseTile | undefined = tilesWithOasisShapes.find( + (cell: MaybeOccupiedBaseTile) => cell.coordinates.y === y - k && cell.coordinates.x === j ); if (!tile) { breakCondition = true; @@ -312,14 +494,14 @@ const generateShapedOasisFields = (tiles: MaybeOccupiedBaseTile[]): MaybeOccupie return tilesWithOasisShapes; }; -const generateSingleOasisFields = (tiles: MaybeOccupiedOrOasisBaseTile[]): MaybeOccupiedOrOasisBaseTile[] => { +const generateSingleOasisFields = (tiles: MaybeOccupiedBaseTile[]): MaybeOccupiedBaseTile[] => { // To make world feel more alive and give player more options, we sprinkle a bunch of 1x1 oasis on empty fields as well - return tiles.map((tile: MaybeOccupiedOrOasisBaseTile) => { + return tiles.map((tile: MaybeOccupiedBaseTile) => { // If field is already an oasis, or a free field, continue if (Object.hasOwn(tile, 'type') || Object.hasOwn(tile, 'oasisType')) { return tile; } - const willBeOccupied = seededRandomIntFromInterval(tile.seed, 1, 5) === 1; + const willBeOccupied = seededRandomIntFromInterval(tile.tileId, 1, 5) === 1; if (!willBeOccupied) { return tile; } @@ -344,13 +526,13 @@ const generateFreeTileTypes = (tiles: MaybeOccupiedOrOasisBaseTile[]): MaybeOccu // This method will mark which oasis will be actual resource oasis, but it does not add animals to them const populateOasis = (tiles: Tile[]): Tile[] => { return tiles.map((tile: Tile) => { - const willOasisHaveABonus = seededRandomIntFromInterval(tile.seed, 1, 3) >= 2; + const willOasisHaveABonus = seededRandomIntFromInterval(tile.tileId, 1, 3) >= 2; if (tile.type !== 'oasis-tile' || !willOasisHaveABonus) { return tile; } - const oasisBonus = seededRandomArrayElement(tile.seed, ['25%', '50%', null]); + const oasisBonus = seededRandomArrayElement(tile.tileId, ['25%', '50%', null]); return { ...tile, oasisBonus @@ -358,24 +540,40 @@ const populateOasis = (tiles: Tile[]): Tile[] => { }); }; -// This method will mark which fields will have villages, but it does not add them to fields -const populateFreeTiles = (tiles: Tile[]): Tile[] => { - return tiles; +// This method will mark which fields will have villages +const populateFreeTiles = (tiles: Tile[], npcPlayers: Player[]): Tile[] => { + return tiles.map((tile: Tile) => { + if(tile.type !== 'free-tile' && !Object.hasOwn(tile, 'ownedBy')) { + return tile; + } + const willBeOccupied = seededRandomIntFromInterval(tile.tileId, 1, 4) === 1; + + return { + ...tile, + ...(willBeOccupied && { + ownedBy: seededRandomArrayElement(tile.tileId, npcPlayers).id + }) + }; + }); }; type MapFactoryProps = { server: Server; + players: Player[]; }; -export const mapFactory = ({ server }: MapFactoryProps): Tile[] => { +export const mapFactory = ({ server, players }: MapFactoryProps): Tile[] => { + const npcPlayers = players.filter(({ faction }) => faction !== 'player'); + const player = players.find(({ faction }) => faction === 'player')!; + const emptyTiles = generateGrid(server); - const tilesWithInitialUserVillage = generateInitialUserVillage(emptyTiles); - const tilesWithPredefinedVillages = generatePredefinedVillages(server, tilesWithInitialUserVillage); + const tilesWithInitialUserVillage = generateInitialUserVillage(emptyTiles, player); + const tilesWithPredefinedVillages = generatePredefinedVillages(server, npcPlayers, tilesWithInitialUserVillage); const tilesWithShapedOasisFields = generateShapedOasisFields(tilesWithPredefinedVillages); const tilesWithSingleOasisFields = generateSingleOasisFields(tilesWithShapedOasisFields); const tilesWithFreeTileTypes = generateFreeTileTypes(tilesWithSingleOasisFields); const tilesWithPopulatedOasis = populateOasis(tilesWithFreeTileTypes); - const tilesWithPopulatedFreeTiles = populateFreeTiles(tilesWithPopulatedOasis); + const tilesWithPopulatedFreeTiles = populateFreeTiles(tilesWithPopulatedOasis, npcPlayers); return tilesWithPopulatedFreeTiles; }; diff --git a/src/factories/player-factory.ts b/src/factories/player-factory.ts new file mode 100644 index 0000000..25ffebf --- /dev/null +++ b/src/factories/player-factory.ts @@ -0,0 +1,15 @@ +import { Server } from 'interfaces/models/game/server'; +import { Player, PlayerFaction } from 'interfaces/models/game/player'; + +type PlayerFactoryProps = { + server: Server; + faction: PlayerFaction; +}; + +export const playerFactory = ({ server, faction }: PlayerFactoryProps): Player => { + return { + id: crypto.randomUUID(), + serverId: server.id, + faction + }; +}; diff --git a/src/factories/quest-factory.ts b/src/factories/quest-factory.ts index 67bb660..cbcc17c 100644 --- a/src/factories/quest-factory.ts +++ b/src/factories/quest-factory.ts @@ -1,14 +1,23 @@ import { Quest } from 'interfaces/models/game/quest'; +import { Server } from 'interfaces/models/game/server'; +import { globalQuests, villageQuests } from 'assets/quests'; +import { Village } from 'interfaces/models/game/village'; -export const questFactory = (): Quest => { - +type QuestFactoryProps = { + server: Server; }; +export const questFactory = ({ server, ...quest }: QuestFactoryProps & Omit): Quest => { + return { + serverId: server.id, + ...quest + }; +}; -export const newVillageQuestsFactory = (): Quest[] => { - return []; +export const newVillageQuestsFactory = ({ server, village }: QuestFactoryProps & { village: Village; }): Quest[] => { + return villageQuests.map((quest) => questFactory({ server, villageId: village.id, ...quest })); } -export const globalQuestsFactory = (): Quest[] => { - return []; +export const globalQuestsFactory = ({ server }: QuestFactoryProps): Quest[] => { + return globalQuests.map((quest) => questFactory({ server, ...quest })); } diff --git a/src/factories/reputation-factory.ts b/src/factories/reputation-factory.ts new file mode 100644 index 0000000..de4103f --- /dev/null +++ b/src/factories/reputation-factory.ts @@ -0,0 +1,27 @@ +import { Server } from 'interfaces/models/game/server'; +import { Reputation, ReputationLevel } from 'interfaces/models/game/reputation'; +import { PlayerFaction } from 'interfaces/models/game/player'; + +type ReputationFactoryProps = { + server: Server; + faction: PlayerFaction; +}; + +// Players start at different levels of reputation with each faction +const factionToPredefinedReputationLevelMap = new Map([ + ['npc1', 'friendly'], + ['npc2', 'neutral'], + ['npc3', 'unfriendly'], + ['npc4', 'hostile'], +]); + +export const reputationFactory = ({ server, faction }: ReputationFactoryProps): Reputation => { + const reputationLevel = factionToPredefinedReputationLevelMap.get(faction)!; + + return { + serverId: server.id, + faction, + percentage: 0, + reputationLevel + }; +}; diff --git a/src/factories/server-factory.ts b/src/factories/server-factory.ts index 995e2e5..bab60c6 100644 --- a/src/factories/server-factory.ts +++ b/src/factories/server-factory.ts @@ -1,10 +1,9 @@ import { Server } from 'interfaces/models/game/server'; -import { v4 as uuidv4 } from 'uuid'; type ServerFactoryProps = Pick; export const serverFactory = ({ name, seed, configuration, playerConfiguration }: ServerFactoryProps): Server => { - const id = uuidv4(); + const id = crypto.randomUUID(); const slug = `server-${id.substring(0, 4)}`; return { diff --git a/src/factories/village-factory.ts b/src/factories/village-factory.ts index 9b46c2f..85c69fc 100644 --- a/src/factories/village-factory.ts +++ b/src/factories/village-factory.ts @@ -1,14 +1,241 @@ -import { Village } from 'interfaces/models/game/village'; -import { Point } from 'interfaces/models/common'; +import { + BuildingField, + ResourceField, + ResourceFieldComposition, + ResourceFieldId, + Village, + VillageBuildingFieldsPresetName +} from 'interfaces/models/game/village'; import { Server } from 'interfaces/models/game/server'; +import { villagePresets } from 'assets/village-presets'; +import { Resource } from 'interfaces/models/game/resource'; +import { Player } from 'interfaces/models/game/player'; +import { FreeTile, OccupiedFreeTile } from 'interfaces/models/game/tile'; + +export type ResourceFieldLayout = Record; + +const fullWheatLayout: ResourceFieldLayout = Object.fromEntries([...(new Array(18))].map((_, i) => [[i + 1], ['wheat']])); + +// We set only non-wheat fields, since wheat field is the most common type +const resourceFieldsLayouts: Record = { + '00018': fullWheatLayout, + '3339': { + ...fullWheatLayout + // 1: '', + // 1: '', + // 1: '', + // 1: '', + // 1: '', + // 1: '', + // 1: '', + // 1: '', + // 1: '' + }, + '3447': { + ...fullWheatLayout + // 1: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '3456': { + ...fullWheatLayout + // 1: '', + // 3: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '3546': { + ...fullWheatLayout + // 1: '', + // 3: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '4347': { + ...fullWheatLayout + // 1: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '4356': { + ...fullWheatLayout + // 1: '', + // 3: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '4437': { + ...fullWheatLayout + // 1: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '4446': { + ...fullWheatLayout, + 1: 'wood', + 3: 'wood', + 4: 'iron', + 5: 'clay', + 6: 'clay', + 7: 'iron', + 10: 'iron', + 11: 'iron', + 14: 'wood', + 16: 'clay', + 17: 'wood', + 18: 'clay' + }, + '4536': { + ...fullWheatLayout + // 1: '', + // 3: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '5346': { + ...fullWheatLayout + // 1: '', + // 3: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '5436': { + ...fullWheatLayout + // 1: '', + // 3: '', + // 4: '', + // 5: '', + // 6: '', + // 7: '', + // 10: '', + // 11: '', + // 14: '', + // 16: '', + // 17: '', + // 18: '' + }, + '11115': { + ...fullWheatLayout, + 3: 'wood', + 4: 'iron', + 16: 'clay' + } +}; + +const convertResourceFieldLayoutToResourceField = (resourceFieldLayout: ResourceFieldLayout): ResourceField[] => { + return (Object.keys(resourceFieldLayout) as ResourceFieldId[]).map((resourceFieldId: ResourceFieldId) => { + const type = resourceFieldLayout[resourceFieldId]; + return { + resourceFieldId, + type, + level: 0 + } + }); +}; + +const getVillageResourceFields = (resourceFieldComposition: ResourceFieldComposition): ResourceField[] => { + const resourceFieldsLayout = resourceFieldsLayouts[resourceFieldComposition]; + return convertResourceFieldLayoutToResourceField(resourceFieldsLayout); +} + +const getVillageBuildingFields = (presetName: VillageBuildingFieldsPresetName): BuildingField[] => { + return villagePresets.find(({ preset }) => preset === presetName)!.buildingFields; +} type VillageFactoryProps = { server: Server; - coordinates: Point; + tile: OccupiedFreeTile; + players: Player[]; + slug: Village['slug']; }; -export const villageFactory = ({ server, coordinates, }: VillageFactoryProps): Village => { +export const villageFactory = ({ server, tile, players, slug }: VillageFactoryProps): Village => { + const { coordinates, resourceFieldComposition } = tile; + const { id: playerId, faction } = players.find((player) => player.id === tile.ownedBy)!; + + const resourceFields = getVillageResourceFields(resourceFieldComposition); + const buildingFields = getVillageBuildingFields('new-village'); + return { serverId: server.id, + id: crypto.randomUUID(), + name: `${faction}-${tile.tileId}`, + slug, + coordinates, + resourceFields, + buildingFields, + playerId, + isCapital: false, + lastUpdatedAt: Date.now(), + resources: { + wood: 750, + clay: 750, + iron: 750, + wheat: 750, + } } }; diff --git a/src/hooks/game/use-current-village.ts b/src/hooks/game/use-current-village.ts index 5b2470f..3f7d026 100644 --- a/src/hooks/game/use-current-village.ts +++ b/src/hooks/game/use-current-village.ts @@ -29,10 +29,14 @@ export const useCurrentVillage = () => { cacheKey: currentVillageCacheKey }); + const currentVillageId = currentVillage!.id; + return { currentVillage, isLoadingCurrentVillage, hasLoadedCurrentVillage, - currentVillageStatus + currentVillageStatus, + mutateCurrentVillage, + currentVillageId, }; }; diff --git a/src/hooks/game/use-effects.ts b/src/hooks/game/use-effects.ts index fc33235..358191e 100644 --- a/src/hooks/game/use-effects.ts +++ b/src/hooks/game/use-effects.ts @@ -4,6 +4,7 @@ import { Effect } from 'interfaces/models/game/effect'; import { useAsyncLiveQuery } from 'hooks/database/use-async-live-query'; import { useDatabaseMutation } from 'hooks/database/use-database-mutation'; import { Server } from 'interfaces/models/game/server'; +import { useCurrentVillage } from 'hooks/game/use-current-village'; export const effectsCacheKey = 'effects'; @@ -11,6 +12,7 @@ export const getEffects = (serverId: Server['id']) => database.effects.where({ s export const useEffects = () => { const { serverId, hasLoadedServer } = useCurrentServer(); + const { currentVillageId } = useCurrentVillage(); const { mutate: mutateEffects } = useDatabaseMutation({ cacheKey: effectsCacheKey }); const { @@ -26,10 +28,16 @@ export const useEffects = () => { enabled: hasLoadedServer }); + const globalEffects = effects.filter(({ scope }) => scope === 'global'); + const currentVillageEffects = effects.filter(({ villageId }) => villageId === currentVillageId); + return { effects, isLoadingEffects, hasLoadedEffects, - effectsQueryStatus + effectsQueryStatus, + mutateEffects, + globalEffects, + currentVillageEffects }; }; diff --git a/src/hooks/game/use-players.ts b/src/hooks/game/use-players.ts new file mode 100644 index 0000000..ecf9131 --- /dev/null +++ b/src/hooks/game/use-players.ts @@ -0,0 +1,41 @@ +import { database } from 'database/database'; +import { useCurrentServer } from 'hooks/game/use-current-server'; +import { useAsyncLiveQuery } from 'hooks/database/use-async-live-query'; +import { useDatabaseMutation } from 'hooks/database/use-database-mutation'; +import { Server } from 'interfaces/models/game/server'; +import { Player, PlayerFaction } from 'interfaces/models/game/player'; + +export const playersCacheKey = 'players'; + +export const getPlayers = (serverId: Server['id']) => database.players.where({ serverId }).toArray(); + +export const usePlayers = () => { + const { serverId, hasLoadedServer } = useCurrentServer(); + const { mutate: mutatePlayers } = useDatabaseMutation({ cacheKey: playersCacheKey }); + + const { + data: players, + isLoading: isLoadingPlayers, + isSuccess: hasLoadedPlayers, + status: playersQueryStatus + } = useAsyncLiveQuery({ + queryFn: () => getPlayers(serverId), + deps: [serverId], + fallback: [], + cacheKey: playersCacheKey, + enabled: hasLoadedServer + }); + + const getFactionByPlayerId = (playerId: Player['id']): PlayerFaction => { + return players.find(({ id }) => playerId === id)?.faction!; + }; + + return { + players, + isLoadingPlayers, + hasLoadedPlayers, + playersQueryStatus, + mutatePlayers, + getFactionByPlayerId + }; +}; diff --git a/src/hooks/game/use-quests.ts b/src/hooks/game/use-quests.ts index e64c95d..8c67a79 100644 --- a/src/hooks/game/use-quests.ts +++ b/src/hooks/game/use-quests.ts @@ -4,6 +4,7 @@ import { Quest } from 'interfaces/models/game/quest'; import { useAsyncLiveQuery } from 'hooks/database/use-async-live-query'; import { useDatabaseMutation } from 'hooks/database/use-database-mutation'; import { Server } from 'interfaces/models/game/server'; +import { useCurrentVillage } from 'hooks/game/use-current-village'; export const questsCacheKey = 'quests'; @@ -11,6 +12,7 @@ export const getQuests = (serverId: Server['id']) => database.quests.where({ ser export const useQuests = () => { const { serverId, hasLoadedServer } = useCurrentServer(); + const { currentVillageId } = useCurrentVillage(); const { mutate: mutateQuests } = useDatabaseMutation({ cacheKey: questsCacheKey }); const { @@ -26,12 +28,16 @@ export const useQuests = () => { enabled: hasLoadedServer }); - const completedQuests = quests.filter((quest: Quest) => true); + const globalQuests = quests.filter(({ scope }) => scope === 'global'); + const currentVillageQuests = quests.filter(({ villageId }) => villageId === currentVillageId); return { quests, isLoadingQuests, hasLoadedQuests, - questsQueryStatus + questsQueryStatus, + mutateQuests, + globalQuests, + currentVillageQuests }; }; diff --git a/src/hooks/game/use-reputations.ts b/src/hooks/game/use-reputations.ts new file mode 100644 index 0000000..83b7a41 --- /dev/null +++ b/src/hooks/game/use-reputations.ts @@ -0,0 +1,42 @@ +import { database } from 'database/database'; +import { useCurrentServer } from 'hooks/game/use-current-server'; +import { useAsyncLiveQuery } from 'hooks/database/use-async-live-query'; +import { useDatabaseMutation } from 'hooks/database/use-database-mutation'; +import { Server } from 'interfaces/models/game/server'; +import { Reputation } from 'interfaces/models/game/reputation'; +import { PlayerFaction } from 'interfaces/models/game/player'; + +export const reputationsCacheKey = 'reputations'; + +export const getReputations = (serverId: Server['id']) => database.reputations.where({ serverId }).toArray(); + +export const useReputations = () => { + const { serverId, hasLoadedServer } = useCurrentServer(); + const { mutate: mutateReputations } = useDatabaseMutation({ cacheKey: reputationsCacheKey }); + + const { + data: reputations, + isLoading: isLoadingReputations, + isSuccess: hasLoadedReputations, + status: reputationsQueryStatus + } = useAsyncLiveQuery({ + queryFn: () => getReputations(serverId), + deps: [serverId], + fallback: [], + cacheKey: reputationsCacheKey, + enabled: hasLoadedServer + }); + + const getReputationByFaction = (faction: PlayerFaction): Reputation => { + return reputations.find(({ faction: reputationFaction }) => faction === reputationFaction)!; + }; + + return { + reputations, + isLoadingReputations, + hasLoadedReputations, + reputationsQueryStatus, + mutateReputations, + getReputationByFaction + }; +}; diff --git a/src/hooks/game/use-villages.ts b/src/hooks/game/use-villages.ts index ecd950c..9366a4a 100644 --- a/src/hooks/game/use-villages.ts +++ b/src/hooks/game/use-villages.ts @@ -29,12 +29,18 @@ export const useVillages = () => { const playerVillages: Village[] = villages?.filter((village: Village) => true); const npcVillages: Village[] = villages?.filter((village: Village) => true); + const getVillageByCoordinates = (coordinates: Village['coordinates']): Village | null => { + return villages.find(({ coordinates: { x, y }}) => coordinates.x === x && coordinates.y === y) ?? null; + } + return { villages, isLoadingVillages, hasLoadedVillages, villagesQueryStatus, + mutateVillages, playerVillages, - npcVillages + npcVillages, + getVillageByCoordinates }; }; diff --git a/src/hooks/use-available-servers.ts b/src/hooks/use-available-servers.ts index cae6f56..38c7005 100644 --- a/src/hooks/use-available-servers.ts +++ b/src/hooks/use-available-servers.ts @@ -42,11 +42,17 @@ export const useAvailableServers = () => { .delete(), database.achievements.where({ serverId }) .delete(), - database.researchLevels.where({ serverId }) - .delete(), database.events.where({ serverId }) .delete(), database.effects.where({ serverId }) + .delete(), + database.banks.where({ serverId }) + .delete(), + database.researchLevels.where({ serverId }) + .delete(), + database.players.where({ serverId }) + .delete(), + database.reputations.where({ serverId }) .delete() ]); }); diff --git a/src/interfaces/models/database/crylite-table.ts b/src/interfaces/models/database/crylite-table.ts index 32a434e..77409e4 100644 --- a/src/interfaces/models/database/crylite-table.ts +++ b/src/interfaces/models/database/crylite-table.ts @@ -9,4 +9,6 @@ export type CryliteTableName = | 'events' | 'effects' | 'banks' + | 'players' + | 'reputations' | 'researchLevels'; diff --git a/src/interfaces/models/game/building.ts b/src/interfaces/models/game/building.ts index 3399e15..61e709a 100644 --- a/src/interfaces/models/game/building.ts +++ b/src/interfaces/models/game/building.ts @@ -9,7 +9,7 @@ export type ResourceBuildingId = | 'IRON_MINE'; export type BuildingId = - ResourceBuildingId + | ResourceBuildingId | 'BAKERY' | 'BRICKYARD' | 'GRAIN_MILL' @@ -49,8 +49,7 @@ export type BuildingId = | 'RESIDENCE' | 'TOWN_HALL' | 'TRADE_OFFICE' - | 'TREASURY' - | 'WONDER_OF_THE_WORLD'; + | 'TREASURY'; export type BuildingEffect = { effectId: EffectId; @@ -70,11 +69,9 @@ export type BuildingRequirement = { export type Building = { id: BuildingId; - maxLevel: number; buildingDuration: number[]; culturePointsProduction: number[]; cropConsumption: number[]; - effects?: BuildingEffect[]; - buildingRequirements?: BuildingRequirement[]; - totalResourceCost: number[]; + effects: BuildingEffect[]; + buildingRequirements: BuildingRequirement[]; } & Record; diff --git a/src/interfaces/models/game/effect.ts b/src/interfaces/models/game/effect.ts index c7a262b..0b07def 100644 --- a/src/interfaces/models/game/effect.ts +++ b/src/interfaces/models/game/effect.ts @@ -74,8 +74,8 @@ export type EffectId = export type Effect = WithServerId<{ // Server effects affect actions from all villages, village effects affect only actions from a particular village - scope: 'server' | 'village'; - village_id?: Village['id']; + scope: 'global' | 'village'; + villageId?: Village['id']; effectId: EffectId; value: number; }>; diff --git a/src/interfaces/models/game/player.ts b/src/interfaces/models/game/player.ts new file mode 100644 index 0000000..6d4f599 --- /dev/null +++ b/src/interfaces/models/game/player.ts @@ -0,0 +1,14 @@ +import { WithServerId } from 'interfaces/models/game/server'; + +// Plan is to add multiple factions, where you'd have different relations with each +export type PlayerFaction = + | 'player' + | 'npc1' + | 'npc2' + | 'npc3' + | 'npc4' + +export type Player = WithServerId<{ + id: string; + faction: PlayerFaction +}>; diff --git a/src/interfaces/models/game/quest.ts b/src/interfaces/models/game/quest.ts index 2fb7e64..20d98b9 100644 --- a/src/interfaces/models/game/quest.ts +++ b/src/interfaces/models/game/quest.ts @@ -6,7 +6,7 @@ import { WithServerId } from 'interfaces/models/game/server'; */ export type Quest = WithServerId<{ id: number; - scope: 'server' | 'village'; - villageId: Village['id']; + scope: 'global' | 'village'; + villageId?: Village['id']; isCompleted: boolean; }>; diff --git a/src/interfaces/models/game/reputation.ts b/src/interfaces/models/game/reputation.ts new file mode 100644 index 0000000..4324556 --- /dev/null +++ b/src/interfaces/models/game/reputation.ts @@ -0,0 +1,23 @@ +import { WithServerId } from 'interfaces/models/game/server'; +import { PlayerFaction } from 'interfaces/models/game/player'; + +export type ReputationLevel = + // Trade is possible. Trade ratio is set at 1:1. Faction may reinforce the player in defensive battles. Faction may offer quests. + | 'ecstatic' + // Trade is possible. Trade ratio is set at 2:1. Faction may offer quests. + | 'respected' + // Trade is possible. Trade ratio is set at 3:1. Faction may offer quests. + | 'friendly' + // Trade is possible. Trade ratio is set at 4:1. + | 'neutral' + // Trade is not possible. Faction will not be sending attacks and raids. + | 'unfriendly' + // Trade is not possible. Faction will be sending attacks and raids. + | 'hostile'; + + +export type Reputation = WithServerId<{ + faction: PlayerFaction; + percentage: number; + reputationLevel: ReputationLevel; +}>; diff --git a/src/interfaces/models/game/tile.ts b/src/interfaces/models/game/tile.ts index d2e5b8a..2709c62 100644 --- a/src/interfaces/models/game/tile.ts +++ b/src/interfaces/models/game/tile.ts @@ -1,17 +1,19 @@ import { Point } from 'interfaces/models/common'; -import { ResourceFieldType, Village } from 'interfaces/models/game/village'; +import { ResourceFieldComposition, Village } from 'interfaces/models/game/village'; import { WithServerId } from 'interfaces/models/game/server'; import { Resource, ResourceCombination } from 'interfaces/models/game/resource'; +import { Player } from 'interfaces/models/game/player'; export type BaseTile = WithServerId<{ - // Seed is constructed from server.id and tile index and is used for seeded prng - seed: string; + // Tile id is constructed from server.id and tile index and is used for seeded prng. This property can't be named 'id', because otherwise Dexie + // auto-sorts by it, and it messes up the map + tileId: string; coordinates: Point; // Both backgroundColor & oasisGroup will be replaced by an actual graphic once they exist graphics: { backgroundColor: string; oasisGroup?: number; - } + }; }>; export type OasisTile = BaseTile & { @@ -21,16 +23,16 @@ export type OasisTile = BaseTile & { }; export type OccupiedOasisTile = OasisTile & { - ownerVillageId: Village['id'] | null; + villageId: Village['id'] | null; }; export type FreeTile = BaseTile & { type: 'free-tile'; - resourceFieldComposition: ResourceFieldType; + resourceFieldComposition: ResourceFieldComposition; }; export type OccupiedFreeTile = FreeTile & { - ownedBy: 'player' | 'npc' | 'abandoned'; + ownedBy: Player['id']; }; export type Tile = @@ -43,3 +45,4 @@ export type MaybeOccupiedBaseTile = BaseTile | OccupiedFreeTile; export type MaybeOccupiedOrOasisBaseTile = MaybeOccupiedBaseTile | OasisTile; export type MaybeOccupiedOrOasisFreeTile = FreeTile | OccupiedFreeTile | OasisTile; export type MaybeOccupiedFreeTile = FreeTile | OccupiedFreeTile; + diff --git a/src/interfaces/models/game/village.ts b/src/interfaces/models/game/village.ts index bbe9e24..68e588a 100644 --- a/src/interfaces/models/game/village.ts +++ b/src/interfaces/models/game/village.ts @@ -2,9 +2,10 @@ import { Point } from 'interfaces/models/common'; import { Resource, Resources } from 'interfaces/models/game/resource'; import { Building } from 'interfaces/models/game/building'; import { Server } from 'interfaces/models/game/server'; +import { Player } from 'interfaces/models/game/player'; export type ResourceFieldId = - '1' + | '1' | '2' | '3' | '4' @@ -24,12 +25,13 @@ export type ResourceFieldId = | '18'; export type ResourceField = { + resourceFieldId: ResourceFieldId; type: Resource; level: number; }; -export type ResourceFieldType = - '4446' +export type ResourceFieldComposition = + | '4446' | '5436' | '5346' | '4536' @@ -43,59 +45,36 @@ export type ResourceFieldType = | '11115' | '00018'; -export type ResourceFieldLayout = { - [key in ResourceFieldId]: Resource; -}; - -export type ResourceFieldLayoutByFieldType = { - [key in ResourceFieldType]: ResourceFieldLayout; -}; - -export type ResourceFields = { - [key in ResourceFieldId]: ResourceField | null; -}; - +// Just kinda reusing a type, since there's no differences between most ids export type BuildingFieldId = - '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8' - | '9' - | '10' - | '11' - | '12' - | '13' - | '14' - | '15' - | '16' - | '17' - | '18' + | ResourceFieldId | '19' | '20'; export type BuildingField = { - buildingId: Building['id']; + buildingFieldId: BuildingFieldId; + buildingId: Building['id'] | null; level: number; }; -export type BuildingFields = Record; - export type Village = { serverId: Server['id']; id: string; + playerId: Player['id']; name: string; slug: string; lastUpdatedAt: number; - position: Point; + coordinates: Point; resources: Resources; - storageCapacity: Resources; - hourlyProduction: Resources; - resourceFields: ResourceFields; - buildingFields: BuildingFields; + resourceFields: ResourceField[]; + buildingFields: BuildingField[]; isCapital: boolean; - ownedBy: number; }; + +export type VillageBuildingFieldsPresetName = + | 'new-village'; + +export type VillageBuildingFieldsPreset = { + preset: VillageBuildingFieldsPresetName; + buildingFields: BuildingField[]; +} diff --git a/src/maps/resource-to-building-map.ts b/src/maps/resource-to-building-map.ts deleted file mode 100644 index d46a957..0000000 --- a/src/maps/resource-to-building-map.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Resource } from 'interfaces/models/game/resource'; - -export const resourceToBuildingMap: { [key in Resource]: 'WOODCUTTER' | 'CLAY_PIT' | 'IRON_MINE' | 'CROPLAND' } = { - wood: 'WOODCUTTER', - clay: 'CLAY_PIT', - iron: 'IRON_MINE', - wheat: 'CROPLAND' -}; diff --git a/src/utils/common.ts b/src/utils/common.ts index 9f34e70..d2ff3ef 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -18,6 +18,7 @@ export const seededRandomIntFromInterval = (seed: string, min: number, max: numb }; export const seededRandomArrayElement = (seed: string, array: T[]): T => { + // TODO: Seeding isn't working for whichever reason, it's disabled for now. Fix when you have the nerve for it. return array[Math.floor(Math.random() * array.length)]; }; @@ -83,3 +84,14 @@ export const chunk = (array: T[], size: number): T[][] => { } return result; }; + +export const seededShuffleArray = (seed: string, array: T[]): T[] => { + const copy = [...array]; + for (let i = copy.length - 1; i > 0; i--) { + // TODO: Seeding isn't working for whichever reason, it's disabled for now. Fix when you have the nerve for it. + const j = Math.floor(Math.random() * (i + 1)); + [copy[i], copy[j]] = [copy[j], copy[i]]; + } + + return copy; +} diff --git a/src/utils/errors.ts b/src/utils/errors.ts deleted file mode 100644 index 67ddeb0..0000000 --- a/src/utils/errors.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable max-classes-per-file */ - -import { Server } from 'interfaces/models/game/server'; - -export class CacheHydrationError extends Error { - constructor(serverId: Server['id']) { - super(); - this.name = 'CacheHydrationError'; - this.message = `One or more database entries are missing for server with id: ${serverId}.`; - } -} - -export class MissingServerError extends Error { - constructor(serverSlug: Server['slug']) { - super(); - this.name = 'MissingServerError'; - this.message = `Server with slug "${serverSlug}" does not exist. If you're accessing this server from an url, try accessing the server from the homepage, to make sure the server still exists.`; - } -} diff --git a/src/utils/game/__tests__/common.test.ts b/src/utils/game/__tests__/common.test.ts new file mode 100644 index 0000000..3300e67 --- /dev/null +++ b/src/utils/game/__tests__/common.test.ts @@ -0,0 +1,24 @@ +import { + calculateCulturePointsProductionFromBuildingFields, + calculatePopulationFromBuildingFields, + calculateResourceProductionFromResourceFields +} from 'utils/game/common'; +import _buildingData from 'assets/buildings.json'; +import { Building } from 'interfaces/models/game/building'; +import { newVillageBuildingFieldsMock } from 'mocks/game/village/building-fields-mock'; + +const buildingData = _buildingData as Building[]; + +describe('Game util functions', () => { + test('calculatePopulationFromBuildingFields', () => { + expect(calculatePopulationFromBuildingFields(newVillageBuildingFieldsMock, buildingData)).toBe(3); + }); + + test('calculateCulturePointsProductionFromBuildingFields', () => { + expect(calculateCulturePointsProductionFromBuildingFields(newVillageBuildingFieldsMock, buildingData)).toBe(3); + }); + + // test('calculateResourceProductionFromResourceFields', () => { + // calculateResourceProductionFromResourceFields + // }); +}); diff --git a/src/utils/game/common.ts b/src/utils/game/common.ts new file mode 100644 index 0000000..c32c27c --- /dev/null +++ b/src/utils/game/common.ts @@ -0,0 +1,45 @@ +import { BuildingField, ResourceField } from 'interfaces/models/game/village'; +import { Resource } from 'interfaces/models/game/resource'; +import { Building } from 'interfaces/models/game/building'; +import { partialArraySum } from 'utils/common'; + +export const calculatePopulationFromBuildingFields = (buildingFields: BuildingField[], buildingData: Building[]): number => { + let sum: number = 0; + + buildingFields.forEach(({ buildingId , level }) => { + if(buildingId === null) { + return; + } + + const fullBuildingData: Building = buildingData.find(({ id }) => id === buildingId)!; + sum += partialArraySum(fullBuildingData.cropConsumption, level); + }); + + return sum; +}; + +export const calculateCulturePointsProductionFromBuildingFields = (buildingFields: BuildingField[], buildingData: Building[]): number => { + let sum: number = 0; + + buildingFields.forEach(({ buildingId , level }) => { + if(buildingId === null) { + return; + } + + const fullBuildingData: Building = buildingData.find(({ id }) => id === buildingId)!; + sum += partialArraySum(fullBuildingData.culturePointsProduction, level); + }); + + return sum; +}; + +export const calculateResourceProductionFromResourceFields = (resourceFields: ResourceField[], buildingData: Building[]) => { + const [woodFields, clayFields, ironFields, wheatFields] = [ + resourceFields.filter(({ type }) => type === 'wood'), + resourceFields.filter(({ type }) => type === 'clay'), + resourceFields.filter(({ type }) => type === 'iron'), + resourceFields.filter(({ type }) => type === 'wheat'), + ]; + + +}; diff --git a/src/utils/game/create-resource-fields-from-resource-layout.ts b/src/utils/game/create-resource-fields-from-resource-layout.ts deleted file mode 100644 index 277e2e9..0000000 --- a/src/utils/game/create-resource-fields-from-resource-layout.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ResourceFieldId, ResourceFieldLayout, ResourceFields } from 'interfaces/models/game/village'; -import { Resource } from 'interfaces/models/game/resource'; - -export const createResourceFieldsFromResourceLayout = (layout: Partial): ResourceFields => { - const resourceFields: Partial = {}; - Object.keys(layout).forEach((resourceFieldId: string) => { - resourceFields[resourceFieldId as ResourceFieldId] = { - type: layout[resourceFieldId as ResourceFieldId] as Resource, - level: 0 - }; - }); - - return resourceFields as ResourceFields; -}; diff --git a/src/utils/workers.ts b/src/utils/workers.ts new file mode 100644 index 0000000..b5e6bf6 --- /dev/null +++ b/src/utils/workers.ts @@ -0,0 +1,12 @@ +export const workerFactory = async (worker: string, payload: TPayload, errorMessage: string): Promise => { + return new Promise((resolve, reject) => { + const workerInstance: Worker = new Worker(worker, { type: 'module' }); + workerInstance.postMessage(payload); + workerInstance.addEventListener('message', async (event: MessageEvent) => { + resolve(event.data); + }); + workerInstance.addEventListener('error', () => { + reject(new Error(errorMessage)); + }); + }); +}; diff --git a/tsconfig.json b/tsconfig.json index fe2823b..2a3f9ff 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,21 +35,12 @@ "hooks/*": [ "src/hooks/*" ], - "constants/*": [ - "src/constants/*" - ], - "maps/*": [ - "src/maps/*" - ], "i18n/*": [ "src/i18n/*" ], "interfaces/*": [ "src/interfaces/*" ], - "providers/*": [ - "src/providers/*" - ], "styles/*": [ "src/styles/*" ], @@ -72,6 +63,7 @@ "src", "config", "**.config.ts", - "__mocks__" + "__mocks__", + "jest-setup.ts" ] } diff --git a/vite.config.ts b/vite.config.ts index e6b684b..5497e2d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,6 +10,21 @@ export default defineConfig({ open: true }, optimizeDeps: { - include: ['react', 'esm-seedrandom', 'uuid', 'dexie', 'formik', 'sha1-uint8array'] + include: [ + 'react', + 'esm-seedrandom', + 'uuid', + 'dexie', + 'formik', + 'sha1-uint8array', + 'react-helmet', + 'react-modal', + 'tailwind-override', + 'usehooks-ts', + 'clsx' + ] + }, + worker: { + format: 'es' } }) satisfies UserConfig;