Rewrite hero resource action
try to detect hero village before balancing
This commit is contained in:
		| @@ -1,71 +1,30 @@ | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { getNumber, trimPrefix } from '../utils'; | ||||
| import { AbortTaskError, ActionError } from '../Errors'; | ||||
|  | ||||
| interface Resource { | ||||
|     type: number; | ||||
|     value: number; | ||||
| } | ||||
|  | ||||
| const ALL = 0; | ||||
| import { grabResources } from '../Page/ResourcesBlock'; | ||||
| import { changeHeroResource, grabCurrentHeroResource } from '../Page/HeroPage'; | ||||
| import { HeroAllResources } from '../Game'; | ||||
|  | ||||
| @registerAction | ||||
| export class BalanceHeroResourcesAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         const res = this.getResources(); | ||||
|         const currentType = this.getCurrentHeroResource(task); | ||||
|         console.log('RESOURCES', res); | ||||
|         const resources = grabResources().asList(); | ||||
|         const currentType = grabCurrentHeroResource(); | ||||
|  | ||||
|         console.log('RESOURCES', resources); | ||||
|         console.log('CURRENT TYPE', currentType); | ||||
|         const sorted = res.sort((x, y) => x.value - y.value); | ||||
|  | ||||
|         const sorted = resources.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; | ||||
|  | ||||
|         console.log('MIN', min, 'MAX', max, 'DELTA', delta, 'EPS', eps); | ||||
|         const resType = delta > eps ? min.type : ALL; | ||||
|  | ||||
|         const resType = delta > eps ? min.type : HeroAllResources; | ||||
|         if (resType !== currentType) { | ||||
|             this.changeToHeroResource(task, resType); | ||||
|             changeHeroResource(resType); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private getResources(): Array<Resource> { | ||||
|         const res = this.state.get('resources'); | ||||
|         const resList: Array<Resource> = []; | ||||
|         for (let r in res) { | ||||
|             const type = getNumber(r); | ||||
|             const value = getNumber(res[r]); | ||||
|             resList.push({ type, value }); | ||||
|         } | ||||
|         return resList; | ||||
|     } | ||||
|  | ||||
|     private getCurrentHeroResource(task: Task): number { | ||||
|         for (let type of [0, 1, 2, 3, 4]) { | ||||
|             const input = jQuery(`#resourceHero${type}`); | ||||
|             if (input.length !== 1) { | ||||
|                 throw new ActionError(task.id, `Hero resource ${type} not found`); | ||||
|             } | ||||
|             if (input.prop('checked')) { | ||||
|                 return type; | ||||
|             } | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     private changeToHeroResource(task: Task, type: number) { | ||||
|         const input = jQuery(`#resourceHero${type}`); | ||||
|         if (input.length !== 1) { | ||||
|             throw new ActionError(task.id, `Hero resource ${type} not found`); | ||||
|         } | ||||
|  | ||||
|         const btn = jQuery('#saveHeroAttributes'); | ||||
|         if (btn.length !== 1) { | ||||
|             throw new ActionError(task.id, `Hero resource button not found`); | ||||
|         } | ||||
|  | ||||
|         input.trigger('click'); | ||||
|         btn.trigger('click'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { BuildingQueueFullError } from '../Errors'; | ||||
| import { grabActiveVillageId } from '../Page/EveryPage'; | ||||
| import { grabActiveVillageId } from '../Page/VillageBlock'; | ||||
|  | ||||
| @registerAction | ||||
| export class CheckBuildingRemainingTimeAction extends ActionController { | ||||
|   | ||||
							
								
								
									
										32
									
								
								src/Action/GoToHeroVillageAction.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Action/GoToHeroVillageAction.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { grabVillageList } from '../Page/VillageBlock'; | ||||
| import { grabHeroVillage } from '../Page/HeroPage'; | ||||
| import { path } from '../utils'; | ||||
|  | ||||
| @registerAction | ||||
| export class GoToHeroVillageAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         const heroVillageId = this.getHeroVillageId(); | ||||
|         if (heroVillageId) { | ||||
|             window.location.assign(path('/hero.php', { newdid: heroVillageId })); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private getHeroVillageId(): number | undefined { | ||||
|         const villages = grabVillageList(); | ||||
|         const heroVillage = grabHeroVillage(); | ||||
|  | ||||
|         console.log('VILLAGES', villages); | ||||
|         console.log('HERO VILLAGE', heroVillage); | ||||
|  | ||||
|         for (let village of villages) { | ||||
|             if (village.name === heroVillage) { | ||||
|                 return village.id; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return undefined; | ||||
|     } | ||||
| } | ||||
| @@ -3,13 +3,11 @@ import { markPage, waitForLoad } from '../utils'; | ||||
| import { Scheduler } from '../Scheduler'; | ||||
| import { TaskQueueRenderer } from '../TaskQueueRenderer'; | ||||
| import { BuildPage } from '../Page/BuildPage'; | ||||
| import { | ||||
|     grabActiveVillageId, | ||||
|     onResourceSlotCtrlClick, | ||||
|     showBuildingSlotIds, | ||||
|     showFieldsSlotIds, | ||||
| } from '../Page/EveryPage'; | ||||
|  | ||||
| import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; | ||||
| import { grabResources } from '../Page/ResourcesBlock'; | ||||
| import { grabActiveVillageId, grabVillageList } from '../Page/VillageBlock'; | ||||
| import { onResourceSlotCtrlClick, showBuildingSlotIds, showFieldsSlotIds } from '../Page/SlotBlock'; | ||||
|  | ||||
| export class Dashboard { | ||||
|     private readonly version: string; | ||||
| @@ -30,6 +28,12 @@ export class Dashboard { | ||||
|         this.renderTaskQueue(); | ||||
|         setInterval(() => this.renderTaskQueue(), 5000); | ||||
|  | ||||
|         const res = grabResources(); | ||||
|         this.log('RES', res); | ||||
|  | ||||
|         const villages = grabVillageList(); | ||||
|         this.log('VILL', villages); | ||||
|  | ||||
|         const villageId = grabActiveVillageId(); | ||||
|  | ||||
|         const tasks = this.scheduler.getTaskItems(); | ||||
|   | ||||
| @@ -1,5 +1,12 @@ | ||||
| import { TaskId } from './Storage/TaskQueue'; | ||||
|  | ||||
| export class GrabError extends Error { | ||||
|     constructor(msg: string = '') { | ||||
|         super(msg); | ||||
|         Object.setPrototypeOf(this, GrabError.prototype); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class ActionError extends Error { | ||||
|     readonly taskId: TaskId; | ||||
|     constructor(taskId: TaskId, msg: string = '') { | ||||
|   | ||||
							
								
								
									
										82
									
								
								src/Game.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/Game.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| export enum ResourceType { | ||||
|     Lumber = 1, | ||||
|     Clay = 2, | ||||
|     Iron = 3, | ||||
|     Crop = 4, | ||||
| } | ||||
|  | ||||
| export const ResourceMapping: ReadonlyArray<{ num: number; type: ResourceType }> = [ | ||||
|     { num: 1, type: ResourceType.Lumber }, | ||||
|     { num: 2, type: ResourceType.Clay }, | ||||
|     { num: 3, type: ResourceType.Iron }, | ||||
|     { num: 4, type: ResourceType.Crop }, | ||||
| ]; | ||||
|  | ||||
| export type ResourceList = Array<{ num: number; type: ResourceType; value: number }>; | ||||
|  | ||||
| export class Resources { | ||||
|     readonly lumber: number; | ||||
|     readonly clay: number; | ||||
|     readonly iron: number; | ||||
|     readonly crop: number; | ||||
|     readonly warehouse: number; | ||||
|     readonly granary: number; | ||||
|     constructor(lumber: number, clay: number, iron: number, crop: number, warehouse: number, granary: number) { | ||||
|         this.lumber = lumber; | ||||
|         this.clay = clay; | ||||
|         this.iron = iron; | ||||
|         this.crop = crop; | ||||
|         this.warehouse = warehouse; | ||||
|         this.granary = granary; | ||||
|     } | ||||
|  | ||||
|     getByType(type: ResourceType): number { | ||||
|         switch (type) { | ||||
|             case ResourceType.Lumber: | ||||
|                 return this.lumber; | ||||
|             case ResourceType.Clay: | ||||
|                 return this.clay; | ||||
|             case ResourceType.Iron: | ||||
|                 return this.iron; | ||||
|             case ResourceType.Crop: | ||||
|                 return this.crop; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     asList(): ResourceList { | ||||
|         const result: ResourceList = []; | ||||
|         for (let mp of ResourceMapping) { | ||||
|             result.push({ num: mp.num, type: mp.type, value: this.getByType(mp.type) }); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class Coordinates { | ||||
|     readonly x: number; | ||||
|     readonly y: number; | ||||
|     constructor(x: number, y: number) { | ||||
|         this.x = x; | ||||
|         this.y = y; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class Village { | ||||
|     readonly id: number; | ||||
|     readonly name: string; | ||||
|     readonly active: boolean; | ||||
|     readonly crd: Coordinates; | ||||
|     constructor(id: number, name: string, active: boolean, crd: Coordinates) { | ||||
|         this.id = id; | ||||
|         this.name = name; | ||||
|         this.active = active; | ||||
|         this.crd = crd; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export type VillageList = Array<Village>; | ||||
|  | ||||
| export type HeroAllResourcesType = 'all'; | ||||
| export const HeroAllResources: HeroAllResourcesType = 'all'; | ||||
|  | ||||
| export type HeroResourceType = ResourceType | HeroAllResourcesType; | ||||
| @@ -2,7 +2,7 @@ import { elClassId, split, uniqId } from '../utils'; | ||||
| import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; | ||||
| import { Scheduler } from '../Scheduler'; | ||||
| import { TrainTroopTask } from '../Task/TrainTroopTask'; | ||||
| import { grabActiveVillageId } from './EveryPage'; | ||||
| import { grabActiveVillageId } from './VillageBlock'; | ||||
|  | ||||
| const QUARTERS_ID = 19; | ||||
|  | ||||
|   | ||||
							
								
								
									
										52
									
								
								src/Page/HeroPage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/Page/HeroPage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| import { GrabError } from '../Errors'; | ||||
| import { HeroAllResources, HeroResourceType, ResourceMapping, ResourceType } from '../Game'; | ||||
|  | ||||
| export function grabCurrentHeroResource(): HeroResourceType { | ||||
|     for (let mp of ResourceMapping) { | ||||
|         if (checkHeroResourceType(mp.num)) { | ||||
|             return mp.type; | ||||
|         } | ||||
|     } | ||||
|     return HeroAllResources; | ||||
| } | ||||
|  | ||||
| function checkHeroResourceType(typeAsNumber: number): boolean { | ||||
|     const input = jQuery(`#resourceHero${typeAsNumber}`); | ||||
|     if (input.length !== 1) { | ||||
|         throw new GrabError(`Hero resource ${typeAsNumber} not found`); | ||||
|     } | ||||
|     return !!input.prop('checked'); | ||||
| } | ||||
|  | ||||
| export function changeHeroResource(type: HeroResourceType) { | ||||
|     const typeAsNumber = heroResourceTypeToNumber(type); | ||||
|     const input = jQuery(`#resourceHero${typeAsNumber}`); | ||||
|     if (input.length !== 1) { | ||||
|         throw new GrabError(`Hero resource ${typeAsNumber} not found`); | ||||
|     } | ||||
|  | ||||
|     const btn = jQuery('#saveHeroAttributes'); | ||||
|     if (btn.length !== 1) { | ||||
|         throw new GrabError(`Hero resource button not found`); | ||||
|     } | ||||
|  | ||||
|     input.trigger('click'); | ||||
|     btn.trigger('click'); | ||||
| } | ||||
|  | ||||
| function heroResourceTypeToNumber(type: HeroResourceType): number { | ||||
|     if (type === HeroAllResources) { | ||||
|         return 0; | ||||
|     } | ||||
|     return type as ResourceType; | ||||
| } | ||||
|  | ||||
| export function grabHeroVillage(): string | undefined { | ||||
|     const status = jQuery('.heroStatusMessage').text(); | ||||
|     const hrefText = jQuery('.heroStatusMessage a').text(); | ||||
|     if (status.includes('в родной деревне')) { | ||||
|         return hrefText || undefined; | ||||
|     } else { | ||||
|         return undefined; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										39
									
								
								src/Page/ResourcesBlock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/Page/ResourcesBlock.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| import { Resources, ResourceType } from '../Game'; | ||||
| import { GrabError } from '../Errors'; | ||||
| import { getNumber } from '../utils'; | ||||
|  | ||||
| export function grabResources(): Resources { | ||||
|     const lumber = grabResource(ResourceType.Lumber); | ||||
|     const clay = grabResource(ResourceType.Clay); | ||||
|     const iron = grabResource(ResourceType.Iron); | ||||
|     const crop = grabResource(ResourceType.Crop); | ||||
|  | ||||
|     const warehouse = grabCapacity('warehouse'); | ||||
|     const granary = grabCapacity('granary'); | ||||
|  | ||||
|     return new Resources(lumber, clay, iron, crop, warehouse, granary); | ||||
| } | ||||
|  | ||||
| function findStockBarElement() { | ||||
|     const stockBarElement = jQuery('#stockBar'); | ||||
|     if (stockBarElement.length !== 1) { | ||||
|         throw new GrabError('Stock Bar not found'); | ||||
|     } | ||||
|     return stockBarElement; | ||||
| } | ||||
|  | ||||
| function grabResource(type: number): number { | ||||
|     const resElement = findStockBarElement().find(`#l${type}`); | ||||
|     if (resElement.length !== 1) { | ||||
|         throw new GrabError(`Resource #${type} not found`); | ||||
|     } | ||||
|     return getNumber(resElement.text().replace(/[^0-9]/g, '')); | ||||
| } | ||||
|  | ||||
| function grabCapacity(type: string): number { | ||||
|     const capacityElement = findStockBarElement().find(`.${type} .capacity .value`); | ||||
|     if (capacityElement.length !== 1) { | ||||
|         throw new GrabError(`Capacity #${type} not found`); | ||||
|     } | ||||
|     return getNumber(capacityElement.text().replace(/[^0-9]/g, '')); | ||||
| } | ||||
| @@ -1,13 +1,5 @@ | ||||
| import * as URLParse from 'url-parse'; | ||||
| import { elClassId, getNumber } from '../utils'; | ||||
| 
 | ||||
| export function grabActiveVillageId(): number { | ||||
|     const href = jQuery('#sidebarBoxVillagelist a.active').attr('href'); | ||||
|     const p = new URLParse(href || '', true); | ||||
|     console.log('VILLAGE REF', href, p); | ||||
|     return getNumber(p.query.newdid); | ||||
| } | ||||
| 
 | ||||
| interface Slot { | ||||
|     el: HTMLElement; | ||||
|     buildId: number; | ||||
							
								
								
									
										40
									
								
								src/Page/VillageBlock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/Page/VillageBlock.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| import { Coordinates, Village, VillageList } from '../Game'; | ||||
| import { GrabError } from '../Errors'; | ||||
| import * as URLParse from 'url-parse'; | ||||
| import { getNumber } from '../utils'; | ||||
|  | ||||
| export function grabVillageList(): VillageList { | ||||
|     const villageList: VillageList = []; | ||||
|     const $elements = getVillageListItems(); | ||||
|     $elements.each((idx, el) => { | ||||
|         villageList.push(grabVillageInfo(jQuery(el))); | ||||
|     }); | ||||
|     return villageList; | ||||
| } | ||||
|  | ||||
| export function grabActiveVillageId(): number { | ||||
|     const villageList = grabVillageList(); | ||||
|     for (let village of villageList) { | ||||
|         if (village.active) { | ||||
|             return village.id; | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| function getVillageListItems() { | ||||
|     const $elements = jQuery('#sidebarBoxVillagelist ul li a'); | ||||
|     if ($elements.length === 0) { | ||||
|         throw new GrabError('Village list items not found'); | ||||
|     } | ||||
|     return $elements; | ||||
| } | ||||
|  | ||||
| function grabVillageInfo($el): Village { | ||||
|     const href = $el.attr('href'); | ||||
|     const parsedHref = new URLParse(href || '', true); | ||||
|     const id = getNumber(parsedHref.query.newdid); | ||||
|     const name = $el.find('.name').text(); | ||||
|     const active = $el.hasClass('active'); | ||||
|     return new Village(id, name, active, new Coordinates(0, 0)); | ||||
| } | ||||
| @@ -3,24 +3,20 @@ import { Task } from '../Storage/TaskQueue'; | ||||
| import { TaskController, registerTask } from './TaskController'; | ||||
| import { GoToPageAction } from '../Action/GoToPageAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { GrabVillageResourcesAction } from '../Action/GrabVillageResourcesAction'; | ||||
| import { BalanceHeroResourcesAction } from '../Action/BalanceHeroResourcesAction'; | ||||
| import { path } from '../utils'; | ||||
| import { GoToHeroVillageAction } from '../Action/GoToHeroVillageAction'; | ||||
|  | ||||
| @registerTask | ||||
| export class BalanceHeroResourcesTask 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'), | ||||
|             }), | ||||
|             new Command(GrabVillageResourcesAction.name, args), | ||||
|             new Command(GoToPageAction.name, { | ||||
|                 ...args, | ||||
|                 path: path('/hero.php'), | ||||
|             }), | ||||
|             new Command(GoToHeroVillageAction.name, args), | ||||
|             new Command(BalanceHeroResourcesAction.name, args), | ||||
|             new Command(CompleteTaskAction.name, args), | ||||
|         ]); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user