Add fast resource transfer management

This commit is contained in:
Anton Vakhrushev 2020-05-08 21:00:47 +03:00
parent f648f7dd5a
commit 304c192ab0
8 changed files with 79 additions and 50 deletions

View File

@ -4,7 +4,6 @@ import { Resources } from '../Core/Resources';
import { Coordinates, Village } from '../Core/Village'; import { Coordinates, Village } from '../Core/Village';
import { grabVillageResources } from '../Page/ResourcesBlock'; import { grabVillageResources } from '../Page/ResourcesBlock';
import { grabActiveVillageId, grabVillageList } from '../Page/VillageBlock'; import { grabActiveVillageId, grabVillageList } from '../Page/VillageBlock';
import { SendResourcesTask } from '../Task/SendResourcesTask';
import { aroundMinutes, timestamp } from '../utils'; import { aroundMinutes, timestamp } from '../utils';
import { VillageStorage } from '../Storage/VillageStorage'; import { VillageStorage } from '../Storage/VillageStorage';
import { Args } from '../Queue/Args'; import { Args } from '../Queue/Args';
@ -16,34 +15,18 @@ const TIMEOUT = 15;
@registerAction @registerAction
export class SendResourcesAction extends ActionController { export class SendResourcesAction extends ActionController {
async run(args: Args, task: Task): Promise<any> { async run(args: Args, task: Task): Promise<any> {
const resources = Resources.fromObject(args.resources || err('No resources'));
const coordinates = Coordinates.fromObject(args.coordinates || err('No coordinates')); const coordinates = Coordinates.fromObject(args.coordinates || err('No coordinates'));
console.log('Send', resources, 'to', coordinates);
const recipientVillage = args.targetVillageId const recipientVillage = args.targetVillageId
? this.findRecipientVillageById(args.targetVillageId) ? this.findRecipientVillageById(args.targetVillageId)
: this.findRecipientVillage(coordinates); : this.findRecipientVillage(coordinates);
const readyToTransfer = this.getResourcesForTransfer(recipientVillage.id).min(resources); const readyToTransfer = this.getResourcesForTransfer(recipientVillage.id);
const remainingResources = resources.sub(readyToTransfer).max(Resources.zero());
console.log('Total res', resources);
console.log('To transfer res', readyToTransfer); console.log('To transfer res', readyToTransfer);
console.log('Remaining res', remainingResources);
if (!remainingResources.empty()) { // Schedule recurrent task
console.log('Schedule next', remainingResources); this.scheduler.scheduleTask(task.name, task.args, timestamp() + aroundMinutes(TIMEOUT));
this.scheduler.scheduleTask(
SendResourcesTask.name,
{
...args,
resources: remainingResources,
},
timestamp() + aroundMinutes(TIMEOUT)
);
}
fillSendResourcesForm(readyToTransfer, coordinates); fillSendResourcesForm(readyToTransfer, coordinates);
clickSendButton(); clickSendButton();

View File

@ -41,6 +41,8 @@ interface GameState {
removeTask(taskId: string): void; removeTask(taskId: string): void;
refreshVillages(): void; refreshVillages(): void;
pause(): void; pause(): void;
scheduleResourceTransferTasks(fromVillageId: number, toVillageId: number): void;
dropResourceTransferTasks(fromVillageId: number, toVillageId: number): void;
} }
export class ControlPanel { export class ControlPanel {
@ -101,6 +103,14 @@ export class ControlPanel {
pause() { pause() {
executionState.setExecutionSettings({ pauseTs: timestamp() + 120 }); executionState.setExecutionSettings({ pauseTs: timestamp() + 120 });
}, },
scheduleResourceTransferTasks(fromVillageId: number, toVillageId: number): void {
scheduler.scheduleResourceTransferTasks(fromVillageId, toVillageId);
},
dropResourceTransferTasks(fromVillageId: number, toVillageId: number): void {
scheduler.dropResourceTransferTasks(fromVillageId, toVillageId);
},
}; };
state.refresh(); state.refresh();
@ -109,11 +119,7 @@ export class ControlPanel {
state.refresh(); state.refresh();
}, 3000); }, 3000);
DataStorage.onChange( DataStorage.onChange(() => state.refresh());
_.debounce(() => {
state.refresh();
}, 500)
);
const getBuildingsInQueue = () => const getBuildingsInQueue = () =>
this.scheduler this.scheduler

View File

@ -172,7 +172,15 @@
class="village-quick-link" class="village-quick-link"
:class="{ active: villageState.shipment.includes(s.id) }" :class="{ active: villageState.shipment.includes(s.id) }"
:href="marketPath(villageState.village, s.village)" :href="marketPath(villageState.village, s.village)"
:title="'Отправить ресурсы из ' + villageState.village.name + ' в ' + s.village.name" :title="
'Отправить ресурсы из ' +
villageState.village.name +
' в ' +
s.village.name +
'(shift+click - настроить отправку)'
"
v-on:click.prevent.shift.exact="setupResourceTransfer(villageState, s)"
v-on:click.prevent.exact="goToMarket(villageState.village, s.village)"
>$->{{ s.village.name }}</a >$->{{ s.village.name }}</a
> >
<a class="village-quick-link" :href="quartersPath(villageState.village)">Казармы</a> <a class="village-quick-link" :href="quartersPath(villageState.village)">Казармы</a>
@ -253,6 +261,20 @@ export default {
} }
return secondsToTime(value.seconds); return secondsToTime(value.seconds);
}, },
goToMarket(fromVillage, toVillage) {
window.location.assign(this.marketPath(fromVillage, toVillage));
},
setupResourceTransfer(villageState, toVillageState) {
villageState.shipment.includes(toVillageState.id)
? this.dropResourceTransferTasks(villageState.id, toVillageState.id)
: this.scheduleResourceTransferTasks(villageState.id, toVillageState.id);
},
scheduleResourceTransferTasks(fromVillageId, toVillageId) {
this.shared.scheduleResourceTransferTasks(fromVillageId, toVillageId);
},
dropResourceTransferTasks(fromVillageId, toVillageId) {
this.shared.dropResourceTransferTasks(fromVillageId, toVillageId);
},
}, },
}; };
</script> </script>

View File

@ -5,23 +5,17 @@ import { IncomingMerchant } from '../../Core/Market';
import { grabResourcesFromList } from './BuildingPage'; import { grabResourcesFromList } from './BuildingPage';
interface SendResourcesClickHandler { interface SendResourcesClickHandler {
(resources: Resources, crd: Coordinates, scale: number): void; (resources: Resources, crd: Coordinates): void;
} }
export function createSendResourcesButton(onClickHandler: SendResourcesClickHandler) { export function createSendResourcesButton(onClickHandler: SendResourcesClickHandler) {
const id1 = uniqId(); const id = uniqId();
const id10 = uniqId();
const id100 = uniqId();
const id1000 = uniqId();
jQuery('#button').before(`<div style="padding: 8px"> jQuery('#button').before(`<div style="padding: 8px">
<a id="${id1}" href="#">Отправить</a> / <a id="${id}" href="#">Отправить</a> /
<a id="${id10}" href="#">x10</a> /
<a id="${id100}" href="#">x100</a> /
<a id="${id1000}" href="#">x1000</a>
</div>`); </div>`);
const createHandler = (handler: SendResourcesClickHandler, scale: number) => (evt: JQuery.Event) => { const createHandler = () => (evt: JQuery.Event) => {
evt.preventDefault(); evt.preventDefault();
const sendSelect = jQuery('#send_select'); const sendSelect = jQuery('#send_select');
const resources = new Resources( const resources = new Resources(
@ -31,13 +25,10 @@ export function createSendResourcesButton(onClickHandler: SendResourcesClickHand
getNumber(sendSelect.find('#r4').val()) getNumber(sendSelect.find('#r4').val())
); );
const crd = new Coordinates(getNumber(jQuery('#xCoordInput').val()), getNumber(jQuery('#yCoordInput').val())); const crd = new Coordinates(getNumber(jQuery('#xCoordInput').val()), getNumber(jQuery('#yCoordInput').val()));
onClickHandler(resources, crd, scale); onClickHandler(resources, crd);
}; };
jQuery(`#${id1}`).on('click', createHandler(onClickHandler, 1)); jQuery(`#${id}`).on('click', createHandler());
jQuery(`#${id10}`).on('click', createHandler(onClickHandler, 10));
jQuery(`#${id100}`).on('click', createHandler(onClickHandler, 100));
jQuery(`#${id1000}`).on('click', createHandler(onClickHandler, 1000));
} }
export function grabMerchantsInfo() { export function grabMerchantsInfo() {

View File

@ -56,7 +56,7 @@ export class BuildingPageController {
} }
if (isMarketSendResourcesPage()) { if (isMarketSendResourcesPage()) {
createSendResourcesButton((res, crd, scale) => this.onSendResources(res, crd, scale)); createSendResourcesButton((res, crd) => this.onSendResources(res, crd));
} }
if (isForgePage()) { if (isForgePage()) {
@ -99,7 +99,7 @@ export class BuildingPageController {
notify(`Training ${count} troopers scheduled`); notify(`Training ${count} troopers scheduled`);
} }
private onSendResources(resources: Resources, coordinates: Coordinates, scale: number) { private onSendResources(resources: Resources, coordinates: Coordinates) {
const villageId = grabActiveVillageId(); const villageId = grabActiveVillageId();
const targetVillage = grabVillageList().find(v => v.crd.eq(coordinates)); const targetVillage = grabVillageList().find(v => v.crd.eq(coordinates));
this.scheduler.scheduleTask(SendResourcesTask.name, { this.scheduler.scheduleTask(SendResourcesTask.name, {
@ -108,10 +108,9 @@ export class BuildingPageController {
buildTypeId: this.attributes.buildTypeId, buildTypeId: this.attributes.buildTypeId,
buildId: this.attributes.buildId, buildId: this.attributes.buildId,
tabId: this.attributes.tabId, tabId: this.attributes.tabId,
resources: resources.scale(scale),
coordinates, coordinates,
}); });
notify(`Send resources ${JSON.stringify(resources)} from ${villageId} to ${JSON.stringify(coordinates)}`); notify(`Send resources from ${villageId} to ${JSON.stringify(coordinates)}`);
} }
private onResearch(resources: Resources, unitId: number) { private onResearch(resources: Resources, unitId: number) {

View File

@ -42,9 +42,11 @@ export class TaskQueue {
return modifiedCount; return modifiedCount;
} }
remove(id: TaskId) { remove(predicate: (t: Task) => boolean): number {
const [_, items] = this.shiftTask(id); const [_, other] = this.split(predicate);
this.flushItems(items); const result = other.length;
this.flushItems(other);
return result;
} }
seeItems(): ImmutableTaskList { seeItems(): ImmutableTaskList {

View File

@ -13,6 +13,8 @@ import { Args } from './Queue/Args';
import { ImmutableTaskList, Task, TaskId } from './Queue/TaskProvider'; import { ImmutableTaskList, Task, TaskId } from './Queue/TaskProvider';
import { ForgeImprovementTask } from './Task/ForgeImprovementTask'; import { ForgeImprovementTask } from './Task/ForgeImprovementTask';
import { getTaskType, TaskType } from './Task/TaskMap'; import { getTaskType, TaskType } from './Task/TaskMap';
import { MARKET_ID } from './Core/Buildings';
import { grabVillageList } from './Page/VillageBlock';
export enum ContractType { export enum ContractType {
UpgradeBuilding, UpgradeBuilding,
@ -84,7 +86,7 @@ export class Scheduler {
} }
removeTask(taskId: TaskId) { removeTask(taskId: TaskId) {
this.taskQueue.remove(taskId); this.taskQueue.remove(t => t.id === taskId);
this.actionQueue.clear(); this.actionQueue.clear();
} }
@ -172,6 +174,30 @@ export class Scheduler {
return tasks.map(t => t.args.targetVillageId!); return tasks.map(t => t.args.targetVillageId!);
} }
dropResourceTransferTasks(fromVillageId: number, toVillageId: number): void {
this.taskQueue.remove(
t =>
t.name === SendResourcesTask.name &&
t.args.villageId === fromVillageId &&
t.args.targetVillageId === toVillageId
);
}
scheduleResourceTransferTasks(fromVillageId: number, toVillageId: number): void {
this.dropResourceTransferTasks(fromVillageId, toVillageId);
const village = grabVillageList().find(v => v.id === toVillageId);
if (!village) {
throw new Error('No village');
}
this.scheduleTask(SendResourcesTask.name, {
villageId: fromVillageId,
targetVillageId: toVillageId,
coordinates: village.crd,
buildTypeId: MARKET_ID,
tabId: 5,
});
}
private reorderVillageTasks(villageId: number) { private reorderVillageTasks(villageId: number) {
const tasks = this.taskQueue.seeItems(); const tasks = this.taskQueue.seeItems();

View File

@ -24,7 +24,7 @@ describe('Task Queue', function() {
it('Can remove task by id', function() { it('Can remove task by id', function() {
const provider = new ArrayTaskProvider([new Task('id1', 1, 'task1', {}), new Task('id2', 2, 'task2', {})]); const provider = new ArrayTaskProvider([new Task('id1', 1, 'task1', {}), new Task('id2', 2, 'task2', {})]);
const queue = new TaskQueue(provider, new NullLogger()); const queue = new TaskQueue(provider, new NullLogger());
queue.remove('id1'); queue.remove(t => t.id === 'id1');
const tasks = provider.getTasks(); const tasks = provider.getTasks();
expect(tasks).to.have.lengthOf(1); expect(tasks).to.have.lengthOf(1);
expect(tasks[0].ts).to.be.equals(2); expect(tasks[0].ts).to.be.equals(2);