tsc-game
Version:
129 lines (109 loc) • 4.47 kB
text/typescript
import { Observable, Subject, filter, race, takeUntil, timer } from 'rxjs';
import { ResourceInventory, } from '../../../inventory/domain/classes/resource-inventory';
import { Player } from '../../../player/domain/classes/player';
import { Round } from '../../../round/domain/classes/round';
import { OpenTradeOffer, TradeCancel, TradeComplete, TradeOffer, TradeResponse } from '../models/trade.model';
import { Resources } from '../../../resources/domain/models/resources.model';
export class TradeManager {
private tradeOffers: TradeOffer[] = [];
private tradeOfferCount = 0;
private maxTradeOffersPerRound = 3;
private tradeOfferStarted = new Subject<TradeOffer>();
private tradeResponses = new Subject<TradeResponse>();
private tradeCompleted = new Subject<TradeComplete>();
private tradeCancel = new Subject<TradeCancel>();
private openTrades: { [key: string]: OpenTradeOffer } = {}
//todo make timeout interval when created trade configurable via constructor
constructor(private bank: ResourceInventory, private round: Round) {}
public get selectTradeOfferStarted(): Observable<TradeOffer> {
return this.tradeOfferStarted.asObservable();
}
/**
* Emits a `TradeComplete` when a trade is completed.
*/
public get selectTradeCompleted(): Observable<TradeComplete> {
return this.tradeCompleted.asObservable();
}
/**
* Emits a `TradeCancel` event whenever a trade is canceled.
*/
public get selectTradeCancel(): Observable<TradeCancel> {
return this.tradeCancel.asObservable();
}
/**
* emits if a player accepts or denies a trade
*/
public get selectTradeResponse(): Observable<TradeResponse> {
return this.tradeResponses.asObservable();
}
public startTrade(offer: TradeOffer): void {
this.tradeOfferStarted.next(offer);
this.startTradeTimer(offer);
this.openTrades[offer.id] = {
...offer,
playerResponses: {}
}
}
private startTradeTimer(offer: TradeOffer, duration: number = 10_000) {
race([
timer(duration).pipe(
takeUntil(
this.tradeCompleted.pipe(
filter(({trade}) => trade.id === offer.id)
)
),
takeUntil(
this.tradeCancel.pipe(
filter(({tradeId}) => tradeId === offer.id)
)
)
)
]).subscribe(() => {
this.cancelTrade(offer.id)
})
}
public cancelTrade(tradeId: string): void {
this.tradeCancel.next({ tradeId });
delete this.openTrades[tradeId];
}
public respondToTrade(response: TradeResponse): void {
const {tradeId, respondedPlayer, accepted} = response;
this.tradeResponses.next({tradeId, respondedPlayer, accepted });
const openTrade = this.getOpenTrade(tradeId);
openTrade.playerResponses[respondedPlayer.id] = accepted;
this.checkTradeStatus(response)
}
private getOpenTrade(tradeId: string) {
const openTrade = this.openTrades[tradeId]
if(!openTrade) throw new Error("Trade not found");
return openTrade
}
private checkTradeStatus(response: TradeResponse) {
const openTrade = this.getOpenTrade(response.tradeId);
// has enought responses
if(Object.keys(openTrade.playerResponses).length < this.round.players.length - 1) return;
// one player has accepted
const hasOnePlayerAccepted = Object.values(openTrade.playerResponses).find((accepted) => accepted === true)
if(hasOnePlayerAccepted) {
this.completeTrade(openTrade, response.respondedPlayer)
} else {
// no player accepted cancel trade
this.cancelTrade(response.tradeId)
}
}
private completeTrade(offer: TradeOffer, acceptedPlayer: Player): void {
// todo Transfer resources between players and bank
Object.entries(offer.requestedResources).forEach(([key, value]) => {
offer.player.resourceInventory.addToInventory(key as keyof Resources, value);
acceptedPlayer.resourceInventory.removeFromInventory(key as keyof Resources, value);
})
Object.entries(offer.offeredResources).forEach(([key, value]) => {
acceptedPlayer.resourceInventory.addToInventory(key as keyof Resources, value);
offer.player.resourceInventory.removeFromInventory(key as keyof Resources, value);
})
this.tradeCompleted.next({
acceptedPlayer: offer.player,
trade: offer
});
}
}