diff --git a/README.md b/README.md index 38aa6b3..662afe2 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # Travian automation -- [ ] Автоматическая отправка набегов на учетные записи или автоматическая отправка волн атак -- [x] Автоматическая отправка героя в приключение - [x] Автоматическое начало строительства зданий и/или ресурсных полей -- [ ] Автоматический скан карты -- [ ] Автоматический скан статистики других игроков -- [ ] Автоматическая отправка ресурсов на другие учетные записи - [x] Автоматическое обучение войск +- [x] Автоматическая отправка героя в приключение +- [x] Автоматическое переключение ресурсов героя +- [ ] Автоматическая отправка набегов на учетные записи или автоматическая отправка волн атак +- [ ] Автоматический скан карты +- [ ] Автоматическая отправка ресурсов на другие учетные записи - [ ] Автоматическое размещение ставок на аукционе - [ ] Автоматический запуск празднований +- [ ] Сканирование статистики других игроков - [ ] Сканирование собственных деревень для отображения ресурсов и информации в одном месте diff --git a/src/Action/BalanceHeroResourcesAction.ts b/src/Action/BalanceHeroResourcesAction.ts new file mode 100644 index 0000000..9c7de40 --- /dev/null +++ b/src/Action/BalanceHeroResourcesAction.ts @@ -0,0 +1,71 @@ +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; + +@registerAction +export class BalanceHeroResourcesAction extends ActionController { + async run(args: Args, task: Task): Promise { + const res = this.getResources(); + const currentType = this.getCurrentHeroResource(task); + console.log('RESOURCES', res); + console.log('CURRENT TYPE', currentType); + const sorted = res.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; + if (resType !== currentType) { + this.changeToHeroResource(task, resType); + } + } + + private getResources(): Array { + const res = this.state.get('resources'); + const resList: Array = []; + 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'); + } +} diff --git a/src/Action/GrabHeroAttributesAction.ts b/src/Action/GrabHeroAttributesAction.ts index 46f0a25..0dc651e 100644 --- a/src/Action/GrabHeroAttributesAction.ts +++ b/src/Action/GrabHeroAttributesAction.ts @@ -2,6 +2,7 @@ import { ActionController, registerAction } from './ActionController'; import { Args } from '../Common'; import { Task } from '../Storage/TaskQueue'; import { ActionError } from '../Errors'; +import { getNumber } from '../utils'; @registerAction export class GrabHeroAttributesAction extends ActionController { @@ -10,13 +11,11 @@ export class GrabHeroAttributesAction extends ActionController { if (healthElement.length !== 1) { throw new ActionError(task.id, 'Health dom element not found'); } + const text = healthElement.text(); let normalized = text.replace(/[^0-9]/g, ''); - const value = Number(normalized); - if (isNaN(value)) { - throw new ActionError(task.id, `Health value "${text}" (${normalized}) couldn't be converted to number`); - } + const value = getNumber(normalized); this.state.set('hero', { health: value }); } } diff --git a/src/Action/GrabVillageResourcesAction.ts b/src/Action/GrabVillageResourcesAction.ts new file mode 100644 index 0000000..3deb0ae --- /dev/null +++ b/src/Action/GrabVillageResourcesAction.ts @@ -0,0 +1,36 @@ +import { ActionController, registerAction } from './ActionController'; +import { Args } from '../Common'; +import { Task } from '../Storage/TaskQueue'; +import { getNumber } from '../utils'; +import { ActionError } from '../Errors'; + +const LUMBER = 1; +const CLAY = 2; +const IRON = 3; +const CROP = 4; + +@registerAction +export class GrabVillageResourcesAction extends ActionController { + async run(args: Args, task: Task): Promise { + const lumber = this.grabResource(task, LUMBER); + const clay = this.grabResource(task, CLAY); + const iron = this.grabResource(task, IRON); + const crop = this.grabResource(task, CROP); + + this.state.set('resources', { [LUMBER]: lumber, [CLAY]: clay, [IRON]: iron, [CROP]: crop }); + } + + private grabResource(task: Task, type: number): number { + const stockBarElement = jQuery('#stockBar'); + if (stockBarElement.length !== 1) { + throw new ActionError(task.id, 'Stock Bar not found'); + } + + const resElement = stockBarElement.find(`#l${type}`); + if (resElement.length !== 1) { + throw new ActionError(task.id, `Resource #${type} not found`); + } + + return getNumber(resElement.text().replace(/[^0-9]/g, '')); + } +} diff --git a/src/Action/TrainTrooperAction.ts b/src/Action/TrainTrooperAction.ts index 11a6efa..49cadc5 100644 --- a/src/Action/TrainTrooperAction.ts +++ b/src/Action/TrainTrooperAction.ts @@ -22,7 +22,7 @@ export class TrainTrooperAction extends ActionController { const maxCount = getNumber(countLink.text()); if (maxCount < trainCount) { - throw new TryLaterError(task.id, 10 * 60, `Max count ${maxCount} less then need ${trainCount}`); + throw new TryLaterError(task.id, 20 * 60, `Max count ${maxCount} less then need ${trainCount}`); } const input = block.find(`input[name="t${troopId}"]`); diff --git a/src/Scheduler.ts b/src/Scheduler.ts index c49a610..7976750 100644 --- a/src/Scheduler.ts +++ b/src/Scheduler.ts @@ -9,6 +9,7 @@ import { createAction } from './Action/ActionController'; import { createTask } from './Task/TaskController'; import { SendOnAdventureTask } from './Task/SendOnAdventureTask'; import { GameState } from './Storage/GameState'; +import { BalanceHeroResourcesTask } from './Task/BalanceHeroResourcesTask'; export class Scheduler { private readonly version: string; @@ -33,8 +34,11 @@ export class Scheduler { this.scheduleHeroAdventure(); setInterval(() => this.scheduleHeroAdventure(), 3600 * 1000); + this.scheduleResGrab(); + setInterval(() => this.scheduleResGrab(), 600 * 1000); + while (true) { - await this.doLoopStep(); + await this.doTaskProcessingStep(); } } @@ -49,7 +53,13 @@ export class Scheduler { } } - private async doLoopStep() { + private scheduleResGrab() { + if (!this.taskQueue.hasNamed(BalanceHeroResourcesTask.name)) { + this.taskQueue.push(new Command(BalanceHeroResourcesTask.name, {}), timestamp()); + } + } + + private async doTaskProcessingStep() { await sleepShort(); const currentTs = timestamp(); const taskCommand = this.taskQueue.get(currentTs); diff --git a/src/Task/BalanceHeroResourcesTask.ts b/src/Task/BalanceHeroResourcesTask.ts new file mode 100644 index 0000000..a82a746 --- /dev/null +++ b/src/Task/BalanceHeroResourcesTask.ts @@ -0,0 +1,22 @@ +import { Args, Command } from '../Common'; +import { Task } from '../Storage/TaskQueue'; +import { TaskController, registerTask } from './TaskController'; +import { GoToPageAction } from '../Action/GoToPageAction'; +import { CompleteTaskAction } from '../Action/CompleteTaskAction'; +import { TrainTrooperAction } from '../Action/TrainTrooperAction'; +import { GrabVillageResourcesAction } from '../Action/GrabVillageResourcesAction'; +import { BalanceHeroResourcesAction } from '../Action/BalanceHeroResourcesAction'; + +@registerTask +export class BalanceHeroResourcesTask extends TaskController { + async run(task: Task) { + const args: Args = { ...task.cmd.args, taskId: task.id }; + this.scheduler.scheduleActions([ + new Command(GoToPageAction.name, { ...args, path: '/dorf1.php' }), + new Command(GrabVillageResourcesAction.name, args), + new Command(GoToPageAction.name, { ...args, path: 'hero.php' }), + new Command(BalanceHeroResourcesAction.name, args), + new Command(CompleteTaskAction.name, args), + ]); + } +} diff --git a/src/Task/TaskController.ts b/src/Task/TaskController.ts index 24e94c2..e86a573 100644 --- a/src/Task/TaskController.ts +++ b/src/Task/TaskController.ts @@ -4,7 +4,6 @@ import { Scheduler } from '../Scheduler'; const taskMap: { [name: string]: Function | undefined } = {}; export function registerTask(constructor: Function) { - console.log('REGISTER TASK', constructor.name); taskMap[constructor.name] = constructor; }