Add research tasks
This commit is contained in:
		| @@ -20,6 +20,10 @@ export function createActionHandler(name: string, scheduler: Scheduler): ActionC | ||||
|     return new constructor(scheduler); | ||||
| } | ||||
|  | ||||
| export function err(msg: string): never { | ||||
|     throw new ActionError(msg); | ||||
| } | ||||
|  | ||||
| export class ActionController { | ||||
|     protected scheduler: Scheduler; | ||||
|     constructor(scheduler: Scheduler) { | ||||
| @@ -29,11 +33,7 @@ export class ActionController { | ||||
|     async run(args: Args, task: Task) {} | ||||
|  | ||||
|     ensureSameVillage(args: Args, task: Task) { | ||||
|         let villageId = args.villageId; | ||||
|         if (villageId === undefined) { | ||||
|             throw new ActionError('Undefined village id'); | ||||
|         } | ||||
|  | ||||
|         let villageId = args.villageId || err('Undefined village id'); | ||||
|         const activeVillageId = grabActiveVillageId(); | ||||
|         if (villageId !== activeVillageId) { | ||||
|             throw new TryLaterError(aroundMinutes(1), 'Not same village'); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { ActionError, GrabError, TryLaterError } from '../Errors'; | ||||
| import { clickBuildButton } from '../Page/BuildingPage'; | ||||
| import { ActionController, err, registerAction } from './ActionController'; | ||||
| import { GrabError, TryLaterError } from '../Errors'; | ||||
| import { clickBuildButton } from '../Page/BuildingPage/BuildingPage'; | ||||
| import { aroundMinutes } from '../utils'; | ||||
| import { Args } from '../Queue/Args'; | ||||
| import { Task } from '../Queue/TaskProvider'; | ||||
| @@ -8,14 +8,9 @@ import { Task } from '../Queue/TaskProvider'; | ||||
| @registerAction | ||||
| export class BuildBuildingAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         this.ensureSameVillage(args, task); | ||||
|  | ||||
|         const buildTypeId = args.buildTypeId; | ||||
|         if (buildTypeId === undefined) { | ||||
|             throw new ActionError('Undefined build type id'); | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             this.ensureSameVillage(args, task); | ||||
|             const buildTypeId = args.buildTypeId || err('Undefined build type id'); | ||||
|             clickBuildButton(buildTypeId); | ||||
|         } catch (e) { | ||||
|             if (e instanceof GrabError) { | ||||
|   | ||||
							
								
								
									
										22
									
								
								src/Action/ResearchAction.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Action/ResearchAction.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import { ActionController, err, registerAction } from './ActionController'; | ||||
| import { GrabError, TryLaterError } from '../Errors'; | ||||
| import { aroundMinutes } from '../utils'; | ||||
| import { Args } from '../Queue/Args'; | ||||
| import { Task } from '../Queue/TaskProvider'; | ||||
| import { clickResearchButton } from '../Page/BuildingPage/ForgePage'; | ||||
|  | ||||
| @registerAction | ||||
| export class ResearchAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         try { | ||||
|             this.ensureSameVillage(args, task); | ||||
|             const unitId = args.unitId || err('No unitId in args'); | ||||
|             clickResearchButton(unitId); | ||||
|         } catch (e) { | ||||
|             if (e instanceof GrabError) { | ||||
|                 throw new TryLaterError(aroundMinutes(15), e.message); | ||||
|             } | ||||
|             throw e; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,8 +1,7 @@ | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { AbortTaskError, ActionError, TryLaterError } from '../Errors'; | ||||
| import { ActionController, err, registerAction } from './ActionController'; | ||||
| import { AbortTaskError, TryLaterError } from '../Errors'; | ||||
| import { Resources } from '../Core/Resources'; | ||||
| import { Coordinates, Village } from '../Core/Village'; | ||||
| import { clickSendButton, fillSendResourcesForm, grabMerchantsInfo } from '../Page/BuildingPage'; | ||||
| import { grabVillageResources } from '../Page/ResourcesBlock'; | ||||
| import { grabActiveVillageId, grabVillageList } from '../Page/VillageBlock'; | ||||
| import { SendResourcesTask } from '../Task/SendResourcesTask'; | ||||
| @@ -10,10 +9,7 @@ import { aroundMinutes, timestamp } from '../utils'; | ||||
| import { VillageStorage } from '../Storage/VillageStorage'; | ||||
| import { Args } from '../Queue/Args'; | ||||
| import { Task } from '../Queue/TaskProvider'; | ||||
|  | ||||
| function err(msg: string): never { | ||||
|     throw new ActionError(msg); | ||||
| } | ||||
| import { clickSendButton, fillSendResourcesForm, grabMerchantsInfo } from '../Page/BuildingPage/MarketPage'; | ||||
|  | ||||
| const TIMEOUT = 15; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { grabContractResources } from '../Page/BuildingPage'; | ||||
| import { grabContractResources } from '../Page/BuildingPage/BuildingPage'; | ||||
| import { Args } from '../Queue/Args'; | ||||
| import { Task } from '../Queue/TaskProvider'; | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { GrabError, TryLaterError } from '../Errors'; | ||||
| import { clickUpgradeButton } from '../Page/BuildingPage'; | ||||
| import { clickUpgradeButton } from '../Page/BuildingPage/BuildingPage'; | ||||
| import { aroundMinutes } from '../utils'; | ||||
| import { Args } from '../Queue/Args'; | ||||
| import { Task } from '../Queue/TaskProvider'; | ||||
| @@ -8,9 +8,8 @@ import { Task } from '../Queue/TaskProvider'; | ||||
| @registerAction | ||||
| export class UpgradeBuildingAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         this.ensureSameVillage(args, task); | ||||
|  | ||||
|         try { | ||||
|             this.ensureSameVillage(args, task); | ||||
|             clickUpgradeButton(); | ||||
|         } catch (e) { | ||||
|             if (e instanceof GrabError) { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| export const WAREHOUSE_ID = 10; | ||||
| export const GARNER_ID = 11; | ||||
| export const FORGE_ID = 13; | ||||
| export const COLLECTION_POINT_ID = 16; | ||||
| export const MARKET_ID = 17; | ||||
| export const QUARTERS_ID = 19; | ||||
|   | ||||
| @@ -125,13 +125,13 @@ export class Executor { | ||||
|         this.scheduler.clearActions(); | ||||
|  | ||||
|         if (err instanceof AbortTaskError) { | ||||
|             this.logger.warn('ABORT TASK', task.id); | ||||
|             this.logger.warn('ABORT TASK', task.id, 'MSG', err.message); | ||||
|             this.scheduler.removeTask(task.id); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (err instanceof TryLaterError) { | ||||
|             this.logger.warn('TRY', task.id, 'AFTER', err.seconds); | ||||
|             this.logger.warn('TRY', task.id, 'AFTER', err.seconds, 'MSG', err.message); | ||||
|             this.scheduler.postponeTask(task.id, err.seconds); | ||||
|             return; | ||||
|         } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { Grabber } from './Grabber'; | ||||
| import { grabActiveVillageId } from '../Page/VillageBlock'; | ||||
| import { VillageStorage } from '../Storage/VillageStorage'; | ||||
| import { grabIncomingMerchants } from '../Page/BuildingPage'; | ||||
| import { isMarketSendResourcesPage } from '../Page/PageDetectors'; | ||||
| import { grabIncomingMerchants } from '../Page/BuildingPage/MarketPage'; | ||||
|  | ||||
| export class MarketPageGrabber extends Grabber { | ||||
|     grab(): void { | ||||
|   | ||||
| @@ -1,172 +0,0 @@ | ||||
| import { GrabError } from '../Errors'; | ||||
| import { elClassId, getNumber, trimPrefix, uniqId } from '../utils'; | ||||
| import { Resources } from '../Core/Resources'; | ||||
| import { Coordinates } from '../Core/Village'; | ||||
| import { IncomingMerchant } from '../Core/Market'; | ||||
|  | ||||
| export function clickBuildButton(typeId: number) { | ||||
|     const section = jQuery(`#contract_building${typeId}`); | ||||
|     if (section.length !== 1) { | ||||
|         throw new GrabError('No build section'); | ||||
|     } | ||||
|     const btn = section.find('.contractLink button.green.new'); | ||||
|     if (btn.length !== 1) { | ||||
|         throw new GrabError('No build button, try later'); | ||||
|     } | ||||
|     btn.trigger('click'); | ||||
| } | ||||
|  | ||||
| export function createBuildButton(onClickHandler: (buildTypeId: number, resources: Resources) => void) { | ||||
|     const $els = jQuery('[id^=contract_building]'); | ||||
|     $els.each((idx, el) => { | ||||
|         const $el = jQuery(el); | ||||
|         const buildTypeId = getNumber(trimPrefix($el.attr('id') || '', 'contract_building')); | ||||
|         const btnId = uniqId(); | ||||
|         const resElement = $el.find('.resourceWrapper .resource'); | ||||
|         const resources = grabResourcesFromList(resElement); | ||||
|         $el.append(`<div style="padding: 8px"><a id="${btnId}" href="#">Построить</a></div>`); | ||||
|         jQuery(`#${btnId}`).on('click', evt => { | ||||
|             evt.preventDefault(); | ||||
|             onClickHandler(buildTypeId, resources); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function clickUpgradeButton() { | ||||
|     const btn = jQuery('.upgradeButtonsContainer .section1 button.green.build'); | ||||
|     if (btn.length !== 1) { | ||||
|         throw new GrabError('No upgrade button, try later'); | ||||
|     } | ||||
|     btn.trigger('click'); | ||||
| } | ||||
|  | ||||
| export function createUpgradeButton(onClickHandler: (resources: Resources) => void) { | ||||
|     const upgradeContainer = jQuery('.upgradeButtonsContainer .section1'); | ||||
|     if (upgradeContainer.length !== 1) { | ||||
|         return; | ||||
|     } | ||||
|     const id = uniqId(); | ||||
|     const btn = `<div style="padding: 8px"><a id="${id}" href="#">В очередь</a></div>`; | ||||
|     upgradeContainer.append(btn); | ||||
|     const resources = grabContractResources(); | ||||
|     jQuery(`#${id}`).on('click', evt => { | ||||
|         evt.preventDefault(); | ||||
|         onClickHandler(resources); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function grabResourcesFromList($els: JQuery) { | ||||
|     const getText = (n: number) => | ||||
|         jQuery($els.get(n)) | ||||
|             .find('.value') | ||||
|             .text(); | ||||
|     const grab = (n: number) => 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'); | ||||
|     } | ||||
|     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); | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| interface SendResourcesClickHandler { | ||||
|     (resources: Resources, crd: Coordinates, scale: number): void; | ||||
| } | ||||
|  | ||||
| export function createSendResourcesButton(onClickHandler: SendResourcesClickHandler) { | ||||
|     const id1 = uniqId(); | ||||
|     const id10 = uniqId(); | ||||
|     const id100 = uniqId(); | ||||
|     const id1000 = uniqId(); | ||||
|  | ||||
|     jQuery('#button').before(`<div style="padding: 8px"> | ||||
|         <a id="${id1}" href="#">Отправить</a> /  | ||||
|         <a id="${id10}" href="#">x10</a> /  | ||||
|         <a id="${id100}" href="#">x100</a> / | ||||
|         <a id="${id1000}" href="#">x1000</a> | ||||
|     </div>`); | ||||
|  | ||||
|     const createHandler = (handler: SendResourcesClickHandler, scale: number) => (evt: JQuery.Event) => { | ||||
|         evt.preventDefault(); | ||||
|         const sendSelect = jQuery('#send_select'); | ||||
|         const resources = new Resources( | ||||
|             getNumber(sendSelect.find('#r1').val()), | ||||
|             getNumber(sendSelect.find('#r2').val()), | ||||
|             getNumber(sendSelect.find('#r3').val()), | ||||
|             getNumber(sendSelect.find('#r4').val()) | ||||
|         ); | ||||
|         const crd = new Coordinates(getNumber(jQuery('#xCoordInput').val()), getNumber(jQuery('#yCoordInput').val())); | ||||
|         onClickHandler(resources, crd, scale); | ||||
|     }; | ||||
|  | ||||
|     jQuery(`#${id1}`).on('click', createHandler(onClickHandler, 1)); | ||||
|     jQuery(`#${id10}`).on('click', createHandler(onClickHandler, 10)); | ||||
|     jQuery(`#${id100}`).on('click', createHandler(onClickHandler, 100)); | ||||
|     jQuery(`#${id1000}`).on('click', createHandler(onClickHandler, 1000)); | ||||
| } | ||||
|  | ||||
| export function grabMerchantsInfo() { | ||||
|     const available = getNumber(jQuery('.merchantsAvailable').text()); | ||||
|     const carry = getNumber(jQuery('.carry b').text()); | ||||
|     return { available, carry }; | ||||
| } | ||||
|  | ||||
| export function fillSendResourcesForm(resources: Resources, crd: Coordinates) { | ||||
|     const sendSelect = jQuery('#send_select'); | ||||
|     sendSelect.find('#r1').val(resources.lumber); | ||||
|     sendSelect.find('#r2').val(resources.clay); | ||||
|     sendSelect.find('#r3').val(resources.iron); | ||||
|     sendSelect.find('#r4').val(resources.crop); | ||||
|     jQuery('#xCoordInput').val(crd.x); | ||||
|     jQuery('#yCoordInput').val(crd.y); | ||||
| } | ||||
|  | ||||
| export function clickSendButton() { | ||||
|     jQuery('#enabledButton').trigger('click'); | ||||
| } | ||||
|  | ||||
| export function grabIncomingMerchants(): ReadonlyArray<IncomingMerchant> { | ||||
|     const result: Array<IncomingMerchant> = []; | ||||
|     const root = jQuery('#merchantsOnTheWay .ownMerchants'); | ||||
|     root.find('.traders').each((idx, el) => { | ||||
|         const $el = jQuery(el); | ||||
|         result.push( | ||||
|             new IncomingMerchant( | ||||
|                 grabResourcesFromList($el.find('.resourceWrapper .resources')), | ||||
|                 getNumber($el.find('.timer').attr('value')) | ||||
|             ) | ||||
|         ); | ||||
|     }); | ||||
|     return result; | ||||
| } | ||||
							
								
								
									
										71
									
								
								src/Page/BuildingPage/BuildingPage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/Page/BuildingPage/BuildingPage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| import { GrabError } from '../../Errors'; | ||||
| import { getNumber, trimPrefix, uniqId } from '../../utils'; | ||||
| import { Resources } from '../../Core/Resources'; | ||||
|  | ||||
| export function clickBuildButton(typeId: number) { | ||||
|     const section = jQuery(`#contract_building${typeId}`); | ||||
|     if (section.length !== 1) { | ||||
|         throw new GrabError('No build section'); | ||||
|     } | ||||
|     const btn = section.find('.contractLink button.green.new'); | ||||
|     if (btn.length !== 1) { | ||||
|         throw new GrabError('No build button, try later'); | ||||
|     } | ||||
|     btn.trigger('click'); | ||||
| } | ||||
|  | ||||
| export function createBuildButton(onClickHandler: (buildTypeId: number, resources: Resources) => void) { | ||||
|     const $els = jQuery('[id^=contract_building]'); | ||||
|     $els.each((idx, el) => { | ||||
|         const $el = jQuery(el); | ||||
|         const buildTypeId = getNumber(trimPrefix($el.attr('id') || '', 'contract_building')); | ||||
|         const btnId = uniqId(); | ||||
|         const resElement = $el.find('.resourceWrapper .resource'); | ||||
|         const resources = grabResourcesFromList(resElement); | ||||
|         $el.append(`<div style="padding: 8px"><a id="${btnId}" href="#">Построить</a></div>`); | ||||
|         jQuery(`#${btnId}`).on('click', evt => { | ||||
|             evt.preventDefault(); | ||||
|             onClickHandler(buildTypeId, resources); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function clickUpgradeButton() { | ||||
|     const btn = jQuery('.upgradeButtonsContainer .section1 button.green.build'); | ||||
|     if (btn.length !== 1) { | ||||
|         throw new GrabError('No upgrade button, try later'); | ||||
|     } | ||||
|     btn.trigger('click'); | ||||
| } | ||||
|  | ||||
| export function createUpgradeButton(onClickHandler: (resources: Resources) => void) { | ||||
|     const upgradeContainer = jQuery('.upgradeButtonsContainer .section1'); | ||||
|     if (upgradeContainer.length !== 1) { | ||||
|         return; | ||||
|     } | ||||
|     const id = uniqId(); | ||||
|     const btn = `<div style="padding: 8px"><a id="${id}" href="#">В очередь</a></div>`; | ||||
|     upgradeContainer.append(btn); | ||||
|     const resources = grabContractResources(); | ||||
|     jQuery(`#${id}`).on('click', evt => { | ||||
|         evt.preventDefault(); | ||||
|         onClickHandler(resources); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function grabResourcesFromList($els: JQuery) { | ||||
|     const getText = (n: number) => | ||||
|         jQuery($els.get(n)) | ||||
|             .find('.value') | ||||
|             .text(); | ||||
|     const grab = (n: number) => 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'); | ||||
|     } | ||||
|     return grabResourcesFromList($els); | ||||
| } | ||||
							
								
								
									
										59
									
								
								src/Page/BuildingPage/ForgePage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/Page/BuildingPage/ForgePage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import { elClassId, getNumber, uniqId } from '../../utils'; | ||||
| import { Resources } from '../../Core/Resources'; | ||||
| import { grabResourcesFromList } from './BuildingPage'; | ||||
| import { GrabError } from '../../Errors'; | ||||
|  | ||||
| interface ResearchClickHandler { | ||||
|     (resources: Resources, unitId: number): void; | ||||
| } | ||||
|  | ||||
| export function createResearchButtons(onClickHandler: ResearchClickHandler) { | ||||
|     const $els = jQuery('.research'); | ||||
|     $els.each((index, $el) => createResearchButton(jQuery($el), onClickHandler)); | ||||
| } | ||||
|  | ||||
| function createResearchButton($el: JQuery, onClickHandler: ResearchClickHandler) { | ||||
|     const unitId = grabUnitId($el); | ||||
|     const resElement = $el.find('.resourceWrapper .resource'); | ||||
|     const resources = grabResourcesFromList(resElement); | ||||
|  | ||||
|     const id = uniqId(); | ||||
|     $el.find('.cta').after(`<div style="padding: 8px"> | ||||
|         <a id="${id}" href="#">Исследовать</a> | ||||
|     </div>`); | ||||
|  | ||||
|     jQuery(`#${id}`).on('click', evt => { | ||||
|         evt.preventDefault(); | ||||
|         onClickHandler(resources, unitId); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function grabUnitId($el: JQuery) { | ||||
|     const unitImg = $el.find('img.unit'); | ||||
|     return getNumber(elClassId(unitImg.attr('class'), 'u')); | ||||
| } | ||||
|  | ||||
| export function clickResearchButton(unitId: number) { | ||||
|     const $blockEl = findResearchBlockByUnitId(unitId); | ||||
|     if ($blockEl === undefined) { | ||||
|         throw new GrabError(`No research block for unit ${unitId}`); | ||||
|     } | ||||
|     const $btn = $blockEl.find('button.green.contracting'); | ||||
|     if ($btn.length !== 1) { | ||||
|         throw new GrabError(`No research button for unit ${unitId}`); | ||||
|     } | ||||
|     $btn.trigger('click'); | ||||
| } | ||||
|  | ||||
| function findResearchBlockByUnitId(unitId: number): JQuery | undefined { | ||||
|     const $els = jQuery('.research'); | ||||
|     let $blockEl: JQuery | undefined = undefined; | ||||
|     $els.each((index, el) => { | ||||
|         const $el = jQuery(el); | ||||
|         const blockUnitId = grabUnitId($el); | ||||
|         if (blockUnitId === unitId && $blockEl === undefined) { | ||||
|             $blockEl = $el; | ||||
|         } | ||||
|     }); | ||||
|     return $blockEl; | ||||
| } | ||||
							
								
								
									
										76
									
								
								src/Page/BuildingPage/MarketPage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/Page/BuildingPage/MarketPage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| import { getNumber, uniqId } from '../../utils'; | ||||
| import { Resources } from '../../Core/Resources'; | ||||
| import { Coordinates } from '../../Core/Village'; | ||||
| import { IncomingMerchant } from '../../Core/Market'; | ||||
| import { grabResourcesFromList } from './BuildingPage'; | ||||
|  | ||||
| interface SendResourcesClickHandler { | ||||
|     (resources: Resources, crd: Coordinates, scale: number): void; | ||||
| } | ||||
|  | ||||
| export function createSendResourcesButton(onClickHandler: SendResourcesClickHandler) { | ||||
|     const id1 = uniqId(); | ||||
|     const id10 = uniqId(); | ||||
|     const id100 = uniqId(); | ||||
|     const id1000 = uniqId(); | ||||
|  | ||||
|     jQuery('#button').before(`<div style="padding: 8px"> | ||||
|         <a id="${id1}" href="#">Отправить</a> /  | ||||
|         <a id="${id10}" href="#">x10</a> /  | ||||
|         <a id="${id100}" href="#">x100</a> / | ||||
|         <a id="${id1000}" href="#">x1000</a> | ||||
|     </div>`); | ||||
|  | ||||
|     const createHandler = (handler: SendResourcesClickHandler, scale: number) => (evt: JQuery.Event) => { | ||||
|         evt.preventDefault(); | ||||
|         const sendSelect = jQuery('#send_select'); | ||||
|         const resources = new Resources( | ||||
|             getNumber(sendSelect.find('#r1').val()), | ||||
|             getNumber(sendSelect.find('#r2').val()), | ||||
|             getNumber(sendSelect.find('#r3').val()), | ||||
|             getNumber(sendSelect.find('#r4').val()) | ||||
|         ); | ||||
|         const crd = new Coordinates(getNumber(jQuery('#xCoordInput').val()), getNumber(jQuery('#yCoordInput').val())); | ||||
|         onClickHandler(resources, crd, scale); | ||||
|     }; | ||||
|  | ||||
|     jQuery(`#${id1}`).on('click', createHandler(onClickHandler, 1)); | ||||
|     jQuery(`#${id10}`).on('click', createHandler(onClickHandler, 10)); | ||||
|     jQuery(`#${id100}`).on('click', createHandler(onClickHandler, 100)); | ||||
|     jQuery(`#${id1000}`).on('click', createHandler(onClickHandler, 1000)); | ||||
| } | ||||
|  | ||||
| export function grabMerchantsInfo() { | ||||
|     const available = getNumber(jQuery('.merchantsAvailable').text()); | ||||
|     const carry = getNumber(jQuery('.carry b').text()); | ||||
|     return { available, carry }; | ||||
| } | ||||
|  | ||||
| export function fillSendResourcesForm(resources: Resources, crd: Coordinates) { | ||||
|     const sendSelect = jQuery('#send_select'); | ||||
|     sendSelect.find('#r1').val(resources.lumber); | ||||
|     sendSelect.find('#r2').val(resources.clay); | ||||
|     sendSelect.find('#r3').val(resources.iron); | ||||
|     sendSelect.find('#r4').val(resources.crop); | ||||
|     jQuery('#xCoordInput').val(crd.x); | ||||
|     jQuery('#yCoordInput').val(crd.y); | ||||
| } | ||||
|  | ||||
| export function clickSendButton() { | ||||
|     jQuery('#enabledButton').trigger('click'); | ||||
| } | ||||
|  | ||||
| export function grabIncomingMerchants(): ReadonlyArray<IncomingMerchant> { | ||||
|     const result: Array<IncomingMerchant> = []; | ||||
|     const root = jQuery('#merchantsOnTheWay .ownMerchants'); | ||||
|     root.find('.traders').each((idx, el) => { | ||||
|         const $el = jQuery(el); | ||||
|         result.push( | ||||
|             new IncomingMerchant( | ||||
|                 grabResourcesFromList($el.find('.resourceWrapper .resources')), | ||||
|                 getNumber($el.find('.timer').attr('value')) | ||||
|             ) | ||||
|         ); | ||||
|     }); | ||||
|     return result; | ||||
| } | ||||
							
								
								
									
										32
									
								
								src/Page/BuildingPage/TrooperPage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Page/BuildingPage/TrooperPage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import { Resources } from '../../Core/Resources'; | ||||
| import { GrabError } from '../../Errors'; | ||||
| import { elClassId, getNumber, uniqId } from '../../utils'; | ||||
| import { grabResourcesFromList } from './BuildingPage'; | ||||
|  | ||||
| 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); | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
| @@ -4,18 +4,17 @@ import { Scheduler } from '../Scheduler'; | ||||
| import { TrainTroopTask } from '../Task/TrainTroopTask'; | ||||
| import { grabActiveVillageId, grabVillageList } from './VillageBlock'; | ||||
| import { ConsoleLogger, Logger } from '../Logger'; | ||||
| import { | ||||
|     createBuildButton, | ||||
|     createSendResourcesButton, | ||||
|     createTrainTroopButtons, | ||||
|     createUpgradeButton, | ||||
| } from './BuildingPage'; | ||||
| import { createBuildButton, createUpgradeButton } from './BuildingPage/BuildingPage'; | ||||
| import { BuildBuildingTask } from '../Task/BuildBuildingTask'; | ||||
| import { Resources } from '../Core/Resources'; | ||||
| import { Coordinates } from '../Core/Village'; | ||||
| import { SendResourcesTask } from '../Task/SendResourcesTask'; | ||||
| import { EMBASSY_ID, HORSE_STABLE_ID, QUARTERS_ID } from '../Core/Buildings'; | ||||
| import { BuildingPageAttributes, isMarketSendResourcesPage } from './PageDetectors'; | ||||
| import { BuildingPageAttributes, isForgePage, isMarketSendResourcesPage } from './PageDetectors'; | ||||
| import { createTrainTroopButtons } from './BuildingPage/TrooperPage'; | ||||
| import { createSendResourcesButton } from './BuildingPage/MarketPage'; | ||||
| import { createResearchButtons } from './BuildingPage/ForgePage'; | ||||
| import { ResearchTask } from '../Task/ResearchTask'; | ||||
|  | ||||
| export class BuildingPageController { | ||||
|     private scheduler: Scheduler; | ||||
| @@ -53,6 +52,10 @@ export class BuildingPageController { | ||||
|         if (isMarketSendResourcesPage()) { | ||||
|             createSendResourcesButton((res, crd, scale) => this.onSendResources(res, crd, scale)); | ||||
|         } | ||||
|  | ||||
|         if (isForgePage()) { | ||||
|             createResearchButtons((res, unitId) => this.onResearch(res, unitId)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private onScheduleBuildBuilding(buildTypeId: number, resources: Resources) { | ||||
| @@ -101,4 +104,16 @@ export class BuildingPageController { | ||||
|         }); | ||||
|         notify(`Send resources ${JSON.stringify(resources)} from ${villageId} to ${JSON.stringify(coordinates)}`); | ||||
|     } | ||||
|  | ||||
|     private onResearch(resources: Resources, unitId: number) { | ||||
|         const villageId = grabActiveVillageId(); | ||||
|         this.scheduler.scheduleTask(ResearchTask.name, { | ||||
|             villageId, | ||||
|             buildTypeId: this.attributes.buildTypeId, | ||||
|             buildId: this.attributes.buildId, | ||||
|             unitId, | ||||
|             resources, | ||||
|         }); | ||||
|         notify(`Researching ${unitId} scheduled`); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { elClassId, getNumber, parseLocation } from '../utils'; | ||||
| import { MARKET_ID } from '../Core/Buildings'; | ||||
| import { FORGE_ID, MARKET_ID } from '../Core/Buildings'; | ||||
|  | ||||
| export interface BuildingPageAttributes { | ||||
|     buildId: number; | ||||
| @@ -40,3 +40,11 @@ export function isMarketSendResourcesPage(): boolean { | ||||
|     const { buildTypeId, tabId } = getBuildingPageAttributes(); | ||||
|     return buildTypeId === MARKET_ID && tabId === 5; | ||||
| } | ||||
|  | ||||
| export function isForgePage(): boolean { | ||||
|     if (!isBuildingPage()) { | ||||
|         return false; | ||||
|     } | ||||
|     const { buildTypeId } = getBuildingPageAttributes(); | ||||
|     return buildTypeId === FORGE_ID; | ||||
| } | ||||
|   | ||||
| @@ -16,6 +16,7 @@ export interface Args { | ||||
|     trainCount?: number; | ||||
|     resources?: ResourcesInterface; | ||||
|     coordinates?: CoordinatesInterface; | ||||
|     unitId?: number; | ||||
|     level?: number; | ||||
|     selector?: string; | ||||
|     path?: string; | ||||
|   | ||||
							
								
								
									
										24
									
								
								src/Task/ResearchTask.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/Task/ResearchTask.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| import { TaskController, registerTask, ActionDefinition } from './TaskController'; | ||||
| import { GoToPageAction } from '../Action/GoToPageAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { TrainTrooperAction } from '../Action/TrainTrooperAction'; | ||||
| import { path } from '../utils'; | ||||
| import { Action } from '../Queue/ActionQueue'; | ||||
| import { Args } from '../Queue/Args'; | ||||
| import { Task } from '../Queue/TaskProvider'; | ||||
| import { ResearchAction } from '../Action/ResearchAction'; | ||||
|  | ||||
| @registerTask | ||||
| export class ResearchTask extends TaskController { | ||||
|     defineActions(task: Task): Array<ActionDefinition> { | ||||
|         const args = task.args; | ||||
|  | ||||
|         const pathArgs = { | ||||
|             newdid: args.villageId, | ||||
|             gid: args.buildTypeId || undefined, | ||||
|             id: args.buildId || undefined, | ||||
|         }; | ||||
|  | ||||
|         return [[GoToPageAction.name, { ...args, path: path('/build.php', pathArgs) }], [ResearchAction.name]]; | ||||
|     } | ||||
| } | ||||
| @@ -1,10 +1,7 @@ | ||||
| import { TaskController, registerTask, ActionDefinition } from './TaskController'; | ||||
| import { GoToPageAction } from '../Action/GoToPageAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { path } from '../utils'; | ||||
| import { UpgradeResourceToLevel } from '../Action/UpgradeResourceToLevel'; | ||||
| import { Action } from '../Queue/ActionQueue'; | ||||
| import { Args } from '../Queue/Args'; | ||||
| import { Task } from '../Queue/TaskProvider'; | ||||
|  | ||||
| @registerTask | ||||
| @@ -12,7 +9,7 @@ export class ResourcesToLevel extends TaskController { | ||||
|     defineActions(task: Task): Array<ActionDefinition> { | ||||
|         return [ | ||||
|             [GoToPageAction.name, { path: path('/dorf1.php', { newdid: task.args.villageId }) }], | ||||
|             [UpgradeResourceToLevel.name, {}], | ||||
|             [UpgradeResourceToLevel.name], | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -26,9 +26,9 @@ export class SendResourcesTask extends TaskController { | ||||
|  | ||||
|         return [ | ||||
|             [GoToPageAction.name, { path: pagePath }], | ||||
|             [SendResourcesAction.name, {}], | ||||
|             [SendResourcesAction.name], | ||||
|             [ClickButtonAction.name, { selector: '#enabledButton.green.sendRessources' }], | ||||
|             [CompleteTaskAction.name, {}], | ||||
|             [CompleteTaskAction.name], | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ export function createTaskHandler(name: string, scheduler: Scheduler): TaskContr | ||||
|     return new constructor(scheduler); | ||||
| } | ||||
|  | ||||
| export type ActionDefinition = [string, Args]; | ||||
| export type ActionDefinition = [string] | [string, Args]; | ||||
|  | ||||
| export class TaskController { | ||||
|     protected scheduler: Scheduler; | ||||
| @@ -41,8 +41,12 @@ export class TaskController { | ||||
|         const args: Args = { ...task.args, taskId: task.id }; | ||||
|         const commands: Array<Action> = []; | ||||
|         for (let def of this.defineActions(task)) { | ||||
|             if (def.length === 1) { | ||||
|                 commands.push(new Action(def[0], args)); | ||||
|             } else { | ||||
|                 commands.push(new Action(def[0], { ...args, ...def[1] })); | ||||
|             } | ||||
|         } | ||||
|         commands.push(new Action(CompleteTaskAction.name, args)); | ||||
|         return commands; | ||||
|     } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { TaskController, registerTask } from './TaskController'; | ||||
| import { registerTask, TaskController } from './TaskController'; | ||||
| import { GoToPageAction } from '../Action/GoToPageAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { TrainTrooperAction } from '../Action/TrainTrooperAction'; | ||||
| @@ -19,10 +19,8 @@ export class TrainTroopTask extends TaskController { | ||||
|             s: args.sheetId, | ||||
|         }; | ||||
|  | ||||
|         const pagePath = path('/build.php', pathArgs); | ||||
|  | ||||
|         this.scheduler.scheduleActions([ | ||||
|             new Action(GoToPageAction.name, { ...args, path: pagePath }), | ||||
|             new Action(GoToPageAction.name, { ...args, path: path('/build.php', pathArgs) }), | ||||
|             new Action(TrainTrooperAction.name, args), | ||||
|             new Action(CompleteTaskAction.name, args), | ||||
|         ]); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user