diff --git a/src/Action/BalanceHeroResourcesAction.ts b/src/Action/BalanceHeroResourcesAction.ts index 98c8813..c8fe1b1 100644 --- a/src/Action/BalanceHeroResourcesAction.ts +++ b/src/Action/BalanceHeroResourcesAction.ts @@ -1,25 +1,53 @@ import { ActionController, registerAction } from './ActionController'; import { Args } from '../Common'; import { Task } from '../Storage/TaskQueue'; -import { grabResources } from '../Page/ResourcesBlock'; +import { grabResources, grabResourceStorage } from '../Page/ResourcesBlock'; import { changeHeroResource, grabCurrentHeroResource } from '../Page/HeroPage'; -import { HeroAllResources } from '../Game'; +import { HeroAllResources, Resources } from '../Game'; +import { grabActiveVillageId } from '../Page/VillageBlock'; @registerAction export class BalanceHeroResourcesAction extends ActionController { async run(args: Args, task: Task): Promise { - const resourcesAsList = grabResources().asList(); + const resources = this.getRequirements(); + + const resourcesAsList = resources.asList(); const currentType = grabCurrentHeroResource(); - const sorted = resourcesAsList.sort((x, y) => x.value - y.value); - const min = sorted[0]; - const max = sorted[sorted.length - 1]; - const delta = max.value - min.value; - const eps = max.value / 10; + const sorted = resourcesAsList.sort((x, y) => y.value - x.value); + const maxRequirement = sorted[0]; + const minRequirement = sorted[sorted.length - 1]; + const delta = maxRequirement.value - minRequirement.value; + const eps = maxRequirement.value / 10; - const resType = delta > eps ? min.type : HeroAllResources; + const resType = delta > eps ? maxRequirement.type : HeroAllResources; if (resType !== currentType) { changeHeroResource(resType); } } + + private getRequirements() { + const resources = grabResources(); + + const villageId = grabActiveVillageId(); + const requiredResources = this.scheduler.getVillageRequiredResources(villageId); + + if (requiredResources) { + return new Resources( + requiredResources.lumber - resources.lumber, + requiredResources.clay - resources.clay, + requiredResources.iron - resources.iron, + requiredResources.crop - resources.crop + ); + } + + const storage = grabResourceStorage(); + + return new Resources( + storage.warehouse - resources.lumber, + storage.warehouse - resources.clay, + storage.warehouse - resources.iron, + storage.granary - resources.crop + ); + } } diff --git a/src/Action/UpdateBuildingTaskResourcesAction.ts b/src/Action/UpdateBuildingTaskResourcesAction.ts new file mode 100644 index 0000000..b21b6d2 --- /dev/null +++ b/src/Action/UpdateBuildingTaskResourcesAction.ts @@ -0,0 +1,21 @@ +import { ActionController, registerAction } from './ActionController'; +import { Args } from '../Common'; +import { Task } from '../Storage/TaskQueue'; +import { grabContractResources } from '../Page/BuildingPage'; + +@registerAction +export class UpdateBuildingTaskResourcesAction extends ActionController { + async run(args: Args, task: Task): Promise { + const buildingTaskId = args.taskId; + if (buildingTaskId === undefined) { + return; + } + + try { + const resources = grabContractResources(); + this.scheduler.updateResources(buildingTaskId, resources); + } catch (e) { + return; + } + } +} diff --git a/src/Common.ts b/src/Common.ts index 655f47c..01e0498 100644 --- a/src/Common.ts +++ b/src/Common.ts @@ -1,4 +1,5 @@ import { ResourcesInterface } from './Game'; +import { TaskId } from './Storage/TaskQueue'; export interface Args { villageId?: number; @@ -6,6 +7,7 @@ export interface Args { categoryId?: number; buildTypeId?: number; resources?: ResourcesInterface; + taskId?: TaskId; [name: string]: any; } diff --git a/src/ControlPanel.ts b/src/ControlPanel.ts index c243884..24119a9 100644 --- a/src/ControlPanel.ts +++ b/src/ControlPanel.ts @@ -72,6 +72,7 @@ export class ControlPanel { const storage = state.getResourceStorage(); const performance = state.getResourcesPerformance(); const buildQueueInfo = state.getBuildingQueueInfo(); + const requiredResources = scheduler.getVillageRequiredResources(village.id); return { id: village.id, name: village.name, @@ -85,6 +86,10 @@ export class ControlPanel { clay_hour: performance.clay, iron_hour: performance.iron, crop_hour: performance.crop, + lumber_need: requiredResources && requiredResources.lumber, + clay_need: requiredResources && requiredResources.clay, + iron_need: requiredResources && requiredResources.iron, + crop_need: requiredResources && requiredResources.crop, warehouse: storage.warehouse, granary: storage.granary, buildRemainingSeconds: buildQueueInfo.seconds, diff --git a/src/DashboardView/VillageStateList.vue b/src/DashboardView/VillageStateList.vue index 076dfcd..f9f0a12 100644 --- a/src/DashboardView/VillageStateList.vue +++ b/src/DashboardView/VillageStateList.vue @@ -41,12 +41,30 @@ + + След: + + + + + + + + + Необх: + + + + + + + - - +{{ village.lumber_hour }} - +{{ village.clay_hour }} - +{{ village.iron_hour }} - +{{ village.crop_hour }} + + +{{ village.lumber_hour }} + +{{ village.clay_hour }} + +{{ village.iron_hour }} + +{{ village.crop_hour }} @@ -130,6 +148,12 @@ export default { .performance-line td { padding: 0 4px 4px; + font-size: 90%; +} + +.required-line td { + padding: 0 4px 4px; + font-size: 90%; } .village-table td.active { @@ -144,10 +168,6 @@ export default { text-align: right; } -.small { - font-size: 90%; -} - .village-quick-link { display: inline-block; } diff --git a/src/Game.ts b/src/Game.ts index e4e95e2..207a85d 100644 --- a/src/Game.ts +++ b/src/Game.ts @@ -42,6 +42,10 @@ export class Resources implements ResourcesInterface { this.crop = crop; } + static fromObject(obj: ResourcesInterface) { + return new Resources(obj.lumber, obj.clay, obj.iron, obj.crop); + } + getByType(type: ResourceType): number { switch (type) { case ResourceType.Lumber: diff --git a/src/Scheduler.ts b/src/Scheduler.ts index f6ec515..cd4d54e 100644 --- a/src/Scheduler.ts +++ b/src/Scheduler.ts @@ -8,6 +8,8 @@ import { ConsoleLogger, Logger } from './Logger'; import { BuildBuildingTask } from './Task/BuildBuildingTask'; import { GrabVillageState } from './Task/GrabVillageState'; import { ActionQueue } from './Storage/ActionQueue'; +import { Resources, ResourcesInterface } from './Game'; +import { UpdateResourceContracts } from './Task/UpdateResourceContracts'; export class Scheduler { private taskQueue: TaskQueue; @@ -20,17 +22,18 @@ export class Scheduler { this.logger = new ConsoleLogger(this.constructor.name); // this.taskQueue.push(GrabVillageState.name, {}, timestamp()); + // this.taskQueue.push(UpdateResourceContracts.name, {}, timestamp()); + // this.taskQueue.push(BalanceHeroResourcesTask.name, {}, timestamp()); - this.scheduleUniqTask(3600, SendOnAdventureTask.name); - this.scheduleUniqTask(1200, BalanceHeroResourcesTask.name); - this.scheduleUniqTask(180, GrabVillageState.name); + this.createUniqTaskTimer(3600, SendOnAdventureTask.name); + this.createUniqTaskTimer(1200, BalanceHeroResourcesTask.name); + this.createUniqTaskTimer(180, GrabVillageState.name); + this.createUniqTaskTimer(300, UpdateResourceContracts.name); } - private scheduleUniqTask(seconds: number, name: string, args: Args = {}) { + public createUniqTaskTimer(seconds: number, name: string, args: Args = {}) { const taskScheduler = () => { - if (!this.taskQueue.has(t => t.name === name)) { - this.scheduleTask(name, args, timestamp() + Math.min(seconds, 5 * 60)); - } + this.scheduleUniqTask(name, args, timestamp() + Math.min(seconds, 5 * 60)); }; taskScheduler(); setInterval(taskScheduler, seconds * 1000); @@ -63,6 +66,13 @@ export class Scheduler { } } + scheduleUniqTask(name: string, args: Args, ts?: number | undefined): void { + let alreadyHasTask = this.taskQueue.has(t => t.name === name); + if (!alreadyHasTask) { + this.scheduleTask(name, args, ts); + } + } + completeTask(id: TaskId) { this.taskQueue.remove(id); this.actionQueue.clear(); @@ -87,6 +97,13 @@ export class Scheduler { ); } + updateResources(taskId: TaskId, resources: Resources): void { + this.taskQueue.modify( + t => t.id === taskId, + t => withResources(t, resources) + ); + } + scheduleActions(actions: Array): void { this.actionQueue.assign(actions); } @@ -94,6 +111,15 @@ export class Scheduler { clearActions() { this.actionQueue.clear(); } + + getVillageRequiredResources(villageId): ResourcesInterface | undefined { + const tasks = this.taskQueue.seeItems().filter(t => isBuildingTask(t.name) && sameVillage(villageId, t.args)); + const first = tasks.shift(); + if (first && first.args.resources) { + return first.args.resources; + } + return undefined; + } } function isBuildingTask(taskName: string) { @@ -104,10 +130,14 @@ function sameVillage(villageId: number | undefined, args: Args) { return villageId !== undefined && args.villageId === villageId; } -export function withTime(task: Task, ts: number): Task { +function withTime(task: Task, ts: number): Task { return new Task(task.id, ts, task.name, task.args); } +function withResources(task: Task, resources: ResourcesInterface): Task { + return new Task(task.id, task.ts, task.name, { ...task.args, resources }); +} + function calculateTimeToPushAfter(tasks: TaskList, predicate: (t: Task) => boolean, ts: number | undefined): number { const normalizedTs = ts || timestamp(); const queuedTaskIndex = findLastIndex(tasks, predicate); diff --git a/src/Storage/TaskQueue.ts b/src/Storage/TaskQueue.ts index cf65560..7b7133d 100644 --- a/src/Storage/TaskQueue.ts +++ b/src/Storage/TaskQueue.ts @@ -107,7 +107,7 @@ export class TaskQueue { } private flushItems(items: TaskList): void { - const normalized = items.sort((x, y) => x.ts - y.ts); + const normalized = items.sort((x, y) => x.ts - y.ts || x.id.localeCompare(y.id)); this.storage.set(QUEUE_NAME, normalized); } } diff --git a/src/Task/UpdateResourceContracts.ts b/src/Task/UpdateResourceContracts.ts new file mode 100644 index 0000000..02acaa0 --- /dev/null +++ b/src/Task/UpdateResourceContracts.ts @@ -0,0 +1,35 @@ +import { Args, Command } from '../Common'; +import { Task } from '../Storage/TaskQueue'; +import { TaskController, registerTask } from './TaskController'; +import { GoToPageAction } from '../Action/GoToPageAction'; +import { path } from '../utils'; +import { UpgradeBuildingTask } from './UpgradeBuildingTask'; +import { UpdateBuildingTaskResourcesAction } from '../Action/UpdateBuildingTaskResourcesAction'; +import { CompleteTaskAction } from '../Action/CompleteTaskAction'; + +@registerTask +export class UpdateResourceContracts extends TaskController { + async run(task: Task) { + const args: Args = { ...task.args, taskId: task.id }; + + const actions: Array = []; + + const tasks = this.scheduler.getTaskItems(); + for (let task of tasks) { + const { villageId, buildId } = task.args; + if (task.name === UpgradeBuildingTask.name && villageId && buildId) { + actions.push( + new Command(GoToPageAction.name, { + ...args, + path: path('/build.php', { newdid: villageId, id: buildId }), + }) + ); + actions.push(new Command(UpdateBuildingTaskResourcesAction.name, { ...args, taskId: task.id })); + } + } + + actions.push(new Command(CompleteTaskAction.name, args)); + + this.scheduler.scheduleActions(actions); + } +}