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 { 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; } }