diff --git a/src/Action/UpgradeResourceToLevel.ts b/src/Action/UpgradeResourceToLevel.ts new file mode 100644 index 0000000..e1ab1a9 --- /dev/null +++ b/src/Action/UpgradeResourceToLevel.ts @@ -0,0 +1,51 @@ +import { ActionController, registerAction } from './ActionController'; +import { Args } from '../Common'; +import { ActionError, GrabError, TryLaterError } from '../Errors'; +import { Task } from '../Storage/TaskQueue'; +import { clickUpgradeButton } from '../Page/BuildingPage'; +import { grabResourceDeposits } from '../Page/SlotBlock'; +import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; + +@registerAction +export class UpgradeResourceToLevel extends ActionController { + async run(args: Args, task: Task): Promise { + const deposits = grabResourceDeposits(); + if (deposits.length === 0) { + throw new ActionError(task.id, 'No deposits'); + } + + const villageId = args.villageId; + const requiredLevel = args.level; + const tasks = this.scheduler.getTaskItems(); + + const allUpgraded = deposits.reduce((memo, dep) => memo && dep.level >= requiredLevel, true); + + if (allUpgraded) { + this.scheduler.completeTask(task.id); + return; + } + + const available = deposits + .sort((x, y) => x.level - y.level) + .filter(dep => dep.ready) + .filter( + dep => + tasks.find( + t => + t.name === UpgradeBuildingTask.name && + t.args.villageId === villageId && + t.args.buildId === dep.buildId + ) === undefined + ); + + if (available.length === 0) { + throw new TryLaterError(task.id, 10 * 60, 'No available deposits'); + } + + const targetDep = available[0]; + + this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId: targetDep.buildId }); + + throw new TryLaterError(task.id, 20 * 60, 'Sleep for next round'); + } +} diff --git a/src/Common.ts b/src/Common.ts index 95bb74f..396cce0 100644 --- a/src/Common.ts +++ b/src/Common.ts @@ -1,4 +1,6 @@ export interface Args { + villageId?: number; + buildId?: number; [name: string]: any; } diff --git a/src/Dashboard/Components/DashboardApp.vue b/src/Dashboard/Components/DashboardApp.vue index 45df1ba..0662fd7 100644 --- a/src/Dashboard/Components/DashboardApp.vue +++ b/src/Dashboard/Components/DashboardApp.vue @@ -3,6 +3,8 @@
+
+
@@ -10,10 +12,12 @@ + + diff --git a/src/Dashboard/Components/TaskList.vue b/src/Dashboard/Components/TaskList.vue index 9d6901b..01d3a84 100644 --- a/src/Dashboard/Components/TaskList.vue +++ b/src/Dashboard/Components/TaskList.vue @@ -3,9 +3,12 @@

Task count: {{ shared.taskList.length }}

- + + @@ -23,11 +26,21 @@ export default { shared: this.$root.$data, }; }, + computed: {}, methods: { formatDate(ts) { const d = new Date(ts * 1000); return dateFormat(d, 'HH:MM:ss'); }, + isThisVillageTask(task) { + const taskVillageId = (task.args || {}).villageId; + const currentVillageId = (this.shared.village || {}).id; + return taskVillageId !== undefined && taskVillageId === currentVillageId; + }, + onRemove(taskId) { + console.log('ON REMOVE TASK', taskId); + this.shared.removeTask(taskId); + }, }, }; @@ -54,4 +67,11 @@ export default { padding: 2px 4px; max-width: 25%; } +.this-village { + color: blue; +} +.remove-action { + font-weight: bold; + color: red; +} diff --git a/src/Dashboard/Dashboard.ts b/src/Dashboard/Dashboard.ts index ffc8c37..eaa075d 100644 --- a/src/Dashboard/Dashboard.ts +++ b/src/Dashboard/Dashboard.ts @@ -1,14 +1,18 @@ import * as URLParse from 'url-parse'; -import { markPage, uniqId, waitForLoad } from '../utils'; +import { uniqId, waitForLoad } from '../utils'; import { Scheduler } from '../Scheduler'; -import { TaskQueueRenderer } from '../TaskQueueRenderer'; import { BuildPage } from '../Page/BuildPage'; import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; -import { grabResources } from '../Page/ResourcesBlock'; -import { grabActiveVillage, grabActiveVillageId, grabVillageList } from '../Page/VillageBlock'; -import { onResourceSlotCtrlClick, showBuildingSlotIds, showResourceSlotIds } from '../Page/SlotBlock'; +import { grabActiveVillage, grabActiveVillageId } from '../Page/VillageBlock'; +import { + grabResourceDeposits, + onResourceSlotCtrlClick, + showBuildingSlotIds, + showResourceSlotIds, +} from '../Page/SlotBlock'; import Vue from 'vue'; import DashboardApp from './Components/DashboardApp.vue'; +import { ResourcesToLevel } from '../Task/ResourcesToLevel'; export class Dashboard { private readonly version: string; @@ -25,45 +29,54 @@ export class Dashboard { const p = new URLParse(window.location.href, true); this.log('PARSED LOCATION', p); - const res = grabResources(); - this.log('RES', res); - - const villages = grabVillageList(); - this.log('VILL', villages); - const villageId = grabActiveVillageId(); + const scheduler = this.scheduler; + const quickActions: any[] = []; + const state = { name: 'Dashboard', village: grabActiveVillage(), version: this.version, taskList: this.scheduler.getTaskItems(), + quickActions: quickActions, + + refreshTasks() { + this.taskList = scheduler.getTaskItems(); + }, + + removeTask(taskId: string) { + scheduler.removeTask(taskId); + this.taskList = scheduler.getTaskItems(); + }, }; - const appId = `app-${uniqId()}`; - jQuery('body').prepend(`
`); - new Vue({ - el: `#${appId}`, - data: state, - render: h => h(DashboardApp), - }); + setInterval(() => state.refreshTasks(), 1000); - // markPage('Dashboard', this.version); - // this.renderTaskQueue(); - // setInterval(() => this.renderTaskQueue(), 5000); + const deposits = grabResourceDeposits(); + if (deposits.length) { + const sorted = deposits.sort((x, y) => x.level - y.level); + const minLevel = sorted[0].level; + for (let i = minLevel + 1; i < minLevel + 4; ++i) { + quickActions.push({ + label: `Ресурсы до уровня ${i}`, + cb: () => { + scheduler.scheduleTask(ResourcesToLevel.name, { villageId, level: i }); + state.refreshTasks(); + }, + }); + } + } const tasks = this.scheduler.getTaskItems(); const buildingsInQueue = tasks .filter(t => t.name === UpgradeBuildingTask.name && t.args.villageId === villageId) - .map(t => t.args.buildId); + .map(t => t.args.buildId || 0); if (p.pathname === '/dorf1.php') { showResourceSlotIds(buildingsInQueue); - onResourceSlotCtrlClick(buildId => { - this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId }); - const n = new Notification(`Building ${buildId} scheduled`); - setTimeout(() => n && n.close(), 4000); - }); + onResourceSlotCtrlClick(buildId => this.onResourceSlotCtrlClick(villageId, buildId, state)); + console.log(grabResourceDeposits()); } if (p.pathname === '/dorf2.php') { @@ -73,11 +86,25 @@ export class Dashboard { if (p.pathname === '/build.php') { new BuildPage(this.scheduler, Number(p.query.id)).run(); } + + this.createControlPanel(state); } - private renderTaskQueue() { - this.log('RENDER TASK QUEUE'); - new TaskQueueRenderer().render(this.scheduler.getTaskItems()); + private createControlPanel(state) { + const appId = `app-${uniqId()}`; + jQuery('body').prepend(`
`); + new Vue({ + el: `#${appId}`, + data: state, + render: h => h(DashboardApp), + }); + } + + private onResourceSlotCtrlClick(villageId: number, buildId: number, state) { + this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId }); + state.refreshTasks(); + const n = new Notification(`Building ${buildId} scheduled`); + setTimeout(() => n && n.close(), 4000); } private log(...args) { diff --git a/src/Game.ts b/src/Game.ts index 39ca4fe..58a2600 100644 --- a/src/Game.ts +++ b/src/Game.ts @@ -12,6 +12,15 @@ export const ResourceMapping: ReadonlyArray<{ num: number; type: ResourceType }> { num: 4, type: ResourceType.Crop }, ]; +export function numberToResourceType(typeAsNumber: number): ResourceType { + for (let mp of ResourceMapping) { + if (typeAsNumber === mp.num) { + return mp.type; + } + } + throw new Error(`Type ${typeAsNumber} in not valid`); +} + export type ResourceList = Array<{ num: number; type: ResourceType; value: number }>; export class Resources { @@ -80,3 +89,16 @@ export type HeroAllResourcesType = 'all'; export const HeroAllResources: HeroAllResourcesType = 'all'; export type HeroResourceType = ResourceType | HeroAllResourcesType; + +export class ResourceDeposit { + readonly buildId: number; + readonly type: ResourceType; + readonly level: number; + readonly ready: boolean; + constructor(buildId: number, type: ResourceType, level: number, ready: boolean) { + this.buildId = buildId; + this.type = type; + this.level = level; + this.ready = ready; + } +} diff --git a/src/Page/BuildPage.ts b/src/Page/BuildPage.ts index 3220f88..6af97cc 100644 --- a/src/Page/BuildPage.ts +++ b/src/Page/BuildPage.ts @@ -59,7 +59,7 @@ export class BuildPage { }); } - private onTrainTroopClick(buildId: Number, troopId: Number, el: HTMLElement) { + private onTrainTroopClick(buildId: number, troopId: number, el: HTMLElement) { console.log('TRAIN TROOPERS', 'TROOP ID', troopId, 'BUILDING ID', buildId); const villageId = grabActiveVillageId(); const input = jQuery(el).find(`input[name="t${troopId}"]`); diff --git a/src/Page/SlotBlock.ts b/src/Page/SlotBlock.ts index 67bbe5b..1c6f69f 100644 --- a/src/Page/SlotBlock.ts +++ b/src/Page/SlotBlock.ts @@ -1,4 +1,5 @@ import { elClassId, getNumber } from '../utils'; +import { numberToResourceType, ResourceDeposit } from '../Game'; interface Slot { el: HTMLElement; @@ -14,7 +15,7 @@ function slotElements(prefix: string): Array { return result; } -function showSlotIds(prefix: string, buildingIds: number[]): void { +function showSlotIds(prefix: string, buildingIds: Array): void { const slots = slotElements(prefix); slots.forEach(slot => { const oldLabel = jQuery(slot.el) @@ -54,3 +55,17 @@ export function onResourceSlotCtrlClick(cb: (buildId: number) => void): void { }); }); } + +function slotToDepositMapper(slot: Slot): ResourceDeposit { + const el = slot.el; + const classes = jQuery(el).attr('class') || ''; + const buildId = getNumber(elClassId(classes, 'buildingSlot')); + const level = getNumber(elClassId(classes, 'level')); + const type = getNumber(elClassId(classes, 'gid')); + const ready = !jQuery(el).hasClass('notNow'); + return new ResourceDeposit(buildId, numberToResourceType(type), level, ready); +} + +export function grabResourceDeposits(): Array { + return slotElements('buildingSlot').map(slotToDepositMapper); +} diff --git a/src/Scheduler.ts b/src/Scheduler.ts index 7bbe5fe..4a3db16 100644 --- a/src/Scheduler.ts +++ b/src/Scheduler.ts @@ -145,6 +145,7 @@ export class Scheduler { completeTask(id: TaskId) { this.taskQueue.complete(id); + this.actionQueue.clear(); } scheduleTask(name: string, args: Args): void { @@ -152,6 +153,11 @@ export class Scheduler { this.taskQueue.push(name, args, timestamp()); } + removeTask(id: TaskId) { + this.taskQueue.remove(id); + this.actionQueue.clear(); + } + scheduleActions(actions: Array): void { this.actionQueue.assign(actions); } diff --git a/src/Storage/TaskQueue.ts b/src/Storage/TaskQueue.ts index 439fe2f..df4e9f2 100644 --- a/src/Storage/TaskQueue.ts +++ b/src/Storage/TaskQueue.ts @@ -76,6 +76,11 @@ export class TaskQueue { this.flushItems(items); } + remove(id: TaskId) { + const [_, items] = this.shiftTask(id); + this.flushItems(items); + } + seeItems(): TaskList { return this.getItems(); } diff --git a/src/Task/ResourcesToLevel.ts b/src/Task/ResourcesToLevel.ts new file mode 100644 index 0000000..b324a86 --- /dev/null +++ b/src/Task/ResourcesToLevel.ts @@ -0,0 +1,22 @@ +import { Args, Command } from '../Common'; +import { Task } from '../Storage/TaskQueue'; +import { TaskController, registerTask } from './TaskController'; +import { GoToPageAction } from '../Action/GoToPageAction'; +import { CompleteTaskAction } from '../Action/CompleteTaskAction'; +import { path } from '../utils'; +import { UpgradeResourceToLevel } from '../Action/UpgradeResourceToLevel'; + +@registerTask +export class ResourcesToLevel extends TaskController { + async run(task: Task) { + const args: Args = { ...task.args, taskId: task.id }; + this.scheduler.scheduleActions([ + new Command(GoToPageAction.name, { + ...args, + path: path('/dorf1.php', { newdid: args.villageId }), + }), + new Command(UpgradeResourceToLevel.name, args), + new Command(CompleteTaskAction.name, args), + ]); + } +} diff --git a/src/utils.ts b/src/utils.ts index 37bf0a4..339f80c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -85,10 +85,12 @@ export function getNumber(value: any, def: number = 0): number { return converted === undefined ? def : converted; } -export function path(p: string, query: { [key: string]: string | number } = {}) { +export function path(p: string, query: { [key: string]: string | number | undefined } = {}) { let parts: string[] = []; for (let k in query) { - parts.push(`${k}=${query[k]}`); + if (query[k] !== undefined) { + parts.push(`${k}=${query[k]}`); + } } return p + (parts.length ? '?' + parts.join('&') : ''); } diff --git a/tsconfig.json b/tsconfig.json index 49a7567..3272ecf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "outDir": "./dist", "strictNullChecks": true, "strictPropertyInitialization": true, - "target": "es2015", + "target": "es2018", "types": ["node", "url-parse", "jquery", "mocha", "chai"] }, "include": [
{{ formatDate(task.ts) }} {{ task.id }} + × + {{ task.name }} {{ JSON.stringify(task.args) }}