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