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;
|
||||
|
||||
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) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ActionController, registerAction } from './ActionController';
|
||||
import { ActionError, taskError, TryLaterError } from '../Errors';
|
||||
import { grabResourceDeposits } from '../Page/SlotBlock';
|
||||
import { grabResourceSlots } from '../Page/SlotBlock';
|
||||
import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask';
|
||||
import { ResourceDeposit } from '../Game';
|
||||
import { ResourceSlot } from '../Game';
|
||||
import { aroundMinutes, getNumber } from '../utils';
|
||||
import { Args } from '../Queue/Args';
|
||||
import { Task } from '../Queue/TaskProvider';
|
||||
@ -10,7 +10,7 @@ import { Task } from '../Queue/TaskProvider';
|
||||
@registerAction
|
||||
export class UpgradeResourceToLevel extends ActionController {
|
||||
async run(args: Args, task: Task): Promise<any> {
|
||||
const deposits = grabResourceDeposits();
|
||||
const deposits = grabResourceSlots();
|
||||
if (deposits.length === 0) {
|
||||
throw new ActionError('No deposits');
|
||||
}
|
||||
@ -20,7 +20,7 @@ export class UpgradeResourceToLevel extends ActionController {
|
||||
const requiredLevel = getNumber(args.level);
|
||||
|
||||
const notUpgraded = deposits.filter(
|
||||
dep => !dep.underConstruction && requiredLevel > dep.level
|
||||
dep => !dep.isUnderConstruction && requiredLevel > dep.level
|
||||
);
|
||||
|
||||
if (notUpgraded.length === 0) {
|
||||
@ -52,7 +52,7 @@ export class UpgradeResourceToLevel extends ActionController {
|
||||
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();
|
||||
return (
|
||||
undefined ===
|
||||
|
@ -4,7 +4,7 @@ import { BuildingPageController } from './Page/BuildingPageController';
|
||||
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
||||
import { grabActiveVillageId } from './Page/VillageBlock';
|
||||
import {
|
||||
grabResourceDeposits,
|
||||
grabResourceSlots,
|
||||
onBuildingSlotCtrlClick,
|
||||
onResourceSlotCtrlClick,
|
||||
showBuildingSlotIds,
|
||||
@ -166,7 +166,7 @@ export class ControlPanel {
|
||||
}
|
||||
|
||||
private createDepositsQuickActions(villageId: number) {
|
||||
const deposits = grabResourceDeposits();
|
||||
const deposits = grabResourceSlots();
|
||||
if (deposits.length === 0) {
|
||||
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 type: ResourceType;
|
||||
readonly level: number;
|
||||
readonly ready: boolean;
|
||||
readonly underConstruction: boolean;
|
||||
readonly isReady: 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 { BuildingQueueInfo } from '../Game';
|
||||
import { ProductionQueue } from '../Core/ProductionQueue';
|
||||
import { grabResourceSlots } from '../Page/SlotBlock';
|
||||
|
||||
export class VillageOverviewPageGrabber extends Grabber {
|
||||
grab(): void {
|
||||
@ -13,6 +14,7 @@ export class VillageOverviewPageGrabber extends Grabber {
|
||||
}
|
||||
|
||||
this.storage.storeResourcesPerformance(grabResourcesPerformance());
|
||||
this.storage.storeResourceSlots(grabResourceSlots());
|
||||
|
||||
const buildingQueueInfo = this.grabBuildingQueueInfoOrDefault();
|
||||
const buildingEndTime = buildingQueueInfo.seconds
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { elClassId, getNumber } from '../utils';
|
||||
import { ResourceDeposit } from '../Game';
|
||||
import { ResourceSlot } from '../Game';
|
||||
import { numberToResourceType } from '../Core/ResourceType';
|
||||
|
||||
interface Slot {
|
||||
interface SlotElement {
|
||||
el: HTMLElement;
|
||||
buildId: number;
|
||||
}
|
||||
|
||||
function slotElements(prefix: string): Array<Slot> {
|
||||
const result: Array<Slot> = [];
|
||||
function slotElements(prefix: string): Array<SlotElement> {
|
||||
const result: Array<SlotElement> = [];
|
||||
jQuery('.level.colorLayer').each((idx, el) => {
|
||||
const buildId = getNumber(elClassId(jQuery(el).attr('class'), prefix));
|
||||
result.push({ el, buildId });
|
||||
@ -74,18 +74,19 @@ export function onBuildingSlotCtrlClick(onClickHandler: (buildId: number) => voi
|
||||
onSlotCtrlClick('aid', onClickHandler);
|
||||
}
|
||||
|
||||
function slotToDepositMapper(slot: Slot): ResourceDeposit {
|
||||
const el = slot.el;
|
||||
const classes = jQuery(el).attr('class');
|
||||
function makeResourceSlot(slot: SlotElement): ResourceSlot {
|
||||
const $el = jQuery(slot.el);
|
||||
const classes = $el.attr('class');
|
||||
return {
|
||||
buildId: getNumber(elClassId(classes, 'buildingSlot')),
|
||||
type: numberToResourceType(getNumber(elClassId(classes, 'gid'))),
|
||||
level: getNumber(elClassId(classes, 'level')),
|
||||
ready: !jQuery(el).hasClass('notNow'),
|
||||
underConstruction: jQuery(el).hasClass('underConstruction'),
|
||||
isReady: !$el.hasClass('notNow'),
|
||||
isUnderConstruction: $el.hasClass('underConstruction'),
|
||||
isMaxLevel: $el.hasClass('maxLevel'),
|
||||
};
|
||||
}
|
||||
|
||||
export function grabResourceDeposits(): Array<ResourceDeposit> {
|
||||
return slotElements('buildingSlot').map(slotToDepositMapper);
|
||||
export function grabResourceSlots(): Array<ResourceSlot> {
|
||||
return slotElements('buildingSlot').map(makeResourceSlot);
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ export class Scheduler {
|
||||
nextTask(ts: number): NextExecution {
|
||||
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) {
|
||||
this.clearActions();
|
||||
return {};
|
||||
@ -99,13 +99,21 @@ export class Scheduler {
|
||||
private replaceTask(task: Task): Task | undefined {
|
||||
if (task.name === RunVillageProductionTask.name && 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) {
|
||||
this.removeTask(task.id);
|
||||
const newTask = new Task(villageTask.id, 0, villageTask.name, {
|
||||
...villageTask.args,
|
||||
villageId: controller.getVillageId(),
|
||||
villageId: controllerForSelect.getVillageId(),
|
||||
});
|
||||
this.taskQueue.add(newTask);
|
||||
return newTask;
|
||||
|
@ -6,12 +6,15 @@ import { VillageSettings, VillageSettingsDefaults } from '../Core/Village';
|
||||
import { ProductionQueue } from '../Core/ProductionQueue';
|
||||
import { getNumber } from '../utils';
|
||||
import { Task, uniqTaskId } from '../Queue/TaskProvider';
|
||||
import { ResourceSlot, ResourceSlotDefaults } from '../Game';
|
||||
|
||||
const RESOURCES_KEY = 'resources';
|
||||
const CAPACITY_KEY = 'capacity';
|
||||
const PERFORMANCE_KEY = 'performance';
|
||||
const INCOMING_MERCHANTS_KEY = 'incoming_merchants';
|
||||
const MERCHANTS_INFO_KEY = 'merchants_info';
|
||||
const RESOURCE_SLOTS_KEY = 'resource_slots';
|
||||
const BUILDING_SLOTS_KEY = 'building_slots';
|
||||
const SETTINGS_KEY = 'settings';
|
||||
const QUEUE_ENDING_TIME_KEY = 'queue_ending_time';
|
||||
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 {
|
||||
return this.storage.getTyped<VillageSettings>(SETTINGS_KEY, {
|
||||
factory: () => Object.assign({}, VillageSettingsDefaults),
|
||||
|
@ -6,6 +6,9 @@ import { Resources } from './Core/Resources';
|
||||
import { MerchantsInfo } from './Core/Market';
|
||||
import { VillageStorage } from './Storage/VillageStorage';
|
||||
import { ReceiveResourcesMode } from './Core/Village';
|
||||
import { ResourceType } from './Core/ResourceType';
|
||||
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
|
||||
import * as _ from 'underscore';
|
||||
|
||||
export class VillageController {
|
||||
private readonly villageId: number;
|
||||
@ -147,4 +150,47 @@ export class VillageController {
|
||||
const newSettings = { ...this.state.settings, receiveResourcesMode: next };
|
||||
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) {
|
||||
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)) {
|
||||
throw new Error(`Task "${name}" is not production task`);
|
||||
}
|
||||
|
||||
if (args.villageId !== this.villageId) {
|
||||
throw new Error(
|
||||
`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();
|
||||
tasks.push(task);
|
||||
this.storage.storeTaskList(tasks);
|
||||
return new Task(uniqTaskId(), 0, name, { villageId: this.villageId, ...args });
|
||||
}
|
||||
|
||||
removeTask(taskId: TaskId) {
|
||||
|
Loading…
Reference in New Issue
Block a user