Skip to content

Commit

Permalink
Merge pull request #81 from Front-znad-zatoki/feature/#80-trade-modal
Browse files Browse the repository at this point in the history
Feature/#80 trade modal
  • Loading branch information
Enessetere authored Feb 5, 2021
2 parents ec39929 + 3581545 commit aa62f6a
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 27 deletions.
32 changes: 19 additions & 13 deletions src/app/Trade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import { multiply } from 'lodash';
import { Player } from '../Player';
import { Herd } from './logic/Herd';

export type Offer = [AnimalNames, number];

export class Trade {
constructor(private bank: Player) {}
get thisBank(): Player {
return this.bank;
}
/**
* Gets an offer from player and returns true or false if transaction can be made processed and process it if possible
* @param offer accepts tuple with offer containing animal name and quantity to be sold
Expand All @@ -14,13 +19,17 @@ export class Trade {
* @returns true if transaction will be processed, and false otherwise
*/
processOffer(
offer: [AnimalNames, number],
offer: Offer,
{ theHerd: playerHerd }: Player,
target: [AnimalNames, number],
target: Offer,
): boolean {
const [offeredAnimal, offeredAnimalCount] = offer;
const [targetAnimal, targetAnimalCount] = target;
if (
playerHerd.getAnimalNumber(offer[0]) < offer[1] ||
this.bank.theHerd.getAnimalNumber(target[0]) < target[1]
playerHerd.getAnimalNumber(offeredAnimal) <
offeredAnimalCount ||
this.bank.theHerd.getAnimalNumber(targetAnimal) <
targetAnimalCount
) {
return false;
}
Expand All @@ -35,17 +44,14 @@ export class Trade {
: this.disposeResult(offer, playerHerd, target);
}

private calculateValue(offer: [AnimalNames, number]): number {
private calculateValue([animal, count]: Offer): number {
return multiply(
ConvertToAnimalObject.convertToAnimalObject(offer[0]).theValue,
offer[1],
ConvertToAnimalObject.convertToAnimalObject(animal).theValue,
count,
);
}

private adjustOffer(
offer: [AnimalNames, number],
target: [AnimalNames, number],
): void {
private adjustOffer(offer: Offer, target: Offer): void {
offer[1] -= 1;
if (this.calculateValue(offer) <= this.calculateValue(target)) {
return;
Expand All @@ -57,9 +63,9 @@ export class Trade {
* updates players and banks herd
*/
private disposeResult(
[animalSold, quantitySold]: [AnimalNames, number],
[animalSold, quantitySold]: Offer,
playerHerd: Herd,
[animalBought, quantityBought]: [AnimalNames, number],
[animalBought, quantityBought]: Offer,
): boolean {
playerHerd.addAnimalsToHerd(animalSold, -quantitySold);
playerHerd.addAnimalsToHerd(animalBought, quantityBought);
Expand Down
31 changes: 31 additions & 0 deletions src/app/components/EmptyModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Render } from '../utils/Render';
import { pull } from 'lodash';

export abstract class EmptyModal {
modal: HTMLElement;
modalContainer: HTMLElement;
constructor() {
this.modalContainer = Render.elementFactory('div', {
className: 'modal__container',
});
this.modal = Render.elementFactory(
'div',
{ className: 'modal' },
this.modalContainer,
);
}

/**
* Hides modal - adds class with display:none
*/
protected hideModal(): void {
this.modal.classList.add('modal--hidden');
}

/**
* Shows modal - removes class with display:none
*/
protected showModal(): void {
this.modal.classList.remove('modal--hidden');
}
}
16 changes: 2 additions & 14 deletions src/app/components/ModalBasic.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import { Render } from '../utils/Render';
import { Button } from './Button';
import { EmptyModal } from './EmptyModal';

export class ModalBasic {
modal: HTMLElement;
modalContainer: HTMLElement;
constructor() {
this.modalContainer = Render.elementFactory('div', {
className: 'modal__container',
});
this.modal = Render.elementFactory(
'div',
{ className: 'modal' },
this.modalContainer,
);
}

export class ModalBasic extends EmptyModal {
/**
* Creates and appends the main modal structure.
* @param {string} heading Heading to be render inside the modal.
Expand Down
220 changes: 220 additions & 0 deletions src/app/components/TradeModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { AnimalNames } from '~src/Enums/AnimalNamesEnum';
import { Player } from '~src/Player';
import { Herd } from '../logic/Herd';
import { Offer, Trade } from '../Trade';
import { Render } from '../utils/Render';
import { EmptyModal } from './EmptyModal';

export class TradeModal extends EmptyModal {
tradeForm: HTMLElement;
playerView: HTMLElement;
bankView: HTMLElement;
warning: HTMLElement;
backButton: HTMLElement;
player: Player;

constructor(private trade: Trade, firstPlayer: Player) {
super();
this.player = firstPlayer;
this.playerView = Render.elementFactory('div', {
className: 'trade__player-wrapper',
});
this.bankView = Render.elementFactory('div', {
className: 'trade__player-wrapper',
});
this.tradeForm = Render.elementFactory(
'form',
{
action: '',
method: 'post',
className: 'trade',
},
this.playerView,
Render.elementFactory('input', {
type: 'submit',
value: 'Trade',
className: 'trade__submit',
}),
this.bankView,
);
this.warning = Render.elementFactory('p', {
className: 'warning',
});
this.backButton = Render.elementFactory('button', {}, 'back');
Render.childrenInjector(
this.modalContainer,
this.tradeForm,
this.warning,
this.backButton,
);
}

/**
* Creates TradeModal and returns it as HTMLElement.
*/
createModal(): HTMLElement {
Render.childrenInjector(
this.bankView,
this.createHerdView(this.trade.thisBank, true),
);
Render.childrenInjector(
this.playerView,
this.createHerdView(this.player),
);
this.tradeForm.addEventListener('submit', this.handleSubmit);
this.modal.addEventListener('keydown', this.clearWarning);
this.modal.addEventListener('click', this.clearWarning);
this.backButton.addEventListener('click', () => this.hideModal());
return this.modal;
}

/**
* Sets next player and his herd in the TradeModal.
*/
setNextPlayer(player: Player): void {
this.player = player;
Render.removeAllChildren(this.playerView);
this.playerView.appendChild(this.createHerdView(this.player));
this.updateBank();
this.showModal();
}

private updateBank(): void {
Render.removeAllChildren(this.bankView);
this.bankView.appendChild(
this.createHerdView(this.trade.thisBank, true),
);
}

private createHerdView(
{ theHerd: herd, theName: name }: Player,
isBank = false,
): HTMLElement {
const herdElement = Render.elementFactory(
'div',
{
className: 'trade__player-herd',
},
...this.generateAnimalRows(herd, isBank),
);
const container = Render.elementFactory(
'div',
{
className: 'trade__player',
},
Render.elementFactory(
'H2',
{ className: 'trade__player-heading' },
`${name}`,
),
herdElement,
);
return container;
}

private generateAnimalRows(
herd: Herd,
isBank: boolean,
): HTMLElement[] {
const animalsRows: HTMLElement[] = herd.theAnimals.reduce(
(animalsElements: HTMLElement[], [animal, count]) => {
if (count > 0) {
animalsElements.push(
Render.elementFactory(
'div',
{ className: 'trade__row' },
Render.elementFactory(
'label',
{
for: `${isBank ? 'bank' : 'player'}_${
animal.theName
}`,
},
`${animal.theName}: ${count}`,
),
Render.elementFactory('input', {
type: 'number',
id: `${isBank ? 'bank' : 'player'}_${animal.theName}`,
name: `${isBank ? 'bank' : 'player'}_${
animal.theName
}`,
min: '0',
max: `${count}`,
}),
),
);
}
return animalsElements;
},
[],
);

return animalsRows;
}

private formDataIntoTuples(formData: FormData): [Offer[], Offer[]] {
const offer: Offer[] = [];
const target: Offer[] = [];
for (const [key, value] of formData.entries()) {
const numberOfAnimals = parseInt(value.toString());
const [player, animal] = key.split('_');
if (numberOfAnimals > 0) {
switch (player) {
case 'player': {
offer.push([animal as AnimalNames, numberOfAnimals]);
break;
}
case 'bank': {
target.push([animal as AnimalNames, numberOfAnimals]);
break;
}
}
}
}
return [offer, target];
}

private processTrade([offer, target]: [Offer[], Offer[]]): boolean {
if (offer.length === 1 && target.length === 1) {
const [[offeredAnimal]] = offer;
const [[targetAnimal]] = target;
if (this.trade.processOffer(offer[0], this.player, target[0])) {
return true;
}
this.displayWarning(
`The value ratio of the ${offeredAnimal}s to ${targetAnimal}s is not correct`,
);
return false;
}
if (offer.length > 1 || target.length > 1) {
this.displayWarning(
'To much types of animals, allowed one type for one type',
);
return false;
}
if (offer.length <= 0 || target.length <= 0) {
this.displayWarning(
'There need to be at least one animal on both sides',
);
return false;
}
return false;
}

private displayWarning(message: string): void {
this.warning.textContent = message;
}

private handleSubmit = (event: Event): void => {
event.preventDefault();
const formData = new FormData(event.target as HTMLFormElement);
const data = this.formDataIntoTuples(formData);
if (this.processTrade(data)) {
this.hideModal();
}
};

private clearWarning = (): void => {
this.warning.textContent = '';
};
}
49 changes: 49 additions & 0 deletions src/app/manuals/TradeModalDemo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { AnimalNames } from '~src/Enums/AnimalNamesEnum';
import { Player } from '~src/Player';
import { TradeModal } from '../components/TradeModal';
import { Bank } from '../logic/Bank';
import { Trade } from '../Trade';
import { Render } from '../utils/Render';

//type TradeModalDemo.playDemo() in init in App.ts
export class TradeModalDemo {
static playDemo(): void {
const trade = new Trade(new Bank());
const player = new Player(
'Donald',
'./static/images/avatars/dog.png',
);
player.theHerd.addAnimalsToHerd(AnimalNames.RABBIT, 20);
player.theHerd.addAnimalsToHerd(AnimalNames.SHEEP, 5);
player.theHerd.addAnimalsToHerd(AnimalNames.PIG, 5);
player.theHerd.addAnimalsToHerd(AnimalNames.COW, 2);
player.theHerd.addAnimalsToHerd(AnimalNames.HORSE, 1);
const modal = new TradeModal(trade, player);
Render.render('#sf-app', modal.createModal());

const player2 = new Player(
'Gerwazy',
'./static/images/avatars/dog.png',
);
player2.theHerd.addAnimalsToHerd(AnimalNames.RABBIT, 6);
player2.theHerd.addAnimalsToHerd(AnimalNames.SHEEP, 1);
player2.theHerd.addAnimalsToHerd(AnimalNames.PIG, 2);
player2.theHerd.addAnimalsToHerd(AnimalNames.COW, 1);
player2.theHerd.addAnimalsToHerd(AnimalNames.HORSE, 1);

const player3 = new Player(
'Eustachy',
'./static/images/avatars/dog.png',
);
player3.theHerd.addAnimalsToHerd(AnimalNames.RABBIT, 8);
player3.theHerd.addAnimalsToHerd(AnimalNames.SHEEP, 3);
player3.theHerd.addAnimalsToHerd(AnimalNames.PIG, 1);
player3.theHerd.addAnimalsToHerd(AnimalNames.COW, 2);
player3.theHerd.addAnimalsToHerd(AnimalNames.HORSE, 0);

setTimeout(() => {
modal.setNextPlayer(player2);
setTimeout(() => modal.setNextPlayer(player3), 15000);
}, 15000);
}
}
Loading

0 comments on commit aa62f6a

Please sign in to comment.