Add crop buildings planning
This commit is contained in:
parent
48bfed5f01
commit
7653c7b6e7
@ -25,7 +25,7 @@ export class TrainTrooperAction extends ActionController {
|
|||||||
const nextToTrainCount = trainCount - readyToTrainCount;
|
const nextToTrainCount = trainCount - readyToTrainCount;
|
||||||
|
|
||||||
if (readyToTrainCount <= 0) {
|
if (readyToTrainCount <= 0) {
|
||||||
throw new TryLaterError(aroundMinutes(15), 'No ready to train troops');
|
throw new TryLaterError(aroundMinutes(15), 'No isReady to train troops');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextToTrainCount > 0) {
|
if (nextToTrainCount > 0) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ActionController, registerAction } from './ActionController';
|
import { ActionController, registerAction } from './ActionController';
|
||||||
import { ActionError, taskError, TryLaterError } from '../Errors';
|
import { ActionError, taskError, TryLaterError } from '../Errors';
|
||||||
import { grabResourceDeposits } from '../Page/SlotBlock';
|
import { grabResourceSlots } from '../Page/SlotBlock';
|
||||||
import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask';
|
import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask';
|
||||||
import { ResourceDeposit } from '../Game';
|
import { ResourceSlot } from '../Game';
|
||||||
import { aroundMinutes, getNumber } from '../utils';
|
import { aroundMinutes, getNumber } from '../utils';
|
||||||
import { Args } from '../Queue/Args';
|
import { Args } from '../Queue/Args';
|
||||||
import { Task } from '../Queue/TaskProvider';
|
import { Task } from '../Queue/TaskProvider';
|
||||||
@ -10,7 +10,7 @@ import { Task } from '../Queue/TaskProvider';
|
|||||||
@registerAction
|
@registerAction
|
||||||
export class UpgradeResourceToLevel extends ActionController {
|
export class UpgradeResourceToLevel extends ActionController {
|
||||||
async run(args: Args, task: Task): Promise<any> {
|
async run(args: Args, task: Task): Promise<any> {
|
||||||
const deposits = grabResourceDeposits();
|
const deposits = grabResourceSlots();
|
||||||
if (deposits.length === 0) {
|
if (deposits.length === 0) {
|
||||||
throw new ActionError('No deposits');
|
throw new ActionError('No deposits');
|
||||||
}
|
}
|
||||||
@ -20,7 +20,7 @@ export class UpgradeResourceToLevel extends ActionController {
|
|||||||
const requiredLevel = getNumber(args.level);
|
const requiredLevel = getNumber(args.level);
|
||||||
|
|
||||||
const notUpgraded = deposits.filter(
|
const notUpgraded = deposits.filter(
|
||||||
dep => !dep.underConstruction && requiredLevel > dep.level
|
dep => !dep.isUnderConstruction && requiredLevel > dep.level
|
||||||
);
|
);
|
||||||
|
|
||||||
if (notUpgraded.length === 0) {
|
if (notUpgraded.length === 0) {
|
||||||
@ -52,7 +52,7 @@ export class UpgradeResourceToLevel extends ActionController {
|
|||||||
throw new TryLaterError(aroundMinutes(10), 'Sleep for next round');
|
throw new TryLaterError(aroundMinutes(10), 'Sleep for next round');
|
||||||
}
|
}
|
||||||
|
|
||||||
private isTaskNotInQueue(villageId: number, dep: ResourceDeposit): boolean {
|
private isTaskNotInQueue(villageId: number, dep: ResourceSlot): boolean {
|
||||||
const tasks = this.scheduler.getTaskItems();
|
const tasks = this.scheduler.getTaskItems();
|
||||||
return (
|
return (
|
||||||
undefined ===
|
undefined ===
|
||||||
|
@ -4,7 +4,7 @@ import { BuildingPageController } from './Page/BuildingPageController';
|
|||||||
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
||||||
import { grabActiveVillageId } from './Page/VillageBlock';
|
import { grabActiveVillageId } from './Page/VillageBlock';
|
||||||
import {
|
import {
|
||||||
grabResourceDeposits,
|
grabResourceSlots,
|
||||||
onBuildingSlotCtrlClick,
|
onBuildingSlotCtrlClick,
|
||||||
onResourceSlotCtrlClick,
|
onResourceSlotCtrlClick,
|
||||||
showBuildingSlotIds,
|
showBuildingSlotIds,
|
||||||
@ -166,7 +166,7 @@ export class ControlPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createDepositsQuickActions(villageId: number) {
|
private createDepositsQuickActions(villageId: number) {
|
||||||
const deposits = grabResourceDeposits();
|
const deposits = grabResourceSlots();
|
||||||
if (deposits.length === 0) {
|
if (deposits.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
16
src/Game.ts
16
src/Game.ts
@ -7,10 +7,20 @@ export class BuildingQueueInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResourceDeposit {
|
export interface ResourceSlot {
|
||||||
readonly buildId: number;
|
readonly buildId: number;
|
||||||
readonly type: ResourceType;
|
readonly type: ResourceType;
|
||||||
readonly level: number;
|
readonly level: number;
|
||||||
readonly ready: boolean;
|
readonly isReady: boolean;
|
||||||
readonly underConstruction: boolean;
|
readonly isUnderConstruction: boolean;
|
||||||
|
readonly isMaxLevel: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ResourceSlotDefaults: ResourceSlot = {
|
||||||
|
buildId: 0,
|
||||||
|
type: ResourceType.Lumber,
|
||||||
|
level: 0,
|
||||||
|
isReady: false,
|
||||||
|
isUnderConstruction: false,
|
||||||
|
isMaxLevel: false,
|
||||||
|
};
|
||||||
|
@ -4,6 +4,7 @@ import { parseLocation, timestamp } from '../utils';
|
|||||||
import { GrabError } from '../Errors';
|
import { GrabError } from '../Errors';
|
||||||
import { BuildingQueueInfo } from '../Game';
|
import { BuildingQueueInfo } from '../Game';
|
||||||
import { ProductionQueue } from '../Core/ProductionQueue';
|
import { ProductionQueue } from '../Core/ProductionQueue';
|
||||||
|
import { grabResourceSlots } from '../Page/SlotBlock';
|
||||||
|
|
||||||
export class VillageOverviewPageGrabber extends Grabber {
|
export class VillageOverviewPageGrabber extends Grabber {
|
||||||
grab(): void {
|
grab(): void {
|
||||||
@ -13,6 +14,7 @@ export class VillageOverviewPageGrabber extends Grabber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.storage.storeResourcesPerformance(grabResourcesPerformance());
|
this.storage.storeResourcesPerformance(grabResourcesPerformance());
|
||||||
|
this.storage.storeResourceSlots(grabResourceSlots());
|
||||||
|
|
||||||
const buildingQueueInfo = this.grabBuildingQueueInfoOrDefault();
|
const buildingQueueInfo = this.grabBuildingQueueInfoOrDefault();
|
||||||
const buildingEndTime = buildingQueueInfo.seconds
|
const buildingEndTime = buildingQueueInfo.seconds
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { elClassId, getNumber } from '../utils';
|
import { elClassId, getNumber } from '../utils';
|
||||||
import { ResourceDeposit } from '../Game';
|
import { ResourceSlot } from '../Game';
|
||||||
import { numberToResourceType } from '../Core/ResourceType';
|
import { numberToResourceType } from '../Core/ResourceType';
|
||||||
|
|
||||||
interface Slot {
|
interface SlotElement {
|
||||||
el: HTMLElement;
|
el: HTMLElement;
|
||||||
buildId: number;
|
buildId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function slotElements(prefix: string): Array<Slot> {
|
function slotElements(prefix: string): Array<SlotElement> {
|
||||||
const result: Array<Slot> = [];
|
const result: Array<SlotElement> = [];
|
||||||
jQuery('.level.colorLayer').each((idx, el) => {
|
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 });
|
result.push({ el, buildId });
|
||||||
@ -74,18 +74,19 @@ export function onBuildingSlotCtrlClick(onClickHandler: (buildId: number) => voi
|
|||||||
onSlotCtrlClick('aid', onClickHandler);
|
onSlotCtrlClick('aid', onClickHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
function slotToDepositMapper(slot: Slot): ResourceDeposit {
|
function makeResourceSlot(slot: SlotElement): ResourceSlot {
|
||||||
const el = slot.el;
|
const $el = jQuery(slot.el);
|
||||||
const classes = jQuery(el).attr('class');
|
const classes = $el.attr('class');
|
||||||
return {
|
return {
|
||||||
buildId: getNumber(elClassId(classes, 'buildingSlot')),
|
buildId: getNumber(elClassId(classes, 'buildingSlot')),
|
||||||
type: numberToResourceType(getNumber(elClassId(classes, 'gid'))),
|
type: numberToResourceType(getNumber(elClassId(classes, 'gid'))),
|
||||||
level: getNumber(elClassId(classes, 'level')),
|
level: getNumber(elClassId(classes, 'level')),
|
||||||
ready: !jQuery(el).hasClass('notNow'),
|
isReady: !$el.hasClass('notNow'),
|
||||||
underConstruction: jQuery(el).hasClass('underConstruction'),
|
isUnderConstruction: $el.hasClass('underConstruction'),
|
||||||
|
isMaxLevel: $el.hasClass('maxLevel'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function grabResourceDeposits(): Array<ResourceDeposit> {
|
export function grabResourceSlots(): Array<ResourceSlot> {
|
||||||
return slotElements('buildingSlot').map(slotToDepositMapper);
|
return slotElements('buildingSlot').map(makeResourceSlot);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ export class Scheduler {
|
|||||||
nextTask(ts: number): NextExecution {
|
nextTask(ts: number): NextExecution {
|
||||||
const task = this.taskQueue.get(ts);
|
const task = this.taskQueue.get(ts);
|
||||||
|
|
||||||
// Task not found - next task not ready or queue is empty
|
// Task not found - next task not isReady or queue is empty
|
||||||
if (!task) {
|
if (!task) {
|
||||||
this.clearActions();
|
this.clearActions();
|
||||||
return {};
|
return {};
|
||||||
@ -99,13 +99,21 @@ export class Scheduler {
|
|||||||
private replaceTask(task: Task): Task | undefined {
|
private replaceTask(task: Task): Task | undefined {
|
||||||
if (task.name === RunVillageProductionTask.name && task.args.villageId) {
|
if (task.name === RunVillageProductionTask.name && task.args.villageId) {
|
||||||
const villageId = task.args.villageId;
|
const villageId = task.args.villageId;
|
||||||
const controller = this.villageControllerFactory.createController(villageId);
|
|
||||||
const villageTask = controller.getReadyProductionTask();
|
// First stage - plan new tasks if needed.
|
||||||
|
const controllerForPlan = this.villageControllerFactory.createController(villageId);
|
||||||
|
controllerForPlan.planTasks();
|
||||||
|
|
||||||
|
// Second stage - select isReady for production task.
|
||||||
|
// We recreate controller, because need new village state.
|
||||||
|
const controllerForSelect = this.villageControllerFactory.createController(villageId);
|
||||||
|
const villageTask = controllerForSelect.getReadyProductionTask();
|
||||||
|
|
||||||
if (villageTask) {
|
if (villageTask) {
|
||||||
this.removeTask(task.id);
|
this.removeTask(task.id);
|
||||||
const newTask = new Task(villageTask.id, 0, villageTask.name, {
|
const newTask = new Task(villageTask.id, 0, villageTask.name, {
|
||||||
...villageTask.args,
|
...villageTask.args,
|
||||||
villageId: controller.getVillageId(),
|
villageId: controllerForSelect.getVillageId(),
|
||||||
});
|
});
|
||||||
this.taskQueue.add(newTask);
|
this.taskQueue.add(newTask);
|
||||||
return newTask;
|
return newTask;
|
||||||
|
@ -6,12 +6,15 @@ import { VillageSettings, VillageSettingsDefaults } from '../Core/Village';
|
|||||||
import { ProductionQueue } from '../Core/ProductionQueue';
|
import { ProductionQueue } from '../Core/ProductionQueue';
|
||||||
import { getNumber } from '../utils';
|
import { getNumber } from '../utils';
|
||||||
import { Task, uniqTaskId } from '../Queue/TaskProvider';
|
import { Task, uniqTaskId } from '../Queue/TaskProvider';
|
||||||
|
import { ResourceSlot, ResourceSlotDefaults } from '../Game';
|
||||||
|
|
||||||
const RESOURCES_KEY = 'resources';
|
const RESOURCES_KEY = 'resources';
|
||||||
const CAPACITY_KEY = 'capacity';
|
const CAPACITY_KEY = 'capacity';
|
||||||
const PERFORMANCE_KEY = 'performance';
|
const PERFORMANCE_KEY = 'performance';
|
||||||
const INCOMING_MERCHANTS_KEY = 'incoming_merchants';
|
const INCOMING_MERCHANTS_KEY = 'incoming_merchants';
|
||||||
const MERCHANTS_INFO_KEY = 'merchants_info';
|
const MERCHANTS_INFO_KEY = 'merchants_info';
|
||||||
|
const RESOURCE_SLOTS_KEY = 'resource_slots';
|
||||||
|
const BUILDING_SLOTS_KEY = 'building_slots';
|
||||||
const SETTINGS_KEY = 'settings';
|
const SETTINGS_KEY = 'settings';
|
||||||
const QUEUE_ENDING_TIME_KEY = 'queue_ending_time';
|
const QUEUE_ENDING_TIME_KEY = 'queue_ending_time';
|
||||||
const TASK_LIST_KEY = 'tasks';
|
const TASK_LIST_KEY = 'tasks';
|
||||||
@ -83,6 +86,16 @@ export class VillageStorage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getResourceSlots(): ReadonlyArray<ResourceSlot> {
|
||||||
|
return this.storage.getTypedList<ResourceSlot>(RESOURCE_SLOTS_KEY, {
|
||||||
|
factory: () => Object.assign({}, ResourceSlotDefaults),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
storeResourceSlots(slots: ReadonlyArray<ResourceSlot>): void {
|
||||||
|
this.storage.set(RESOURCE_SLOTS_KEY, slots);
|
||||||
|
}
|
||||||
|
|
||||||
getSettings(): VillageSettings {
|
getSettings(): VillageSettings {
|
||||||
return this.storage.getTyped<VillageSettings>(SETTINGS_KEY, {
|
return this.storage.getTyped<VillageSettings>(SETTINGS_KEY, {
|
||||||
factory: () => Object.assign({}, VillageSettingsDefaults),
|
factory: () => Object.assign({}, VillageSettingsDefaults),
|
||||||
|
@ -6,6 +6,9 @@ import { Resources } from './Core/Resources';
|
|||||||
import { MerchantsInfo } from './Core/Market';
|
import { MerchantsInfo } from './Core/Market';
|
||||||
import { VillageStorage } from './Storage/VillageStorage';
|
import { VillageStorage } from './Storage/VillageStorage';
|
||||||
import { ReceiveResourcesMode } from './Core/Village';
|
import { ReceiveResourcesMode } from './Core/Village';
|
||||||
|
import { ResourceType } from './Core/ResourceType';
|
||||||
|
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
||||||
|
import * as _ from 'underscore';
|
||||||
|
|
||||||
export class VillageController {
|
export class VillageController {
|
||||||
private readonly villageId: number;
|
private readonly villageId: number;
|
||||||
@ -147,4 +150,47 @@ export class VillageController {
|
|||||||
const newSettings = { ...this.state.settings, receiveResourcesMode: next };
|
const newSettings = { ...this.state.settings, receiveResourcesMode: next };
|
||||||
this.storage.storeSettings(newSettings);
|
this.storage.storeSettings(newSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
planTasks(): void {
|
||||||
|
const performance = this.state.performance;
|
||||||
|
|
||||||
|
if (performance.crop < 100) {
|
||||||
|
this.planCropBuilding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private planCropBuilding() {
|
||||||
|
const resourceSlots = this.storage.getResourceSlots();
|
||||||
|
const tasks = this.taskCollection.getTasks();
|
||||||
|
|
||||||
|
const cropSlots = resourceSlots.filter(s => s.type === ResourceType.Crop);
|
||||||
|
|
||||||
|
// Check, if crop field is building now
|
||||||
|
const isCropBuilding = cropSlots.filter(s => s.isUnderConstruction);
|
||||||
|
if (isCropBuilding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check, if we already have crop task in queue
|
||||||
|
const cropBuildIds = cropSlots.map(s => s.buildId);
|
||||||
|
const cropBuildingTaskInQueue = tasks.find(
|
||||||
|
t => t.args.buildId && cropBuildIds.includes(t.args.buildId)
|
||||||
|
);
|
||||||
|
if (cropBuildingTaskInQueue !== undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find ready for building slots and sort them by level
|
||||||
|
const readyCropSlots = cropSlots.filter(s => !s.isMaxLevel);
|
||||||
|
readyCropSlots.sort((s1, s2) => s1.level - s2.level);
|
||||||
|
|
||||||
|
const targetCropBuildId = _.first(readyCropSlots)?.buildId;
|
||||||
|
if (!targetCropBuildId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.taskCollection.addTaskAsFirst(UpgradeBuildingTask.name, {
|
||||||
|
buildId: targetCropBuildId,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,19 +40,29 @@ export class VillageTaskCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addTask(name: string, args: Args) {
|
addTask(name: string, args: Args) {
|
||||||
|
const tasks = this.getTasks();
|
||||||
|
tasks.push(this.createVillageTask(name, args));
|
||||||
|
this.storage.storeTaskList(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
addTaskAsFirst(name: string, args: Args) {
|
||||||
|
const tasks = this.getTasks();
|
||||||
|
tasks.unshift(this.createVillageTask(name, args));
|
||||||
|
this.storage.storeTaskList(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createVillageTask(name: string, args: Args): Task {
|
||||||
if (!isProductionTask(name)) {
|
if (!isProductionTask(name)) {
|
||||||
throw new Error(`Task "${name}" is not production task`);
|
throw new Error(`Task "${name}" is not production task`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.villageId !== this.villageId) {
|
if (args.villageId !== this.villageId) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Task village id (${args.villageId}) not equal controller village id (${this.villageId}`
|
`Task village id (${args.villageId}) not equal controller village id (${this.villageId}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const task = new Task(uniqTaskId(), 0, name, { villageId: this.villageId, ...args });
|
|
||||||
|
|
||||||
const tasks = this.getTasks();
|
return new Task(uniqTaskId(), 0, name, { villageId: this.villageId, ...args });
|
||||||
tasks.push(task);
|
|
||||||
this.storage.storeTaskList(tasks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTask(taskId: TaskId) {
|
removeTask(taskId: TaskId) {
|
||||||
|
Loading…
Reference in New Issue
Block a user