Village settings
This commit is contained in:
		| @@ -8,9 +8,6 @@ import { Task } from '../Queue/TaskProvider'; | ||||
| import { clickSendButton, fillSendResourcesForm, grabMerchantsInfo } from '../Page/BuildingPage/MarketPage'; | ||||
| import { VillageState } from '../VillageState'; | ||||
|  | ||||
| const TIMEOUT = 15; | ||||
| const AMOUNT_THRESHOLD = 100; | ||||
|  | ||||
| @registerAction | ||||
| export class SendResourcesAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
| @@ -29,17 +26,18 @@ export class SendResourcesAction extends ActionController { | ||||
|         console.log('To transfer res', readyToTransfer); | ||||
|  | ||||
|         // Schedule recurrent task | ||||
|         this.scheduler.scheduleTask(task.name, task.args, timestamp() + aroundMinutes(TIMEOUT)); | ||||
|         const timeout = senderVillage.settings.sendResourcesTimeout; | ||||
|         this.scheduler.scheduleTask(task.name, task.args, timestamp() + aroundMinutes(timeout)); | ||||
|  | ||||
|         fillSendResourcesForm(readyToTransfer, coordinates); | ||||
|         clickSendButton(); | ||||
|     } | ||||
|  | ||||
|     private getMerchantsCapacity(): number { | ||||
|     private getMerchantsCapacity(timeout: number): number { | ||||
|         const merchants = grabMerchantsInfo(); | ||||
|         const capacity = merchants.available * merchants.carry; | ||||
|         if (!capacity) { | ||||
|             throw new TryLaterError(aroundMinutes(TIMEOUT), 'No merchants'); | ||||
|             throw new TryLaterError(aroundMinutes(timeout), 'No merchants'); | ||||
|         } | ||||
|         return capacity; | ||||
|     } | ||||
| @@ -53,13 +51,21 @@ export class SendResourcesAction extends ActionController { | ||||
|             { name: 'Sender free', ...free }, | ||||
|         ]); | ||||
|  | ||||
|         if (free.amount() < AMOUNT_THRESHOLD) { | ||||
|             throw new TryLaterError(aroundMinutes(TIMEOUT), 'Little free resources'); | ||||
|         const amount = free.amount(); | ||||
|         const threshold = senderState.settings.sendResourcesThreshold; | ||||
|         const timeout = senderState.settings.sendResourcesTimeout; | ||||
|  | ||||
|         if (amount < threshold) { | ||||
|             throw new TryLaterError( | ||||
|                 aroundMinutes(timeout), | ||||
|                 `No free resources (amount ${amount} < threshold ${threshold})` | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return free; | ||||
|     } | ||||
|  | ||||
|     private getRecipientRequirements(recipientState: VillageState): Resources { | ||||
|     private getRecipientRequirements(recipientState: VillageState, timeout: number): Resources { | ||||
|         const maxPossibleToStore = recipientState.storage.capacity.sub(recipientState.performance); | ||||
|         const currentResources = recipientState.resources; | ||||
|         const incomingResources = recipientState.incomingResources; | ||||
| @@ -79,17 +85,18 @@ export class SendResourcesAction extends ActionController { | ||||
|         ]); | ||||
|  | ||||
|         if (missingResources.empty()) { | ||||
|             throw new TryLaterError(aroundMinutes(TIMEOUT), 'No missing resources'); | ||||
|             throw new TryLaterError(aroundMinutes(timeout), 'No missing resources'); | ||||
|         } | ||||
|  | ||||
|         return missingResources; | ||||
|     } | ||||
|  | ||||
|     private getResourcesForTransfer(senderState: VillageState, recipientState: VillageState): Resources { | ||||
|         const timeout = senderState.settings.sendResourcesTimeout; | ||||
|         const senderReadySendResources = this.getSenderAvailableResources(senderState); | ||||
|         const recipientNeedsResources = this.getRecipientRequirements(recipientState); | ||||
|         const recipientNeedsResources = this.getRecipientRequirements(recipientState, timeout); | ||||
|         const contractResources = senderReadySendResources.min(recipientNeedsResources); | ||||
|         const merchantsCapacity = this.getMerchantsCapacity(); | ||||
|         const merchantsCapacity = this.getMerchantsCapacity(timeout); | ||||
|  | ||||
|         let readyToTransfer = contractResources; | ||||
|         if (contractResources.amount() > merchantsCapacity) { | ||||
|   | ||||
| @@ -155,16 +155,16 @@ export class ControlPanel { | ||||
|             buildPage.run(); | ||||
|         } | ||||
|  | ||||
|         this.createControlPanel(state); | ||||
|         this.createControlPanel(state, villageStateRepository); | ||||
|     } | ||||
|  | ||||
|     private createControlPanel(gameState: GameState) { | ||||
|     private createControlPanel(gameState: GameState, villageStateRepository: VillageStateRepository) { | ||||
|         const appId = `app-${uniqId()}`; | ||||
|         jQuery('body').prepend(`<div id="${appId}"></div>`); | ||||
|         new Vue({ | ||||
|             el: `#${appId}`, | ||||
|             data: gameState, | ||||
|             store: createStore(), | ||||
|             store: createStore(villageStateRepository), | ||||
|             render: h => h(DashboardApp), | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -37,7 +37,12 @@ export class Village { | ||||
|  | ||||
| export type VillageList = Array<Village>; | ||||
|  | ||||
| // export interface VillageSettings { | ||||
| //     id: number; | ||||
| // | ||||
| // } | ||||
| export interface VillageSettings { | ||||
|     sendResourcesThreshold: number; | ||||
|     sendResourcesTimeout: number; | ||||
| } | ||||
|  | ||||
| export const VillageSettingsDefaults: VillageSettings = { | ||||
|     sendResourcesTimeout: 15, | ||||
|     sendResourcesThreshold: 100, | ||||
| } as const; | ||||
|   | ||||
| @@ -8,8 +8,9 @@ | ||||
|       <hr class="separator" /> | ||||
|       <task-list /> | ||||
|     </section> | ||||
|     <section id="dashboard-secondary"> | ||||
|     <section id="dashboard-secondary" v-if="isSecondaryDashboardVisible"> | ||||
|       <log-list v-if="isLogsVisible" /> | ||||
|       <village-editor v-if="isVillageEditorVisible" /> | ||||
|     </section> | ||||
|   </main> | ||||
| </template> | ||||
| @@ -22,8 +23,10 @@ import VillageStateList from './VillageStateList'; | ||||
| import LogList from './LogList'; | ||||
| import { mapState } from 'vuex'; | ||||
| import { Mutations } from './Store'; | ||||
| import VillageEditor from './VillageEditor'; | ||||
| export default { | ||||
|   components: { | ||||
|     'village-editor': VillageEditor, | ||||
|     'hdr': Header, | ||||
|     'task-list': TaskList, | ||||
|     'quick-actions': QuickActions, | ||||
| @@ -35,9 +38,15 @@ export default { | ||||
|       shared: this.$root.$data, | ||||
|     }; | ||||
|   }, | ||||
|   computed: mapState({ | ||||
|     isLogsVisible: state => state.views.logs, | ||||
|   }), | ||||
|   computed: { | ||||
|     ...mapState({ | ||||
|       isLogsVisible: state => state.views.logs, | ||||
|       isVillageEditorVisible: state => state.views.villageEditor, | ||||
|     }), | ||||
|     isSecondaryDashboardVisible() { | ||||
|       return this.isLogsVisible || this.isVillageEditorVisible; | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     toggleLogs() { | ||||
|       this.$store.commit(Mutations.toggleLogs); | ||||
|   | ||||
| @@ -1,21 +1,45 @@ | ||||
| import Vuex from 'vuex'; | ||||
| import { StorageLogRecord } from '../Logger'; | ||||
| import { LogStorage } from '../Storage/LogStorage'; | ||||
| import { VillageStateRepository } from '../VillageState'; | ||||
| import { VillageSettings, VillageSettingsDefaults } from '../Core/Village'; | ||||
| import { getNumber, notify } from '../utils'; | ||||
| import { VillageStorage } from '../Storage/VillageStorage'; | ||||
|  | ||||
| export enum Mutations { | ||||
|     showLogs = 'showLogs', | ||||
|     hideLogs = 'hideLogs', | ||||
|     toggleLogs = 'toggleLogs', | ||||
|     updateLogs = 'updateLogs', | ||||
|     ToggleVillageEditor = 'toggle_village_editor', | ||||
|     SetVillageSettings = 'set_village_settings', | ||||
|     UpdateVillageSendResourceThreshold = 'UpdateVillageSendResourceThreshold', | ||||
|     UpdateVillageSendResourceTimeout = 'UpdateVillageSendResourceTimeout', | ||||
| } | ||||
|  | ||||
| export function createStore() { | ||||
| export enum Actions { | ||||
|     OpenVillageEditor = 'open_village_editor', | ||||
|     SaveVillageSettings = 'save_village_settings', | ||||
| } | ||||
|  | ||||
| export function createStore(villageStateRepository: VillageStateRepository) { | ||||
|     const store = new Vuex.Store({ | ||||
|         state: { | ||||
|             views: { | ||||
|                 villageEditor: false, | ||||
|                 logs: false, | ||||
|             }, | ||||
|             logs: [], | ||||
|             villageSettings: { | ||||
|                 villageId: 0, | ||||
|                 villageName: '', | ||||
|                 sendResourcesThreshold: 0, | ||||
|                 sendResourcesTimeout: 0, | ||||
|             }, | ||||
|         }, | ||||
|         getters: { | ||||
|             reverseLogs: state => { | ||||
|                 return state.logs.slice().reverse(); | ||||
|             }, | ||||
|         }, | ||||
|         mutations: { | ||||
|             [Mutations.showLogs](state) { | ||||
| @@ -30,10 +54,43 @@ export function createStore() { | ||||
|             [Mutations.updateLogs](state, { logs }) { | ||||
|                 state.logs = logs; | ||||
|             }, | ||||
|             [Mutations.ToggleVillageEditor](state, visible?: any) { | ||||
|                 state.views.villageEditor = visible === undefined ? !state.views.villageEditor : !!visible; | ||||
|             }, | ||||
|             [Mutations.SetVillageSettings](state, settings) { | ||||
|                 state.villageSettings = settings; | ||||
|             }, | ||||
|             [Mutations.UpdateVillageSendResourceThreshold](state, value) { | ||||
|                 state.villageSettings.sendResourcesThreshold = getNumber(value); | ||||
|             }, | ||||
|             [Mutations.UpdateVillageSendResourceTimeout](state, value) { | ||||
|                 state.villageSettings.sendResourcesTimeout = getNumber(value); | ||||
|             }, | ||||
|         }, | ||||
|         getters: { | ||||
|             reverseLogs: state => { | ||||
|                 return state.logs.slice().reverse(); | ||||
|         actions: { | ||||
|             [Actions.OpenVillageEditor]({ commit }, { villageId }) { | ||||
|                 const state = villageStateRepository.getVillageState(villageId); | ||||
|                 const settings = state.settings; | ||||
|                 commit(Mutations.SetVillageSettings, { | ||||
|                     villageId: state.id, | ||||
|                     villageName: state.village.name, | ||||
|                     sendResourcesThreshold: settings.sendResourcesThreshold, | ||||
|                     sendResourcesTimeout: settings.sendResourcesTimeout, | ||||
|                 }); | ||||
|                 commit(Mutations.ToggleVillageEditor, true); | ||||
|             }, | ||||
|             [Actions.SaveVillageSettings]({ state }) { | ||||
|                 const villageId = state.villageSettings.villageId; | ||||
|                 const villageName = state.villageSettings.villageName; | ||||
|                 const newSettings: VillageSettings = { | ||||
|                     sendResourcesThreshold: | ||||
|                         state.villageSettings.sendResourcesThreshold || VillageSettingsDefaults.sendResourcesThreshold, | ||||
|                     sendResourcesTimeout: | ||||
|                         state.villageSettings.sendResourcesTimeout || VillageSettingsDefaults.sendResourcesTimeout, | ||||
|                 }; | ||||
|                 const storage = new VillageStorage(villageId); | ||||
|                 storage.storeSettings(newSettings); | ||||
|                 notify(`Настройки для ${villageName} сохранены`); | ||||
|             }, | ||||
|         }, | ||||
|     }); | ||||
|   | ||||
							
								
								
									
										91
									
								
								src/DashboardView/VillageEditor.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/DashboardView/VillageEditor.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| <template> | ||||
|   <section class="village-editor"> | ||||
|     <p class="summary"> | ||||
|       Village Editor: {{ villageName }}, | ||||
|       <a href="#" v-on:click.prevent="close">close</a> | ||||
|     </p> | ||||
|     <form class="form" action="" v-on:submit.prevent="save"> | ||||
|       <div class="form-input"> | ||||
|         <label class="label" title="Порог отправки (сумма)">Порог отправки (сумма)</label> | ||||
|         <input class="input" type="text" v-model="sendResourcesThreshold" /> | ||||
|       </div> | ||||
|       <div class="form-input"> | ||||
|         <label class="label" title="Таймаут отправки (мин)">Таймаут отправки (мин)</label> | ||||
|         <input class="input" type="text" v-model="sendResourcesTimeout" /> | ||||
|       </div> | ||||
|       <div class="form-actions"> | ||||
|         <button class="btn">Сохранить</button> | ||||
|       </div> | ||||
|     </form> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { Actions, Mutations } from './Store'; | ||||
| import { mapState } from 'vuex'; | ||||
|  | ||||
| export default { | ||||
|   computed: { | ||||
|     ...mapState({ | ||||
|       villageName: state => state.villageSettings.villageName, | ||||
|     }), | ||||
|     sendResourcesThreshold: { | ||||
|       get() { | ||||
|         return this.$store.state.villageSettings.sendResourcesThreshold; | ||||
|       }, | ||||
|       set(value) { | ||||
|         this.$store.commit(Mutations.UpdateVillageSendResourceThreshold, value); | ||||
|       }, | ||||
|     }, | ||||
|     sendResourcesTimeout: { | ||||
|       get() { | ||||
|         return this.$store.state.villageSettings.sendResourcesTimeout; | ||||
|       }, | ||||
|       set(value) { | ||||
|         this.$store.commit(Mutations.UpdateVillageSendResourceTimeout, value); | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     close() { | ||||
|       this.$store.commit(Mutations.ToggleVillageEditor, false); | ||||
|     }, | ||||
|     save() { | ||||
|       this.$store.dispatch(Actions.SaveVillageSettings); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| @import 'style'; | ||||
| .village-editor { | ||||
|   background-color: white; | ||||
| } | ||||
| .summary { | ||||
|   @include with-padding; | ||||
| } | ||||
| .form { | ||||
|   padding-bottom: 10px; | ||||
|   padding-left: 10px; | ||||
|   padding-right: 10px; | ||||
| } | ||||
| .form-input { | ||||
|   margin-bottom: 5px; | ||||
| } | ||||
| .label { | ||||
|   display: inline-block; | ||||
|   width: 200px; | ||||
|   text-align: right; | ||||
| } | ||||
| .input { | ||||
|   margin-left: 5px; | ||||
| } | ||||
| .form-actions { | ||||
|   margin-top: 10px; | ||||
| } | ||||
| .btn { | ||||
|   border: 1px solid #555; | ||||
|   padding: 2px 4px; | ||||
| } | ||||
| </style> | ||||
| @@ -14,8 +14,9 @@ | ||||
|       <tbody> | ||||
|         <template v-for="villageState in shared.villageStates"> | ||||
|           <tr class="normal-line top-line"> | ||||
|             <td :class="{ active: villageState.village.active }" :title="villageState.id"> | ||||
|             <td :class="{ active: villageState.village.active }" :title="villageHint(villageState)"> | ||||
|               {{ villageState.village.name }} | ||||
|               (<a href="#" v-on:click.prevent="openEditor(villageState.id)">ред</a>) | ||||
|             </td> | ||||
|             <td class="right"> | ||||
|               <filling | ||||
| @@ -199,6 +200,7 @@ import ResourceBalance from './ResourceBalance'; | ||||
| import VillageResource from './VillageResource'; | ||||
| import { COLLECTION_POINT_ID, HORSE_STABLE_ID, MARKET_ID, QUARTERS_ID } from '../Core/Buildings'; | ||||
| import { path } from '../Helpers/Path'; | ||||
| import { Actions } from './Store'; | ||||
|  | ||||
| function secondsToTime(value) { | ||||
|   if (value === 0) { | ||||
| @@ -221,6 +223,13 @@ export default { | ||||
|     }; | ||||
|   }, | ||||
|   methods: { | ||||
|     villageHint(villageState) { | ||||
|       const id = villageState.id; | ||||
|       const name = villageState.village.name; | ||||
|       const timeout = villageState.settings.sendResourcesTimeout; | ||||
|       const threshold = villageState.settings.sendResourcesThreshold; | ||||
|       return `${name}, ${id}, отправка ${timeout} мин, порог ${threshold}`; | ||||
|     }, | ||||
|     path(name, args) { | ||||
|       return path(name, args); | ||||
|     }, | ||||
| @@ -274,6 +283,9 @@ export default { | ||||
|     dropResourceTransferTasks(fromVillageId, toVillageId) { | ||||
|       this.shared.dropResourceTransferTasks(fromVillageId, toVillageId); | ||||
|     }, | ||||
|     openEditor(villageId) { | ||||
|       this.$store.dispatch(Actions.OpenVillageEditor, { villageId }); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|   | ||||
| @@ -3,12 +3,14 @@ import { BuildingQueueInfo } from '../Game'; | ||||
| import { Resources, ResourcesInterface } from '../Core/Resources'; | ||||
| import { ResourceStorage } from '../Core/ResourceStorage'; | ||||
| import { IncomingMerchant } from '../Core/Market'; | ||||
| import { VillageSettings, VillageSettingsDefaults } from '../Core/Village'; | ||||
|  | ||||
| const RESOURCES_KEY = 'res'; | ||||
| const CAPACITY_KEY = 'cap'; | ||||
| const PERFORMANCE_KEY = 'perf'; | ||||
| const BUILDING_QUEUE_KEY = 'bq'; | ||||
| const INCOMING_MERCHANTS_KEY = 'im'; | ||||
| const SETTINGS_KEY = 'settings'; | ||||
|  | ||||
| const ResourceOptions = { | ||||
|     factory: () => new Resources(0, 0, 0, 0), | ||||
| @@ -74,4 +76,14 @@ export class VillageStorage { | ||||
|             return new IncomingMerchant(Resources.fromObject(norm), Number(norm.ts || 0)); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     getSettings(): VillageSettings { | ||||
|         return this.storage.getTyped<VillageSettings>(SETTINGS_KEY, { | ||||
|             factory: () => Object.assign({}, VillageSettingsDefaults), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     storeSettings(settings: VillageSettings) { | ||||
|         this.storage.set(SETTINGS_KEY, settings); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Village } from './Core/Village'; | ||||
| import { Village, VillageSettings } from './Core/Village'; | ||||
| import { Scheduler } from './Scheduler'; | ||||
| import { Resources } from './Core/Resources'; | ||||
| import { VillageStorage } from './Storage/VillageStorage'; | ||||
| @@ -55,6 +55,7 @@ interface VillageOwnState { | ||||
|     totalRequired: RequiredResources; | ||||
|     incomingResources: Resources; | ||||
|     buildRemainingSeconds: number; | ||||
|     settings: VillageSettings; | ||||
| } | ||||
|  | ||||
| interface VillageOwnStateDictionary { | ||||
| @@ -124,6 +125,7 @@ function createVillageOwnState(village: Village, scheduler: Scheduler): VillageO | ||||
|         totalRequired: calcResourceBalance(totalRequiredResources, resources, performance), | ||||
|         buildRemainingSeconds: buildQueueInfo.seconds, | ||||
|         incomingResources: calcIncomingResources(storage), | ||||
|         settings: storage.getSettings(), | ||||
|     }; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user