From 0798e1dc3f595e44955f567148a72c342e5eb55a Mon Sep 17 00:00:00 2001
From: Anton Vakhrushev <anwinged@ya.ru>
Date: Sat, 9 May 2020 20:44:07 +0300
Subject: [PATCH] Improve unit training

---
 src/Action/TrainTrooperAction.ts     | 62 ++++++++--------------------
 src/Page/BuildingPage/TrooperPage.ts | 34 +++++++++++++++
 src/Page/BuildingPageController.ts   | 26 ++++++------
 src/Task/TrainTroopTask.ts           | 18 ++++----
 4 files changed, 72 insertions(+), 68 deletions(-)

diff --git a/src/Action/TrainTrooperAction.ts b/src/Action/TrainTrooperAction.ts
index 06df3c4..4668452 100644
--- a/src/Action/TrainTrooperAction.ts
+++ b/src/Action/TrainTrooperAction.ts
@@ -1,57 +1,31 @@
-import { ActionController, registerAction } from './ActionController';
-import { ActionError, TryLaterError } from '../Errors';
-import { getNumber, toNumber } from '../utils';
+import { ActionController, err, registerAction } from './ActionController';
+import { TryLaterError } from '../Errors';
+import { aroundMinutes } from '../utils';
 import { Args } from '../Queue/Args';
 import { Task } from '../Queue/TaskProvider';
+import { clickTrainButton, fillTrainCount, getAvailableCount } from '../Page/BuildingPage/TrooperPage';
+import { TrainTroopTask } from '../Task/TrainTroopTask';
 
 @registerAction
 export class TrainTrooperAction extends ActionController {
     async run(args: Args, task: Task): Promise<any> {
-        const troopId = this.getTroopId(args);
-        const trainCount = this.getTrainCount(args);
+        const troopId = args.troopId || err('No troop id');
+        const trainCount = args.trainCount || err('No troop train count');
 
-        const block = jQuery(`.innerTroopWrapper[data-troopid="${troopId}"]`);
-        if (block.length !== 1) {
-            throw new ActionError(`Troop block not found`);
+        const availableCount = getAvailableCount(troopId);
+
+        const readyToTrainCount = Math.min(availableCount, trainCount);
+        const nextToTrainCount = trainCount - readyToTrainCount;
+
+        if (readyToTrainCount <= 0) {
+            throw new TryLaterError(aroundMinutes(15), 'No ready to train troops');
         }
 
-        const countLink = block.find('.cta a');
-        if (countLink.length !== 1) {
-            throw new ActionError(`Link with max count not found`);
+        if (nextToTrainCount > 0) {
+            this.scheduler.scheduleTask(TrainTroopTask.name, { ...task.args, trainCount: nextToTrainCount });
         }
 
-        const maxCount = getNumber(countLink.text());
-        if (maxCount < trainCount) {
-            throw new TryLaterError(20 * 60, `Max count ${maxCount} less then need ${trainCount}`);
-        }
-
-        const input = block.find(`input[name="t${troopId}"]`);
-        if (input.length !== 1) {
-            throw new ActionError(`Input element not found`);
-        }
-
-        const trainButton = jQuery('.startTraining.green').first();
-        if (trainButton.length !== 1) {
-            throw new ActionError('Train button not found');
-        }
-
-        input.val(trainCount);
-        trainButton.trigger('click');
-    }
-
-    private getTroopId(args: Args): number {
-        const troopId = toNumber(args.troopId);
-        if (troopId === undefined) {
-            throw new ActionError(`Troop id must be a number, given "${args.troopId}"`);
-        }
-        return troopId;
-    }
-
-    private getTrainCount(args: Args): number {
-        const trainCount = toNumber(args.trainCount);
-        if (trainCount === undefined) {
-            throw new ActionError(`Train count must be a number, given "${args.trainCount}"`);
-        }
-        return trainCount;
+        fillTrainCount(troopId, readyToTrainCount);
+        clickTrainButton();
     }
 }
diff --git a/src/Page/BuildingPage/TrooperPage.ts b/src/Page/BuildingPage/TrooperPage.ts
index 5d2b24c..149af4f 100644
--- a/src/Page/BuildingPage/TrooperPage.ts
+++ b/src/Page/BuildingPage/TrooperPage.ts
@@ -30,3 +30,37 @@ export function createTrainTroopButtons(
         });
     });
 }
+
+function getTroopBlock(troopId: number): JQuery {
+    const $block = jQuery(`.innerTroopWrapper[data-troopid="${troopId}"]`);
+    if ($block.length !== 1) {
+        throw new GrabError(`Troop block not found`);
+    }
+    return $block;
+}
+
+export function getAvailableCount(troopId: number): number {
+    const $block = getTroopBlock(troopId);
+    const $countLink = $block.find('.cta a');
+    if ($countLink.length !== 1) {
+        throw new GrabError(`Link with max count not found`);
+    }
+    return getNumber($countLink.text());
+}
+
+export function fillTrainCount(troopId: number, trainCount: number): void {
+    const $block = getTroopBlock(troopId);
+    const input = $block.find(`input[name="t${troopId}"]`);
+    if (input.length !== 1) {
+        throw new GrabError(`Input element not found`);
+    }
+    input.val(trainCount);
+}
+
+export function clickTrainButton(): void {
+    const $trainButton = jQuery('.startTraining.green').first();
+    if ($trainButton.length !== 1) {
+        throw new GrabError('Train button not found');
+    }
+    $trainButton.trigger('click');
+}
diff --git a/src/Page/BuildingPageController.ts b/src/Page/BuildingPageController.ts
index de79a73..6e7147a 100644
--- a/src/Page/BuildingPageController.ts
+++ b/src/Page/BuildingPageController.ts
@@ -83,20 +83,18 @@ export class BuildingPageController {
         notify(`Upgrading ${buildId} scheduled`);
     }
 
-    private onScheduleTrainTroopers(troopId: number, resources: Resources, count: number) {
-        for (let chunk of split(count)) {
-            const args = {
-                villageId: grabActiveVillageId(),
-                buildId: this.attributes.buildId,
-                buildTypeId: this.attributes.buildTypeId,
-                sheetId: this.attributes.sheetId,
-                troopId,
-                resources: resources.scale(chunk),
-                trainCount: chunk,
-            };
-            this.scheduler.scheduleTask(TrainTroopTask.name, args);
-        }
-        notify(`Training ${count} troopers scheduled`);
+    private onScheduleTrainTroopers(troopId: number, resources: Resources, trainCount: number) {
+        const args = {
+            villageId: grabActiveVillageId(),
+            buildId: this.attributes.buildId,
+            buildTypeId: this.attributes.buildTypeId,
+            sheetId: this.attributes.sheetId,
+            troopId,
+            trainCount,
+            resources: resources.scale(trainCount),
+        };
+        this.scheduler.scheduleTask(TrainTroopTask.name, args);
+        notify(`Training ${trainCount} troopers scheduled`);
     }
 
     private onSendResources(resources: Resources, coordinates: Coordinates) {
diff --git a/src/Task/TrainTroopTask.ts b/src/Task/TrainTroopTask.ts
index bb9e565..f10a200 100644
--- a/src/Task/TrainTroopTask.ts
+++ b/src/Task/TrainTroopTask.ts
@@ -1,17 +1,15 @@
-import { TaskController } from './TaskController';
+import { ActionDefinition, TaskController } from './TaskController';
 import { GoToPageAction } from '../Action/GoToPageAction';
 import { CompleteTaskAction } from '../Action/CompleteTaskAction';
 import { TrainTrooperAction } from '../Action/TrainTrooperAction';
-import { Action } from '../Queue/ActionQueue';
-import { Args } from '../Queue/Args';
 import { Task } from '../Queue/TaskProvider';
 import { path } from '../Helpers/Path';
 import { registerTask, TaskType } from './TaskMap';
 
 @registerTask({ type: TaskType.TrainUnit })
 export class TrainTroopTask extends TaskController {
-    async run(task: Task) {
-        const args: Args = { ...task.args, taskId: task.id };
+    defineActions(task: Task): Array<ActionDefinition> {
+        const args = task.args;
 
         const pathArgs = {
             newdid: args.villageId,
@@ -20,10 +18,10 @@ export class TrainTroopTask extends TaskController {
             s: args.sheetId,
         };
 
-        this.scheduler.scheduleActions([
-            new Action(GoToPageAction.name, { ...args, path: path('/build.php', pathArgs) }),
-            new Action(TrainTrooperAction.name, args),
-            new Action(CompleteTaskAction.name, args),
-        ]);
+        return [
+            [GoToPageAction.name, { path: path('/build.php', pathArgs) }],
+            [TrainTrooperAction.name],
+            [CompleteTaskAction.name],
+        ];
     }
 }