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 { Scheduler } from './Scheduler'; | ||||||
| import { BuildingPageController } from './Page/BuildingPageController'; | import { BuildingPageController } from './Page/BuildingPageController'; | ||||||
| import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask'; | import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask'; | ||||||
| @@ -13,21 +13,35 @@ import Vue from 'vue'; | |||||||
| import DashboardApp from './DashboardView/Dashboard.vue'; | import DashboardApp from './DashboardView/Dashboard.vue'; | ||||||
| import { ResourcesToLevel } from './Task/ResourcesToLevel'; | import { ResourcesToLevel } from './Task/ResourcesToLevel'; | ||||||
| import { ConsoleLogger, Logger } from './Logger'; | 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 { DataStorage } from './DataStorage'; | ||||||
| import { getBuildingPageAttributes, isBuildingPage } from './Page/PageDetectors'; | import { getBuildingPageAttributes, isBuildingPage } from './Page/PageDetectors'; | ||||||
| import { debounce } from 'debounce'; | import { debounce } from 'debounce'; | ||||||
| import { ExecutionStorage } from './Storage/ExecutionStorage'; | 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 { | interface QuickAction { | ||||||
|     label: string; |     label: string; | ||||||
|     cb: () => void; |     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 { | export class ControlPanel { | ||||||
|     private readonly version: string; |     private readonly version: string; | ||||||
|     private readonly scheduler: Scheduler; |     private readonly scheduler: Scheduler; | ||||||
| @@ -48,18 +62,17 @@ export class ControlPanel { | |||||||
|         const villageId = grabActiveVillageId(); |         const villageId = grabActiveVillageId(); | ||||||
|  |  | ||||||
|         const scheduler = this.scheduler; |         const scheduler = this.scheduler; | ||||||
|         const quickActions: QuickAction[] = []; |  | ||||||
|  |  | ||||||
|         const executionState = new ExecutionStorage(); |         const executionState = new ExecutionStorage(); | ||||||
|  |  | ||||||
|         const state: any = { |         const state: GameState = { | ||||||
|             name: 'Dashboard', |             name: 'Control', | ||||||
|             version: this.version, |             version: this.version, | ||||||
|             activeVillage: {}, |             activeVillageState: undefined, | ||||||
|             villages: [], |             villageStates: [], | ||||||
|             taskList: [], |             taskList: [], | ||||||
|             actionList: [], |             actionList: [], | ||||||
|             quickActions: quickActions, |             quickActions: [], | ||||||
|             pauseSeconds: 0, |             pauseSeconds: 0, | ||||||
|  |  | ||||||
|             refresh() { |             refresh() { | ||||||
| @@ -76,12 +89,10 @@ export class ControlPanel { | |||||||
|             }, |             }, | ||||||
|  |  | ||||||
|             refreshVillages() { |             refreshVillages() { | ||||||
|                 this.villages = grabVillageList().map(village => { |                 this.villageStates = createVillageStates(grabVillageList(), scheduler); | ||||||
|                     return new VillageController(village, new VillageStorage(village.id), scheduler); |                 for (let state of this.villageStates) { | ||||||
|                 }); |                     if (state.village.active) { | ||||||
|                 for (let village of this.villages) { |                         this.activeVillageState = state; | ||||||
|                     if (village.active) { |  | ||||||
|                         this.activeVillage = village; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
| @@ -111,7 +122,7 @@ export class ControlPanel { | |||||||
|         if (p.pathname === '/dorf1.php') { |         if (p.pathname === '/dorf1.php') { | ||||||
|             showResourceSlotIds(buildingsInQueue); |             showResourceSlotIds(buildingsInQueue); | ||||||
|             onResourceSlotCtrlClick(buildId => this.onResourceSlotCtrlClick(villageId, buildId)); |             onResourceSlotCtrlClick(buildId => this.onResourceSlotCtrlClick(villageId, buildId)); | ||||||
|             quickActions.push(...this.createDepositsQuickActions(villageId)); |             state.quickActions.push(...this.createDepositsQuickActions(villageId)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (p.pathname === '/dorf2.php') { |         if (p.pathname === '/dorf2.php') { | ||||||
| @@ -126,12 +137,12 @@ export class ControlPanel { | |||||||
|         this.createControlPanel(state); |         this.createControlPanel(state); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private createControlPanel(state: any) { |     private createControlPanel(gameState: GameState) { | ||||||
|         const appId = `app-${uniqId()}`; |         const appId = `app-${uniqId()}`; | ||||||
|         jQuery('body').prepend(`<div id="${appId}"></div>`); |         jQuery('body').prepend(`<div id="${appId}"></div>`); | ||||||
|         new Vue({ |         new Vue({ | ||||||
|             el: `#${appId}`, |             el: `#${appId}`, | ||||||
|             data: state, |             data: gameState, | ||||||
|             render: h => h(DashboardApp), |             render: h => h(DashboardApp), | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @@ -157,78 +168,6 @@ export class ControlPanel { | |||||||
|  |  | ||||||
|     private onResourceSlotCtrlClick(villageId: number, buildId: number) { |     private onResourceSlotCtrlClick(villageId: number, buildId: number) { | ||||||
|         this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId }); |         this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId }); | ||||||
|         const n = new Notification(`Building ${buildId} scheduled`); |         notify(`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)); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,8 +19,8 @@ export default { | |||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     villageName() { |     villageName() { | ||||||
|       let village = this.shared.activeVillage; |       let state = this.shared.activeVillageState; | ||||||
|       return village ? village.name : 'Unknown'; |       return state ? state.village.name : 'Unknown'; | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|   | |||||||
| @@ -25,13 +25,12 @@ | |||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import * as dateFormat from 'dateformat'; | import * as dateFormat from 'dateformat'; | ||||||
| import { timestamp } from '../utils'; |  | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       shared: this.$root.$data, |       shared: this.$root.$data, | ||||||
|       activeVillage: this.$root.$data.activeVillage, |       activeVillageState: this.$root.$data.activeVillageState, | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
| @@ -49,7 +48,7 @@ export default { | |||||||
|     }, |     }, | ||||||
|     isThisVillageTask(task) { |     isThisVillageTask(task) { | ||||||
|       const taskVillageId = (task.args || {}).villageId; |       const taskVillageId = (task.args || {}).villageId; | ||||||
|       const currentVillageId = this.activeVillage.id; |       const currentVillageId = this.activeVillageState.id; | ||||||
|       return taskVillageId !== undefined && taskVillageId === currentVillageId; |       return taskVillageId !== undefined && taskVillageId === currentVillageId; | ||||||
|     }, |     }, | ||||||
|     onRemove(taskId) { |     onRemove(taskId) { | ||||||
|   | |||||||
| @@ -13,39 +13,57 @@ | |||||||
|         </tr> |         </tr> | ||||||
|       </thead> |       </thead> | ||||||
|       <tbody> |       <tbody> | ||||||
|         <template v-for="village in shared.villages"> |         <template v-for="villageState in shared.villageStates"> | ||||||
|           <tr class="normal-line top-line"> |           <tr class="normal-line top-line"> | ||||||
|             <td :class="{ active: village.active }" :title="village.id">{{ village.name }}</td> |             <td :class="{ active: villageState.village.active }" :title="villageState.id"> | ||||||
|             <td class="right"> |               {{ villageState.village.name }} | ||||||
|               <filling :value="village.lumber" :max="village.warehouse" :speed="village.performance.lumber"></filling> |  | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <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> | ||||||
|             <td class="right"> |             <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> | ||||||
|             <td class="right"> |             <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> | ||||||
|             <td class="right"> |             <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> | ||||||
|             <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> | ||||||
|           <tr class="performance-line"> |           <tr class="performance-line"> | ||||||
|             <td class="right">Прирост:</td> |             <td class="right">Прирост:</td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.performance.lumber"></resource> |               <resource :value="villageState.performance.lumber"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.performance.clay"></resource> |               <resource :value="villageState.performance.clay"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.performance.iron"></resource> |               <resource :value="villageState.performance.iron"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.performance.crop"></resource> |               <resource :value="villageState.performance.crop"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td></td> |             <td></td> | ||||||
|             <td></td> |             <td></td> | ||||||
| @@ -54,7 +72,7 @@ | |||||||
|             <td class="right">След:</td> |             <td class="right">След:</td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource |               <resource | ||||||
|                 :value="village.requiredResources.lumber" |                 :value="villageState.required.resources.lumber" | ||||||
|                 :hide-zero="true" |                 :hide-zero="true" | ||||||
|                 :color="false" |                 :color="false" | ||||||
|                 :sign="false" |                 :sign="false" | ||||||
| @@ -62,7 +80,7 @@ | |||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource |               <resource | ||||||
|                 :value="village.requiredResources.clay" |                 :value="villageState.required.resources.clay" | ||||||
|                 :hide-zero="true" |                 :hide-zero="true" | ||||||
|                 :color="false" |                 :color="false" | ||||||
|                 :sign="false" |                 :sign="false" | ||||||
| @@ -70,7 +88,7 @@ | |||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource |               <resource | ||||||
|                 :value="village.requiredResources.iron" |                 :value="villageState.required.resources.iron" | ||||||
|                 :hide-zero="true" |                 :hide-zero="true" | ||||||
|                 :color="false" |                 :color="false" | ||||||
|                 :sign="false" |                 :sign="false" | ||||||
| @@ -78,62 +96,79 @@ | |||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource |               <resource | ||||||
|                 :value="village.requiredResources.crop" |                 :value="villageState.required.resources.crop" | ||||||
|                 :hide-zero="true" |                 :hide-zero="true" | ||||||
|                 :color="false" |                 :color="false" | ||||||
|                 :sign="false" |                 :sign="false" | ||||||
|               ></resource> |               ></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right" v-text="secondsToTime(village.buildRemainingSeconds)"></td> |             <td class="right" v-text="secondsToRequiredTime(villageState.buildRemainingSeconds)"></td> | ||||||
|             <td></td> |             <td></td> | ||||||
|           </tr> |           </tr> | ||||||
|           <tr class="required-line"> |           <tr class="required-line"> | ||||||
|             <td class="right">Баланс:</td> |             <td class="right">Баланс:</td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.requiredBalance.lumber"></resource> |               <resource :value="villageState.required.balance.lumber"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.requiredBalance.clay"></resource> |               <resource :value="villageState.required.balance.clay"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.requiredBalance.iron"></resource> |               <resource :value="villageState.required.balance.iron"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.requiredBalance.crop"></resource> |               <resource :value="villageState.required.balance.crop"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right" v-text="timeToRequired(village)"></td> |             <td class="right" v-text="secondsToRequiredTime(villageState.required.time)"></td> | ||||||
|             <td></td> |             <td></td> | ||||||
|           </tr> |           </tr> | ||||||
|           <tr class="required-line"> |           <tr class="required-line"> | ||||||
|             <td class="right">Баланс очереди:</td> |             <td class="right">Баланс очереди:</td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.totalRequiredBalance.lumber"></resource> |               <resource :value="villageState.totalRequired.balance.lumber"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.totalRequiredBalance.clay"></resource> |               <resource :value="villageState.totalRequired.balance.clay"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.totalRequiredBalance.iron"></resource> |               <resource :value="villageState.totalRequired.balance.iron"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.totalRequiredBalance.crop"></resource> |               <resource :value="villageState.totalRequired.balance.crop"></resource> | ||||||
|             </td> |             </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> |             <td></td> | ||||||
|           </tr> |           </tr> | ||||||
|           <tr class="incoming-line"> |           <tr class="incoming-line"> | ||||||
|             <td class="right">Торговцы:</td> |             <td class="right">Торговцы:</td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.incomingResources.lumber" :hide-zero="true"></resource> |               <resource :value="villageState.incomingResources.lumber" :hide-zero="true"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.incomingResources.clay" :hide-zero="true"></resource> |               <resource :value="villageState.incomingResources.clay" :hide-zero="true"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <td class="right"> | ||||||
|               <resource :value="village.incomingResources.iron" :hide-zero="true"></resource> |               <resource :value="villageState.incomingResources.iron" :hide-zero="true"></resource> | ||||||
|             </td> |             </td> | ||||||
|             <td class="right"> |             <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></td> | ||||||
|             <td></td> |             <td></td> | ||||||
| @@ -143,14 +178,14 @@ | |||||||
|             <td class="right" colspan="6"> |             <td class="right" colspan="6"> | ||||||
|               <a |               <a | ||||||
|                 class="village-quick-link" |                 class="village-quick-link" | ||||||
|                 v-for="v in shared.villages" |                 v-for="s in shared.villageStates" | ||||||
|                 v-if="v.id !== village.id" |                 v-if="s.id !== villageState.id" | ||||||
|                 :href="marketPath(village, v)" |                 :href="marketPath(villageState.village, s.village)" | ||||||
|                 :title="'Отправить ресурсы из ' + village.name + ' в ' + v.name" |                 :title="'Отправить ресурсы из ' + villageState.village.name + ' в ' + s.village.name" | ||||||
|                 >->{{ v.name }}</a |                 >->{{ s.village.name }}</a | ||||||
|               > |               > | ||||||
|               <a class="village-quick-link" :href="quartersPath(village)">Казармы</a> |               <a class="village-quick-link" :href="quartersPath(villageState.village)">Казармы</a> | ||||||
|               <a class="village-quick-link" :href="horseStablePath(village)">Конюшни</a> |               <a class="village-quick-link" :href="horseStablePath(villageState.village)">Конюшни</a> | ||||||
|             </td> |             </td> | ||||||
|           </tr> |           </tr> | ||||||
|         </template> |         </template> | ||||||
| @@ -173,7 +208,7 @@ export default { | |||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       shared: this.$root.$data, |       shared: this.$root.$data, | ||||||
|       activeVillage: this.$root.$data.activeVillage, |       activeVillageState: this.$root.$data.activeVillageState, | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
| @@ -213,12 +248,6 @@ export default { | |||||||
|       } |       } | ||||||
|       return this.secondsToTime(value); |       return this.secondsToTime(value); | ||||||
|     }, |     }, | ||||||
|     timeToRequired(village) { |  | ||||||
|       return this.secondsToRequiredTime(village.timeToRequired()); |  | ||||||
|     }, |  | ||||||
|     timeToTotalRequired(village) { |  | ||||||
|       return this.secondsToRequiredTime(village.timeToTotalRequired()); |  | ||||||
|     }, |  | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
| @@ -244,6 +273,7 @@ export default { | |||||||
|  |  | ||||||
| .performance-line td, | .performance-line td, | ||||||
| .required-line td, | .required-line td, | ||||||
|  | .commitments-line td, | ||||||
| .incoming-line td { | .incoming-line td { | ||||||
|   padding: 0 4px 4px; |   padding: 0 4px 4px; | ||||||
|   font-size: 90%; |   font-size: 90%; | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ export interface Args { | |||||||
|     taskId?: TaskId; |     taskId?: TaskId; | ||||||
|     targetTaskId?: TaskId; |     targetTaskId?: TaskId; | ||||||
|     villageId?: number; |     villageId?: number; | ||||||
|  |     targetVillageId?: number; | ||||||
|     buildId?: number; |     buildId?: number; | ||||||
|     categoryId?: number; |     categoryId?: number; | ||||||
|     sheetId?: 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)); |         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) { |     private reorderVillageTasks(villageId: number) { | ||||||
|         const tasks = this.taskQueue.seeItems(); |         const tasks = this.taskQueue.seeItems(); | ||||||
|         const trainPred = (t: Task) => isTrainTroopTask(t.name) && sameVillage(villageId, t.args); |         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