From 500e2665174edf5ef85a670b6100434e123eb03c Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 19 Apr 2020 16:26:49 +0300 Subject: [PATCH] Add troopers training --- src/Command.ts | 5 +- src/ControlPanel.ts | 12 +++- src/Game.ts | 28 +++++++++- src/Page/BuildPage.ts | 89 ------------------------------ src/Page/BuildingPage.ts | 49 +++++++++++++--- src/Page/BuildingPageController.ts | 87 +++++++++++++++++++++++++++++ src/Page/SlotBlock.ts | 4 +- src/Scheduler.ts | 2 +- src/Task/TrainTroopTask.ts | 15 +++-- src/utils.ts | 14 ++--- 10 files changed, 189 insertions(+), 116 deletions(-) delete mode 100644 src/Page/BuildPage.ts create mode 100644 src/Page/BuildingPageController.ts diff --git a/src/Command.ts b/src/Command.ts index 86c1084..24d8b5e 100644 --- a/src/Command.ts +++ b/src/Command.ts @@ -2,12 +2,15 @@ import { ResourcesInterface } from './Game'; import { TaskId } from './Queue/TaskQueue'; export interface Args { + taskId?: TaskId; villageId?: number; buildId?: number; categoryId?: number; + tabId?: number; buildTypeId?: number; + troopId?: number; + trainCount?: number; resources?: ResourcesInterface; - taskId?: TaskId; [name: string]: any; } diff --git a/src/ControlPanel.ts b/src/ControlPanel.ts index 74169fd..2bae61d 100644 --- a/src/ControlPanel.ts +++ b/src/ControlPanel.ts @@ -1,6 +1,6 @@ -import { getNumber, parseLocation, uniqId, waitForLoad } from './utils'; +import { elClassId, getNumber, parseLocation, uniqId, waitForLoad } from './utils'; import { Scheduler } from './Scheduler'; -import { BuildPage } from './Page/BuildPage'; +import { BuildingPageController } from './Page/BuildingPageController'; import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask'; import { grabActiveVillageId, grabVillageList } from './Page/VillageBlock'; import { @@ -127,7 +127,13 @@ export class ControlPanel { } if (p.pathname === '/build.php') { - new BuildPage(this.scheduler, getNumber(p.query.id), getNumber(p.query.category, 1)).run(); + const buildPage = new BuildingPageController(this.scheduler, { + buildId: getNumber(p.query.id), + buildTypeId: getNumber(elClassId(jQuery('#build').attr('class'), 'gid')), + categoryId: getNumber(p.query.category, 1), + tabId: getNumber(p.query.s, 0), + }); + buildPage.run(); } this.createControlPanel(state); diff --git a/src/Game.ts b/src/Game.ts index d14a1f2..1fdd04b 100644 --- a/src/Game.ts +++ b/src/Game.ts @@ -42,10 +42,14 @@ export class Resources implements ResourcesInterface { this.crop = crop; } - static fromObject(obj: ResourcesInterface) { + static fromObject(obj: ResourcesInterface): Resources { return new Resources(obj.lumber, obj.clay, obj.iron, obj.crop); } + static fromStorage(warehouse: number, granary: number): Resources { + return new Resources(warehouse, warehouse, warehouse, granary); + } + getByType(type: ResourceType): number { switch (type) { case ResourceType.Lumber: @@ -66,6 +70,28 @@ export class Resources implements ResourcesInterface { } return result; } + + scale(n: number): Resources { + return new Resources(this.lumber * n, this.clay * n, this.iron * n, this.crop * n); + } + + add(other: Resources): Resources { + return new Resources( + this.lumber + other.lumber, + this.clay + other.clay, + this.iron + other.iron, + this.crop + other.crop + ); + } + + sub(other: Resources): Resources { + return new Resources( + this.lumber - other.lumber, + this.clay - other.clay, + this.iron - other.iron, + this.crop - other.crop + ); + } } export class ResourceStorage { diff --git a/src/Page/BuildPage.ts b/src/Page/BuildPage.ts deleted file mode 100644 index 15f11fe..0000000 --- a/src/Page/BuildPage.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { elClassId, notify, split, uniqId } from '../utils'; -import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; -import { Scheduler } from '../Scheduler'; -import { TrainTroopTask } from '../Task/TrainTroopTask'; -import { grabActiveVillageId } from './VillageBlock'; -import { ConsoleLogger, Logger } from '../Logger'; -import { createBuildButton, createUpgradeButton, grabContractResources } from './BuildingPage'; -import { BuildBuildingTask } from '../Task/BuildBuildingTask'; - -const QUARTERS_ID = 19; - -export class BuildPage { - private scheduler: Scheduler; - private readonly buildId: number; - private readonly logger; - private readonly categoryId: number; - - constructor(scheduler: Scheduler, buildId: number, categoryId: number) { - this.scheduler = scheduler; - this.buildId = buildId; - this.categoryId = categoryId; - this.logger = new ConsoleLogger(this.constructor.name); - } - - run() { - const buildTypeId = elClassId(jQuery('#build').attr('class') || '', 'gid'); - this.logger.log('BUILD PAGE DETECTED', 'ID', this.buildId, 'TYPE', buildTypeId); - - createBuildButton(buildTypeId => this.onScheduleBuildBuilding(buildTypeId)); - createUpgradeButton(() => this.onScheduleUpgradeBuilding()); - - if (buildTypeId === QUARTERS_ID) { - this.createTrainTroopButton(); - } - } - - private onScheduleBuildBuilding(buildTypeId: number) { - const buildId = this.buildId; - const categoryId = this.categoryId; - const villageId = grabActiveVillageId(); - const resources = grabContractResources(); - this.scheduler.scheduleTask(BuildBuildingTask.name, { villageId, buildId, categoryId, buildTypeId, resources }); - notify(`Building ${buildId} scheduled`); - } - - private onScheduleUpgradeBuilding() { - const buildId = this.buildId; - const villageId = grabActiveVillageId(); - const resources = grabContractResources(); - this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId, resources }); - notify(`Upgrading ${buildId} scheduled`); - } - - private createTrainTroopButton() { - const troopBlocks = jQuery('#nonFavouriteTroops .action.troop:not(.empty) .innerTroopWrapper'); - troopBlocks.each((idx, el) => { - const troopId = elClassId(jQuery(el).attr('class') || '', 'troop'); - this.logger.log('TROOP', troopId); - if (troopId) { - const id = uniqId(); - jQuery(el) - .find('.details') - .append(`
Обучить
`); - jQuery(`#${id}`).on('click', evt => { - evt.preventDefault(); - this.onTrainTroopClick(this.buildId, troopId, el); - }); - } - }); - } - - private onTrainTroopClick(buildId: number, troopId: number, el: HTMLElement) { - this.logger.log('TRAIN TROOPERS', 'TROOP ID', troopId, 'BUILDING ID', buildId); - const villageId = grabActiveVillageId(); - const input = jQuery(el).find(`input[name="t${troopId}"]`); - const count = Number(input.val()); - if (!isNaN(count) && count > 0) { - this.logger.log('PREPARE TO TRAIN', count, 'TROOPERS'); - for (let n of split(count)) { - this.scheduler.scheduleTask(TrainTroopTask.name, { - villageId, - buildId, - troopId, - trainCount: n, - }); - } - } - } -} diff --git a/src/Page/BuildingPage.ts b/src/Page/BuildingPage.ts index 2182bad..d8f4040 100644 --- a/src/Page/BuildingPage.ts +++ b/src/Page/BuildingPage.ts @@ -1,6 +1,8 @@ import { GrabError } from '../Errors'; -import { getNumber, trimPrefix, uniqId } from '../utils'; +import { elClassId, getNumber, split, trimPrefix, uniqId } from '../utils'; import { Resources } from '../Game'; +import { grabActiveVillageId } from './VillageBlock'; +import { TrainTroopTask } from '../Task/TrainTroopTask'; export function clickBuildButton(typeId: number) { const section = jQuery(`#contract_building${typeId}`); @@ -52,16 +54,47 @@ export function createUpgradeButton(onClickHandler: () => void) { }); } +function grabResourcesFromList($els) { + const getText = n => + jQuery($els.get(n)) + .find('.value') + .text(); + const grab = n => getNumber(getText(n)); + return new Resources(grab(0), grab(1), grab(2), grab(3)); +} + export function grabContractResources(): Resources { const $els = jQuery('#contract .resource'); if ($els.length === 0) { throw new GrabError('No resource contract element'); } - const grab = n => - getNumber( - jQuery($els.get(n)) - .find('.value') - .text() - ); - return new Resources(grab(0), grab(1), grab(2), grab(3)); + return grabResourcesFromList($els); +} + +export function createTrainTroopButtons( + onClickHandler: (troopId: number, resources: Resources, count: number) => void +) { + const troopBlocks = jQuery('.action.troop:not(.empty) .innerTroopWrapper'); + if (troopBlocks.length === 0) { + throw new GrabError('No troop blocks'); + } + troopBlocks.each((idx, el) => { + const $el = jQuery(el); + const troopId = elClassId($el.attr('class'), 'troop'); + if (troopId === undefined) { + return; + } + const id = uniqId(); + $el.find('.details').append(`
Обучить
`); + const resElement = $el.find('.resourceWrapper .resource'); + const resources = grabResourcesFromList(resElement); + jQuery(`#${id}`).on('click', evt => { + evt.preventDefault(); + const input = $el.find(`input[name="t${troopId}"]`); + const count = getNumber(input.val()); + if (count > 0) { + onClickHandler(troopId, resources, count); + } + }); + }); } diff --git a/src/Page/BuildingPageController.ts b/src/Page/BuildingPageController.ts new file mode 100644 index 0000000..50b7b91 --- /dev/null +++ b/src/Page/BuildingPageController.ts @@ -0,0 +1,87 @@ +import { elClassId, notify, split, uniqId } from '../utils'; +import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; +import { Scheduler } from '../Scheduler'; +import { TrainTroopTask } from '../Task/TrainTroopTask'; +import { grabActiveVillageId } from './VillageBlock'; +import { ConsoleLogger, Logger } from '../Logger'; +import { createBuildButton, createTrainTroopButtons, createUpgradeButton, grabContractResources } from './BuildingPage'; +import { BuildBuildingTask } from '../Task/BuildBuildingTask'; +import { Resources } from '../Game'; + +const QUARTERS_ID = 19; +const HORSE_STABLE_ID = 20; +const EMBASSY_ID = 25; + +export interface BuildingPageAttributes { + buildId: number; + buildTypeId: number; + categoryId: number; + tabId: number; +} + +export class BuildingPageController { + private scheduler: Scheduler; + private readonly attributes: BuildingPageAttributes; + private readonly logger; + + constructor(scheduler: Scheduler, attributes: BuildingPageAttributes) { + this.scheduler = scheduler; + this.attributes = attributes; + this.logger = new ConsoleLogger(this.constructor.name); + } + + run() { + const buildTypeId = this.attributes.buildTypeId; + this.logger.log('BUILD PAGE DETECTED', 'ID', this.attributes.buildId, 'TYPE', buildTypeId); + + createBuildButton(buildTypeId => this.onScheduleBuildBuilding(buildTypeId)); + createUpgradeButton(() => this.onScheduleUpgradeBuilding()); + + if (buildTypeId === QUARTERS_ID) { + createTrainTroopButtons((troopId, res, count) => this.onScheduleTrainTroopers(troopId, res, count)); + } + + if (buildTypeId === HORSE_STABLE_ID) { + createTrainTroopButtons((troopId, res, count) => this.onScheduleTrainTroopers(troopId, res, count)); + } + + if (buildTypeId === EMBASSY_ID && this.attributes.tabId === 1) { + createTrainTroopButtons((troopId, res, count) => this.onScheduleTrainTroopers(troopId, res, count)); + } + } + + private onScheduleBuildBuilding(buildTypeId: number) { + const buildId = this.attributes.buildId; + const categoryId = this.attributes.categoryId; + const villageId = grabActiveVillageId(); + const resources = grabContractResources(); + this.scheduler.scheduleTask(BuildBuildingTask.name, { villageId, buildId, categoryId, buildTypeId, resources }); + notify(`Building ${buildId} scheduled`); + } + + private onScheduleUpgradeBuilding() { + const buildId = this.attributes.buildId; + const villageId = grabActiveVillageId(); + const resources = grabContractResources(); + this.scheduler.scheduleTask(UpgradeBuildingTask.name, { villageId, buildId, resources }); + notify(`Upgrading ${buildId} scheduled`); + } + + private onScheduleTrainTroopers(troopId: number, resources: Resources, count: number) { + const tabId = this.attributes.tabId; + for (let chunk of split(count)) { + const args = { + villageId: grabActiveVillageId(), + buildId: this.attributes.buildId, + buildTypeId: this.attributes.buildTypeId, + tabId, + troopId, + resources: resources.scale(chunk), + trainCount: chunk, + }; + console.log('TRAIN TROOP', args); + this.scheduler.scheduleTask(TrainTroopTask.name, args); + } + notify(`Training ${count} troopers scheduled`); + } +} diff --git a/src/Page/SlotBlock.ts b/src/Page/SlotBlock.ts index 1c6f69f..701323d 100644 --- a/src/Page/SlotBlock.ts +++ b/src/Page/SlotBlock.ts @@ -9,7 +9,7 @@ interface Slot { function slotElements(prefix: string): Array { const result: Array = []; jQuery('.level.colorLayer').each((idx, el) => { - const buildId = getNumber(elClassId(jQuery(el).attr('class') || '', prefix)); + const buildId = getNumber(elClassId(jQuery(el).attr('class'), prefix)); result.push({ el, buildId }); }); return result; @@ -58,7 +58,7 @@ export function onResourceSlotCtrlClick(cb: (buildId: number) => void): void { function slotToDepositMapper(slot: Slot): ResourceDeposit { const el = slot.el; - const classes = jQuery(el).attr('class') || ''; + const classes = jQuery(el).attr('class'); const buildId = getNumber(elClassId(classes, 'buildingSlot')); const level = getNumber(elClassId(classes, 'level')); const type = getNumber(elClassId(classes, 'gid')); diff --git a/src/Scheduler.ts b/src/Scheduler.ts index 7037734..0425b6a 100644 --- a/src/Scheduler.ts +++ b/src/Scheduler.ts @@ -121,7 +121,7 @@ export class Scheduler { } getVillageRequiredResources(villageId): ResourcesInterface | undefined { - const tasks = this.taskQueue.seeItems().filter(t => isBuildingTask(t.name) && sameVillage(villageId, t.args)); + const tasks = this.taskQueue.seeItems().filter(t => sameVillage(villageId, t.args) && t.args.resources); const first = tasks.shift(); if (first && first.args.resources) { return first.args.resources; diff --git a/src/Task/TrainTroopTask.ts b/src/Task/TrainTroopTask.ts index f53f52b..2a7d9bf 100644 --- a/src/Task/TrainTroopTask.ts +++ b/src/Task/TrainTroopTask.ts @@ -10,11 +10,18 @@ import { path } from '../utils'; export class TrainTroopTask extends TaskController { async run(task: Task) { const args: Args = { ...task.args, taskId: task.id }; + + const pathArgs = { + newdid: args.villageId, + gid: args.buildTypeId || undefined, + id: args.buildId || undefined, + s: args.tabId, + }; + + const pagePath = path('/build.php', pathArgs); + this.scheduler.scheduleActions([ - new Command(GoToPageAction.name, { - ...args, - path: path('/build.php', { newdid: args.villageId, id: args.buildId }), - }), + new Command(GoToPageAction.name, { ...args, path: pagePath }), new Command(TrainTrooperAction.name, args), new Command(CompleteTaskAction.name, args), ]); diff --git a/src/utils.ts b/src/utils.ts index ea539e8..036a9db 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -53,23 +53,23 @@ export function trimPrefix(text: string, prefix: string): string { return text.startsWith(prefix) ? text.substr(prefix.length) : text; } -export function elClassId(classes: string, prefix: string): number | undefined { +export function elClassId(classes: string | undefined, prefix: string): number | undefined { + if (classes === undefined) { + return undefined; + } let result: number | undefined = undefined; classes.split(/\s/).forEach(part => { if (part.startsWith(prefix)) { - result = Number(part.substr(prefix.length)); - if (isNaN(result)) { - result = undefined; - } + result = toNumber(part.substr(prefix.length)); } }); return result; } -export function* split(n: number) { +export function* split(n: number, from: number = 2, to: number = 6) { let c = n; while (c > 0) { - const next = 2 + Math.floor(Math.random() * 3); + const next = from + Math.floor(Math.random() * (to - from)); if (next < c) { yield next; c -= next;