Add troopers training
This commit is contained in:
		| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
							
								
								
									
										28
									
								
								src/Game.ts
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								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 { | ||||
|   | ||||
| @@ -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(`<div style="padding: 8px 0"><a id="${id}" href="#">Обучить</a></div>`); | ||||
|                 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, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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(`<div style="padding: 8px 0"><a id="${id}" href="#">Обучить</a></div>`); | ||||
|         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); | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|   | ||||
							
								
								
									
										87
									
								
								src/Page/BuildingPageController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/Page/BuildingPageController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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`); | ||||
|     } | ||||
| } | ||||
| @@ -9,7 +9,7 @@ interface Slot { | ||||
| function slotElements(prefix: string): Array<Slot> { | ||||
|     const result: Array<Slot> = []; | ||||
|     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')); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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), | ||||
|         ]); | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/utils.ts
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user