Rebuild village state calculation
This commit is contained in:
		| @@ -1,4 +1,4 @@ | ||||
| import { parseLocation, timestamp, uniqId, waitForLoad } from './utils'; | ||||
| import { notify, parseLocation, timestamp, uniqId, waitForLoad } from './utils'; | ||||
| import { Scheduler } from './Scheduler'; | ||||
| import { BuildingPageController } from './Page/BuildingPageController'; | ||||
| import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask'; | ||||
| @@ -13,21 +13,35 @@ import Vue from 'vue'; | ||||
| import DashboardApp from './DashboardView/Dashboard.vue'; | ||||
| import { ResourcesToLevel } from './Task/ResourcesToLevel'; | ||||
| import { ConsoleLogger, Logger } from './Logger'; | ||||
| import { VillageStorage } from './Storage/VillageStorage'; | ||||
| import { Resources } from './Core/Resources'; | ||||
| import { Coordinates, Village } from './Core/Village'; | ||||
| import { calcGatheringTimings } from './Core/GatheringTimings'; | ||||
| import { DataStorage } from './DataStorage'; | ||||
| import { getBuildingPageAttributes, isBuildingPage } from './Page/PageDetectors'; | ||||
| import { debounce } from 'debounce'; | ||||
| import { ExecutionStorage } from './Storage/ExecutionStorage'; | ||||
| import { ResourceStorage } from './Core/ResourceStorage'; | ||||
| import { createVillageStates, VillageState } from './VillageState'; | ||||
| import { Task } from './Queue/TaskProvider'; | ||||
| import { Action } from './Queue/ActionQueue'; | ||||
|  | ||||
| interface QuickAction { | ||||
|     label: string; | ||||
|     cb: () => void; | ||||
| } | ||||
|  | ||||
| interface GameState { | ||||
|     name: string; | ||||
|     version: string; | ||||
|     activeVillageState: VillageState | undefined; | ||||
|     villageStates: ReadonlyArray<VillageState>; | ||||
|     taskList: ReadonlyArray<Task>; | ||||
|     actionList: ReadonlyArray<Action>; | ||||
|     quickActions: Array<QuickAction>; | ||||
|     pauseSeconds: number; | ||||
|  | ||||
|     refresh(): void; | ||||
|     removeTask(taskId: string): void; | ||||
|     refreshVillages(): void; | ||||
|     pause(): void; | ||||
| } | ||||
|  | ||||
| export class ControlPanel { | ||||
|     private readonly version: string; | ||||
|     private readonly scheduler: Scheduler; | ||||
| @@ -48,18 +62,17 @@ export class ControlPanel { | ||||
|         const villageId = grabActiveVillageId(); | ||||
|  | ||||
|         const scheduler = this.scheduler; | ||||
|         const quickActions: QuickAction[] = []; | ||||
|  | ||||
|         const executionState = new ExecutionStorage(); | ||||
|  | ||||
|         const state: any = { | ||||
|             name: 'Dashboard', | ||||
|         const state: GameState = { | ||||
|             name: 'Control', | ||||
|             version: this.version, | ||||
|             activeVillage: {}, | ||||
|             villages: [], | ||||
|             activeVillageState: undefined, | ||||
|             villageStates: [], | ||||
|             taskList: [], | ||||
|             actionList: [], | ||||
|             quickActions: quickActions, | ||||
|             quickActions: [], | ||||
|             pauseSeconds: 0, | ||||
|  | ||||
|             refresh() { | ||||
| @@ -76,12 +89,10 @@ export class ControlPanel { | ||||
|             }, | ||||
|  | ||||
|             refreshVillages() { | ||||
|                 this.villages = grabVillageList().map(village => { | ||||
|                     return new VillageController(village, new VillageStorage(village.id), scheduler); | ||||
|                 }); | ||||
|                 for (let village of this.villages) { | ||||
|                     if (village.active) { | ||||
|                         this.activeVillage = village; | ||||
|                 this.villageStates = createVillageStates(grabVillageList(), scheduler); | ||||
|                 for (let state of this.villageStates) { | ||||
|                     if (state.village.active) { | ||||
|                         this.activeVillageState = state; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
| @@ -111,7 +122,7 @@ export class ControlPanel { | ||||
|         if (p.pathname === '/dorf1.php') { | ||||
|             showResourceSlotIds(buildingsInQueue); | ||||
|             onResourceSlotCtrlClick(buildId => this.onResourceSlotCtrlClick(villageId, buildId)); | ||||
|             quickActions.push(...this.createDepositsQuickActions(villageId)); | ||||
|             state.quickActions.push(...this.createDepositsQuickActions(villageId)); | ||||
|         } | ||||
|  | ||||
|         if (p.pathname === '/dorf2.php') { | ||||
| @@ -126,12 +137,12 @@ export class ControlPanel { | ||||
|         this.createControlPanel(state); | ||||
|     } | ||||
|  | ||||
|     private createControlPanel(state: any) { | ||||
|     private createControlPanel(gameState: GameState) { | ||||
|         const appId = `app-${uniqId()}`; | ||||
|         jQuery('body').prepend(`<div id="${appId}"></div>`); | ||||
|         new Vue({ | ||||
|             el: `#${appId}`, | ||||
|             data: state, | ||||
|             data: gameState, | ||||
|             render: h => h(DashboardApp), | ||||
|         }); | ||||
|     } | ||||
| @@ -157,78 +168,6 @@ export class ControlPanel { | ||||
|  | ||||
|     private onResourceSlotCtrlClick(villageId: number, buildId: number) { | ||||
|         this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId }); | ||||
|         const n = new Notification(`Building ${buildId} scheduled`); | ||||
|         setTimeout(() => n && n.close(), 4000); | ||||
|     } | ||||
| } | ||||
|  | ||||
| class VillageController { | ||||
|     public readonly id: number; | ||||
|     public readonly name: string; | ||||
|     public readonly crd: Coordinates; | ||||
|     public readonly active: boolean; | ||||
|     public readonly lumber: number; | ||||
|     public readonly clay: number; | ||||
|     public readonly iron: number; | ||||
|     public readonly crop: number; | ||||
|     public readonly resources: Resources; | ||||
|     public readonly performance: Resources; | ||||
|     public readonly requiredResources: Resources; | ||||
|     public readonly requiredBalance: Resources; | ||||
|     public readonly totalRequiredResources: Resources; | ||||
|     public readonly totalRequiredBalance: Resources; | ||||
|     public readonly incomingResources: Resources; | ||||
|     public readonly storage: ResourceStorage; | ||||
|     public readonly warehouse: number; | ||||
|     public readonly granary: number; | ||||
|     public readonly buildRemainingSeconds: number; | ||||
|  | ||||
|     constructor(village: Village, state: VillageStorage, scheduler: Scheduler) { | ||||
|         const resources = state.getResources(); | ||||
|         const storage = state.getResourceStorage(); | ||||
|         const performance = state.getResourcesPerformance(); | ||||
|         const buildQueueInfo = state.getBuildingQueueInfo(); | ||||
|         const requiredResources = scheduler.getVillageRequiredResources(village.id); | ||||
|         const totalRequiredResources = scheduler.getTotalVillageRequiredResources(village.id); | ||||
|         this.id = village.id; | ||||
|         this.name = village.name; | ||||
|         this.crd = village.crd; | ||||
|         this.active = village.active; | ||||
|         this.lumber = resources.lumber; | ||||
|         this.clay = resources.clay; | ||||
|         this.iron = resources.iron; | ||||
|         this.crop = resources.crop; | ||||
|         this.resources = resources; | ||||
|         this.performance = performance; | ||||
|         this.requiredResources = requiredResources; | ||||
|         this.requiredBalance = resources.sub(requiredResources); | ||||
|         this.totalRequiredResources = totalRequiredResources; | ||||
|         this.totalRequiredBalance = resources.sub(totalRequiredResources); | ||||
|         this.storage = storage; | ||||
|         this.warehouse = storage.warehouse; | ||||
|         this.granary = storage.granary; | ||||
|         this.buildRemainingSeconds = buildQueueInfo.seconds; | ||||
|         this.incomingResources = this.calcIncomingResources(state); | ||||
|     } | ||||
|  | ||||
|     timeToRequired() { | ||||
|         return this.timeToResources(this.requiredResources); | ||||
|     } | ||||
|  | ||||
|     timeToTotalRequired() { | ||||
|         return this.timeToResources(this.totalRequiredResources); | ||||
|     } | ||||
|  | ||||
|     private timeToResources(resources: Resources): number { | ||||
|         const timings = calcGatheringTimings(this.resources, resources, this.performance); | ||||
|         if (timings.never) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         return timings.hours * 3600; | ||||
|     } | ||||
|  | ||||
|     private calcIncomingResources(state: VillageStorage): Resources { | ||||
|         return state.getIncomingMerchants().reduce((m, i) => m.add(i.resources), new Resources(0, 0, 0, 0)); | ||||
|         notify(`Building ${buildId} scheduled`); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -19,8 +19,8 @@ export default { | ||||
|   }, | ||||
|   computed: { | ||||
|     villageName() { | ||||
|       let village = this.shared.activeVillage; | ||||
|       return village ? village.name : 'Unknown'; | ||||
|       let state = this.shared.activeVillageState; | ||||
|       return state ? state.village.name : 'Unknown'; | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|   | ||||
| @@ -25,13 +25,12 @@ | ||||
|  | ||||
| <script> | ||||
| import * as dateFormat from 'dateformat'; | ||||
| import { timestamp } from '../utils'; | ||||
|  | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       shared: this.$root.$data, | ||||
|       activeVillage: this.$root.$data.activeVillage, | ||||
|       activeVillageState: this.$root.$data.activeVillageState, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
| @@ -49,7 +48,7 @@ export default { | ||||
|     }, | ||||
|     isThisVillageTask(task) { | ||||
|       const taskVillageId = (task.args || {}).villageId; | ||||
|       const currentVillageId = this.activeVillage.id; | ||||
|       const currentVillageId = this.activeVillageState.id; | ||||
|       return taskVillageId !== undefined && taskVillageId === currentVillageId; | ||||
|     }, | ||||
|     onRemove(taskId) { | ||||
|   | ||||
| @@ -13,39 +13,57 @@ | ||||
|         </tr> | ||||
|       </thead> | ||||
|       <tbody> | ||||
|         <template v-for="village in shared.villages"> | ||||
|         <template v-for="villageState in shared.villageStates"> | ||||
|           <tr class="normal-line top-line"> | ||||
|             <td :class="{ active: village.active }" :title="village.id">{{ village.name }}</td> | ||||
|             <td class="right"> | ||||
|               <filling :value="village.lumber" :max="village.warehouse" :speed="village.performance.lumber"></filling> | ||||
|             <td :class="{ active: villageState.village.active }" :title="villageState.id"> | ||||
|               {{ villageState.village.name }} | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <filling :value="village.clay" :max="village.warehouse" :speed="village.performance.clay"></filling> | ||||
|               <filling | ||||
|                 :value="villageState.resources.lumber" | ||||
|                 :max="villageState.storage.lumber" | ||||
|                 :speed="villageState.performance.lumber" | ||||
|               ></filling> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <filling :value="village.iron" :max="village.warehouse" :speed="village.performance.iron"></filling> | ||||
|               <filling | ||||
|                 :value="villageState.resources.clay" | ||||
|                 :max="villageState.storage.clay" | ||||
|                 :speed="villageState.performance.clay" | ||||
|               ></filling> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <filling :value="village.crop" :max="village.granary" :speed="village.performance.crop"></filling> | ||||
|               <filling | ||||
|                 :value="villageState.resources.iron" | ||||
|                 :max="villageState.storage.iron" | ||||
|                 :speed="villageState.performance.iron" | ||||
|               ></filling> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <a :href="warehousePath(village)" v-text="village.warehouse"></a> | ||||
|               <filling | ||||
|                 :value="villageState.resources.crop" | ||||
|                 :max="villageState.storage.crop" | ||||
|                 :speed="villageState.performance.crop" | ||||
|               ></filling> | ||||
|             </td> | ||||
|             <td class="right" v-text="village.granary"></td> | ||||
|             <td class="right"> | ||||
|               <a :href="warehousePath(villageState.village)" v-text="villageState.storage.lumber"></a> | ||||
|             </td> | ||||
|             <td class="right" v-text="villageState.storage.crop"></td> | ||||
|           </tr> | ||||
|           <tr class="performance-line"> | ||||
|             <td class="right">Прирост:</td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.performance.lumber"></resource> | ||||
|               <resource :value="villageState.performance.lumber"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.performance.clay"></resource> | ||||
|               <resource :value="villageState.performance.clay"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.performance.iron"></resource> | ||||
|               <resource :value="villageState.performance.iron"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.performance.crop"></resource> | ||||
|               <resource :value="villageState.performance.crop"></resource> | ||||
|             </td> | ||||
|             <td></td> | ||||
|             <td></td> | ||||
| @@ -54,7 +72,7 @@ | ||||
|             <td class="right">След:</td> | ||||
|             <td class="right"> | ||||
|               <resource | ||||
|                 :value="village.requiredResources.lumber" | ||||
|                 :value="villageState.required.resources.lumber" | ||||
|                 :hide-zero="true" | ||||
|                 :color="false" | ||||
|                 :sign="false" | ||||
| @@ -62,7 +80,7 @@ | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource | ||||
|                 :value="village.requiredResources.clay" | ||||
|                 :value="villageState.required.resources.clay" | ||||
|                 :hide-zero="true" | ||||
|                 :color="false" | ||||
|                 :sign="false" | ||||
| @@ -70,7 +88,7 @@ | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource | ||||
|                 :value="village.requiredResources.iron" | ||||
|                 :value="villageState.required.resources.iron" | ||||
|                 :hide-zero="true" | ||||
|                 :color="false" | ||||
|                 :sign="false" | ||||
| @@ -78,62 +96,79 @@ | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource | ||||
|                 :value="village.requiredResources.crop" | ||||
|                 :value="villageState.required.resources.crop" | ||||
|                 :hide-zero="true" | ||||
|                 :color="false" | ||||
|                 :sign="false" | ||||
|               ></resource> | ||||
|             </td> | ||||
|             <td class="right" v-text="secondsToTime(village.buildRemainingSeconds)"></td> | ||||
|             <td class="right" v-text="secondsToRequiredTime(villageState.buildRemainingSeconds)"></td> | ||||
|             <td></td> | ||||
|           </tr> | ||||
|           <tr class="required-line"> | ||||
|             <td class="right">Баланс:</td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.requiredBalance.lumber"></resource> | ||||
|               <resource :value="villageState.required.balance.lumber"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.requiredBalance.clay"></resource> | ||||
|               <resource :value="villageState.required.balance.clay"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.requiredBalance.iron"></resource> | ||||
|               <resource :value="villageState.required.balance.iron"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.requiredBalance.crop"></resource> | ||||
|               <resource :value="villageState.required.balance.crop"></resource> | ||||
|             </td> | ||||
|             <td class="right" v-text="timeToRequired(village)"></td> | ||||
|             <td class="right" v-text="secondsToRequiredTime(villageState.required.time)"></td> | ||||
|             <td></td> | ||||
|           </tr> | ||||
|           <tr class="required-line"> | ||||
|             <td class="right">Баланс очереди:</td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.totalRequiredBalance.lumber"></resource> | ||||
|               <resource :value="villageState.totalRequired.balance.lumber"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.totalRequiredBalance.clay"></resource> | ||||
|               <resource :value="villageState.totalRequired.balance.clay"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.totalRequiredBalance.iron"></resource> | ||||
|               <resource :value="villageState.totalRequired.balance.iron"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.totalRequiredBalance.crop"></resource> | ||||
|               <resource :value="villageState.totalRequired.balance.crop"></resource> | ||||
|             </td> | ||||
|             <td class="right" v-text="timeToTotalRequired(village)"></td> | ||||
|             <td class="right" v-text="secondsToRequiredTime(villageState.totalRequired.time)"></td> | ||||
|             <td></td> | ||||
|           </tr> | ||||
|           <tr class="commitments-line"> | ||||
|             <td class="right">Обязательства:</td> | ||||
|             <td class="right"> | ||||
|               <resource :value="villageState.commitments.lumber" :hide-zero="true"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="villageState.commitments.clay" :hide-zero="true"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="villageState.commitments.iron" :hide-zero="true"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="villageState.commitments.crop" :hide-zero="true"></resource> | ||||
|             </td> | ||||
|             <td></td> | ||||
|             <td></td> | ||||
|           </tr> | ||||
|           <tr class="incoming-line"> | ||||
|             <td class="right">Торговцы:</td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.incomingResources.lumber" :hide-zero="true"></resource> | ||||
|               <resource :value="villageState.incomingResources.lumber" :hide-zero="true"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.incomingResources.clay" :hide-zero="true"></resource> | ||||
|               <resource :value="villageState.incomingResources.clay" :hide-zero="true"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.incomingResources.iron" :hide-zero="true"></resource> | ||||
|               <resource :value="villageState.incomingResources.iron" :hide-zero="true"></resource> | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <resource :value="village.incomingResources.crop" :hide-zero="true"></resource> | ||||
|               <resource :value="villageState.incomingResources.crop" :hide-zero="true"></resource> | ||||
|             </td> | ||||
|             <td></td> | ||||
|             <td></td> | ||||
| @@ -143,14 +178,14 @@ | ||||
|             <td class="right" colspan="6"> | ||||
|               <a | ||||
|                 class="village-quick-link" | ||||
|                 v-for="v in shared.villages" | ||||
|                 v-if="v.id !== village.id" | ||||
|                 :href="marketPath(village, v)" | ||||
|                 :title="'Отправить ресурсы из ' + village.name + ' в ' + v.name" | ||||
|                 >->{{ v.name }}</a | ||||
|                 v-for="s in shared.villageStates" | ||||
|                 v-if="s.id !== villageState.id" | ||||
|                 :href="marketPath(villageState.village, s.village)" | ||||
|                 :title="'Отправить ресурсы из ' + villageState.village.name + ' в ' + s.village.name" | ||||
|                 >->{{ s.village.name }}</a | ||||
|               > | ||||
|               <a class="village-quick-link" :href="quartersPath(village)">Казармы</a> | ||||
|               <a class="village-quick-link" :href="horseStablePath(village)">Конюшни</a> | ||||
|               <a class="village-quick-link" :href="quartersPath(villageState.village)">Казармы</a> | ||||
|               <a class="village-quick-link" :href="horseStablePath(villageState.village)">Конюшни</a> | ||||
|             </td> | ||||
|           </tr> | ||||
|         </template> | ||||
| @@ -173,7 +208,7 @@ export default { | ||||
|   data() { | ||||
|     return { | ||||
|       shared: this.$root.$data, | ||||
|       activeVillage: this.$root.$data.activeVillage, | ||||
|       activeVillageState: this.$root.$data.activeVillageState, | ||||
|     }; | ||||
|   }, | ||||
|   methods: { | ||||
| @@ -213,12 +248,6 @@ export default { | ||||
|       } | ||||
|       return this.secondsToTime(value); | ||||
|     }, | ||||
|     timeToRequired(village) { | ||||
|       return this.secondsToRequiredTime(village.timeToRequired()); | ||||
|     }, | ||||
|     timeToTotalRequired(village) { | ||||
|       return this.secondsToRequiredTime(village.timeToTotalRequired()); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| @@ -244,6 +273,7 @@ export default { | ||||
|  | ||||
| .performance-line td, | ||||
| .required-line td, | ||||
| .commitments-line td, | ||||
| .incoming-line td { | ||||
|   padding: 0 4px 4px; | ||||
|   font-size: 90%; | ||||
|   | ||||
| @@ -6,6 +6,7 @@ export interface Args { | ||||
|     taskId?: TaskId; | ||||
|     targetTaskId?: TaskId; | ||||
|     villageId?: number; | ||||
|     targetVillageId?: number; | ||||
|     buildId?: number; | ||||
|     categoryId?: number; | ||||
|     sheetId?: number; | ||||
|   | ||||
| @@ -133,6 +133,18 @@ export class Scheduler { | ||||
|         return tasks.reduce((acc, t) => acc.add(t.args.resources!), new Resources(0, 0, 0, 0)); | ||||
|     } | ||||
|  | ||||
|     getResourceCommitments(villageId: number): Array<number> { | ||||
|         const tasks = this.taskQueue | ||||
|             .seeItems() | ||||
|             .filter( | ||||
|                 t => | ||||
|                     t.name === SendResourcesTask.name && | ||||
|                     t.args.villageId === villageId && | ||||
|                     t.args.targetVillageId !== undefined | ||||
|             ); | ||||
|         return tasks.map(t => t.args.targetVillageId!); | ||||
|     } | ||||
|  | ||||
|     private reorderVillageTasks(villageId: number) { | ||||
|         const tasks = this.taskQueue.seeItems(); | ||||
|         const trainPred = (t: Task) => isTrainTroopTask(t.name) && sameVillage(villageId, t.args); | ||||
|   | ||||
							
								
								
									
										100
									
								
								src/VillageState.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/VillageState.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| import { Village } from './Core/Village'; | ||||
| import { Scheduler } from './Scheduler'; | ||||
| import { Resources } from './Core/Resources'; | ||||
| import { VillageStorage } from './Storage/VillageStorage'; | ||||
| import { calcGatheringTimings } from './Core/GatheringTimings'; | ||||
|  | ||||
| interface RequiredResources { | ||||
|     resources: Resources; | ||||
|     balance: Resources; | ||||
|     time: number; | ||||
| } | ||||
|  | ||||
| interface VillageOwnState { | ||||
|     id: number; | ||||
|     village: Village; | ||||
|     resources: Resources; | ||||
|     performance: Resources; | ||||
|     required: RequiredResources; | ||||
|     totalRequired: RequiredResources; | ||||
|     incomingResources: Resources; | ||||
|     storage: Resources; | ||||
|     buildRemainingSeconds: number; | ||||
| } | ||||
|  | ||||
| interface VillageOwnStateDictionary { | ||||
|     [id: number]: VillageOwnState; | ||||
| } | ||||
|  | ||||
| export interface VillageState extends VillageOwnState { | ||||
|     commitments: Resources; | ||||
| } | ||||
|  | ||||
| function calcResourceBalance(resources: Resources, current: Resources, performance: Resources): RequiredResources { | ||||
|     return { | ||||
|         resources: resources, | ||||
|         balance: current.sub(resources), | ||||
|         time: timeToResources(current, resources, performance), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| function timeToResources(current: Resources, desired: Resources, performance: Resources): number { | ||||
|     const timings = calcGatheringTimings(current, desired, performance); | ||||
|     if (timings.never) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return timings.hours * 3600; | ||||
| } | ||||
|  | ||||
| function calcIncomingResources(storage: VillageStorage): Resources { | ||||
|     return storage.getIncomingMerchants().reduce((m, i) => m.add(i.resources), Resources.zero()); | ||||
| } | ||||
|  | ||||
| function createVillageOwnState(village: Village, scheduler: Scheduler): VillageOwnState { | ||||
|     const storage = new VillageStorage(village.id); | ||||
|     const resources = storage.getResources(); | ||||
|     const resourceStorage = storage.getResourceStorage(); | ||||
|     const performance = storage.getResourcesPerformance(); | ||||
|     const buildQueueInfo = storage.getBuildingQueueInfo(); | ||||
|     const requiredResources = scheduler.getVillageRequiredResources(village.id); | ||||
|     const totalRequiredResources = scheduler.getTotalVillageRequiredResources(village.id); | ||||
|  | ||||
|     return { | ||||
|         id: village.id, | ||||
|         village, | ||||
|         resources, | ||||
|         performance, | ||||
|         required: calcResourceBalance(requiredResources, resources, performance), | ||||
|         totalRequired: calcResourceBalance(totalRequiredResources, resources, performance), | ||||
|         storage: Resources.fromStorage(resourceStorage), | ||||
|         buildRemainingSeconds: buildQueueInfo.seconds, | ||||
|         incomingResources: calcIncomingResources(storage), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| function createVillageOwnStates(villages: Array<Village>, scheduler: Scheduler): VillageOwnStateDictionary { | ||||
|     const result: VillageOwnStateDictionary = {}; | ||||
|     for (let village of villages) { | ||||
|         result[village.id] = createVillageOwnState(village, scheduler); | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| function createVillageState( | ||||
|     state: VillageOwnState, | ||||
|     ownStates: VillageOwnStateDictionary, | ||||
|     scheduler: Scheduler | ||||
| ): VillageState { | ||||
|     const villageIds = scheduler.getResourceCommitments(state.id); | ||||
|     const commitments = villageIds.reduce( | ||||
|         (res, villageId) => res.add(ownStates[villageId].required.balance), | ||||
|         Resources.zero() | ||||
|     ); | ||||
|     return { ...state, commitments }; | ||||
| } | ||||
|  | ||||
| export function createVillageStates(villages: Array<Village>, scheduler: Scheduler): Array<VillageState> { | ||||
|     const ownStates = createVillageOwnStates(villages, scheduler); | ||||
|     return villages.map(village => createVillageState(ownStates[village.id], ownStates, scheduler)); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user