travian/src/Action/SendResourcesAction.ts

119 lines
4.9 KiB
TypeScript

import { ActionController, registerAction } from './ActionController';
import { taskError, TryLaterError } from '../Errors';
import { Resources } from '../Core/Resources';
import { Coordinates } from '../Core/Village';
import { aroundMinutes, timestamp } from '../utils';
import { Args } from '../Queue/Args';
import { Task } from '../Queue/TaskProvider';
import { clickSendButton, fillSendResourcesForm, grabMerchantsInfo } from '../Page/BuildingPage/MarketPage';
import { VillageState } from '../VillageState';
@registerAction
export class SendResourcesAction extends ActionController {
async run(args: Args, task: Task): Promise<any> {
this.ensureSameVillage(args, task);
const senderVillageId = args.villageId || taskError('No source village id');
const targetVillageId = args.targetVillageId || taskError('No target village id');
const coordinates = Coordinates.fromObject(args.coordinates || taskError('No coordinates'));
const senderVillage = this.villageStateRepository.getVillageState(senderVillageId);
const recipientVillage = this.villageStateRepository.getVillageState(targetVillageId);
const readyToTransfer = this.getResourcesForTransfer(senderVillage, recipientVillage);
console.log('To transfer res', readyToTransfer);
// Schedule recurrent task
const timeout = senderVillage.settings.sendResourcesTimeout;
this.scheduler.scheduleTask(task.name, task.args, timestamp() + aroundMinutes(timeout));
fillSendResourcesForm(readyToTransfer, coordinates);
clickSendButton();
}
private getMerchantsCapacity(timeout: number): number {
const merchants = grabMerchantsInfo();
const capacity = merchants.available * merchants.carry;
if (!capacity) {
throw new TryLaterError(aroundMinutes(timeout), 'No merchants');
}
return capacity;
}
private getSenderAvailableResources(senderState: VillageState): Resources {
const balance = senderState.required.balance;
const free = balance.max(Resources.zero());
console.table([
{ name: 'Sender balance', ...balance },
{ name: 'Sender free', ...free },
]);
const amount = free.amount();
const threshold = senderState.settings.sendResourcesThreshold;
const timeout = senderState.settings.sendResourcesTimeout;
if (amount < threshold) {
throw new TryLaterError(
aroundMinutes(timeout),
`No free resources (amount ${amount} < threshold ${threshold})`
);
}
return free;
}
private getRecipientRequirements(recipientState: VillageState, timeout: number): Resources {
const maxPossibleToStore = recipientState.storage.capacity.sub(recipientState.performance);
const currentResources = recipientState.resources;
const incomingResources = recipientState.incomingResources;
const requirementResources = recipientState.required.resources;
const missingResources = requirementResources
.min(maxPossibleToStore)
.sub(incomingResources)
.sub(currentResources)
.max(Resources.zero());
console.table([
{ name: 'Recipient max possible', ...maxPossibleToStore },
{ name: 'Recipient resources', ...currentResources },
{ name: 'Recipient incoming', ...incomingResources },
{ name: 'Recipient requirements', ...requirementResources },
{ name: 'Recipient missing', ...missingResources },
]);
if (missingResources.empty()) {
throw new TryLaterError(aroundMinutes(timeout), 'No missing resources');
}
return missingResources;
}
private getResourcesForTransfer(senderState: VillageState, recipientState: VillageState): Resources {
const timeout = senderState.settings.sendResourcesTimeout;
const senderReadySendResources = this.getSenderAvailableResources(senderState);
const recipientNeedsResources = this.getRecipientRequirements(recipientState, timeout);
const contractResources = senderReadySendResources.min(recipientNeedsResources);
const merchantsCapacity = this.getMerchantsCapacity(timeout);
let readyToTransfer = contractResources;
if (contractResources.amount() > merchantsCapacity) {
const merchantScale = merchantsCapacity / contractResources.amount();
readyToTransfer = contractResources.scale(merchantScale);
}
console.log('Merchants capacity', merchantsCapacity);
console.table([
{ name: 'Sender', ...senderReadySendResources },
{ name: 'Recipient', ...recipientNeedsResources },
{ name: 'Prepared', ...contractResources },
{ name: 'Ready to transfer', ...readyToTransfer },
]);
return readyToTransfer;
}
}