Skip to content

Commit

Permalink
feat(item): can now sell items
Browse files Browse the repository at this point in the history
  • Loading branch information
seiyria committed Jun 24, 2023
1 parent 9839a1d commit 0740d47
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 11 deletions.
50 changes: 42 additions & 8 deletions client/src/app/pages/inventory/inventory.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@
class="dark"
[rows]="items"
[rowHeight]="64"
[columnMode]="'force'"
[sorts]="[{ prop: 'name', dir: 'asc' }]"
>
<ngx-datatable-column name="" [maxWidth]="100" [sortable]="false">
<ngx-datatable-column
name=""
[maxWidth]="100"
[minWidth]="100"
[sortable]="false"
>
<ng-template let-row="row" ngx-datatable-cell-template>
<app-item-icon [item]="row" size="small"></app-item-icon>
</ng-template>
Expand All @@ -48,11 +54,10 @@
</ng-template>
</ngx-datatable-column>

<ngx-datatable-column name="Value">
<ng-template
let-row="row"
ngx-datatable-cell-template
></ng-template>
<ngx-datatable-column name="Value" [comparator]="valueComparison">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ getValueForItem(row) | number }}
</ng-template>
</ngx-datatable-column>

<!--
Expand All @@ -63,11 +68,40 @@
</ngx-datatable-column>
-->

<ngx-datatable-column name="" [sortable]="false">
<ngx-datatable-column name="" [maxWidth]="90" [sortable]="false">
<ng-template
let-row="row"
let-rowIndex="rowIndex"
ngx-datatable-cell-template
></ng-template>
>
<ion-button
class="icon-only"
size="small"
color="tertiary"
(click)="popover.present($event)"
>
<ion-icon name="ellipsis-vertical"></ion-icon>
</ion-button>

<ion-popover
#popover
[showBackdrop]="false"
class="inventory-item-actions"
>
<ng-template>
<ion-content>
<ion-list class="action-list">
<ion-item
class="cursor-pointer"
(click)="sellItem(row, rowIndex); popover.dismiss()"
>
Sell
</ion-item>
</ion-list>
</ion-content>
</ng-template>
</ion-popover>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</ion-card-content>
Expand Down
22 changes: 22 additions & 0 deletions client/src/app/pages/inventory/inventory.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { IEquipment, IItem } from '@interfaces';
import { ContentService } from '@services/content.service';
import { PlayerService } from '@services/player.service';

import { itemValue } from '@helpers/item';
import { Store } from '@ngxs/store';
import { GameplayService } from '@services/gameplay.service';

@Component({
selector: 'app-inventory',
templateUrl: './inventory.page.html',
Expand All @@ -12,8 +16,10 @@ export class InventoryPage implements OnInit {
public items: IItem[] = [];

constructor(
private store: Store,
public playerService: PlayerService,
public contentService: ContentService,
private gameplayService: GameplayService,
) {}

ngOnInit() {
Expand All @@ -38,4 +44,20 @@ export class InventoryPage implements OnInit {
getElementsForItem(item: IItem) {
return (item as IEquipment).elements || [];
}

getValueForItem(item: IItem) {
return itemValue(item);
}

valueComparison(vA: unknown, vB: unknown, itemA: IItem, itemB: IItem) {
return itemValue(itemB) - itemValue(itemA);
}

sellItem(item: IItem, index: number) {
if (!item.instanceId) return;

this.gameplayService.sellItem(item.instanceId).subscribe(() => {
this.items = this.items.filter((i) => i !== item);
});
}
}
6 changes: 6 additions & 0 deletions client/src/app/services/gameplay.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ export class GameplayService {
isWaveBack,
});
}

sellItem(instanceId: string) {
return this.http.post(`${environment.apiUrl}/gameplay/sellitem`, {
instanceId,
});
}
}
5 changes: 5 additions & 0 deletions client/src/styles/ionic-overrides.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ ion-avatar {
display: flex !important;
align-items: center;
}

ion-button.icon-only {
--padding-start: 4px;
--padding-end: 4px;
}
6 changes: 5 additions & 1 deletion server/src/modules/content/content.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';

import { ICollectible, IEquipment, IJob, ILocation } from '@interfaces';
import { ICollectible, IEquipment, IItem, IJob, ILocation } from '@interfaces';
import * as fs from 'fs-extra';
import { Logger } from 'nestjs-pino';

Expand Down Expand Up @@ -84,4 +84,8 @@ export class ContentService {
public getEquipment(equipment: string): IEquipment | undefined {
return this.equipment[equipment];
}

public getItem(item: string): IItem | undefined {
return this.equipment[item] || this.collectibles[item];
}
}
17 changes: 17 additions & 0 deletions server/src/modules/inventory/inventory.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ export class InventoryService {
return this.inventoryItems.find({ userId });
}

async getInventoryItemForUser(
userId: string,
instanceId: string,
): Promise<InventoryItem | null> {
return this.inventoryItems.findOne({ userId, instanceId });
}

async removeInventoryItemForUser(
userId: string,
instanceId: string,
): Promise<any> {
const item = await this.getInventoryItemForUser(userId, instanceId);
if (!item) return;

return this.em.remove<InventoryItem>(item);
}

async isInventoryFull(userId: string): Promise<boolean> {
const count = await this.inventoryItems.count({
userId,
Expand Down
9 changes: 8 additions & 1 deletion server/src/modules/player/gameplay.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class GameplayController {
@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'Explore Event: Take an item' })
@Post('takeitem')
async takeitem(@User() user) {
async takeItem(@User() user) {
if (await this.inventoryService.isInventoryFull(user.userId)) {
throw new BadRequestException('Inventory is full.');
}
Expand All @@ -76,4 +76,11 @@ export class GameplayController {
player: await this.gameplayService.takeItem(user.userId),
};
}

@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'Sell an item' })
@Post('sellitem')
async sellItem(@User() user, @Body('instanceId') instanceId: string) {
return await this.gameplayService.sellItem(user.userId, instanceId);
}
}
29 changes: 29 additions & 0 deletions server/src/modules/player/gameplay.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { itemValue } from '@helpers/item';
import { IItem, ILocation, TrackedStat } from '@interfaces';
import { ConstantsService } from '@modules/content/constants.service';
import { ContentService } from '@modules/content/content.service';
Expand Down Expand Up @@ -390,4 +391,32 @@ export class GameplayService {

return playerPatches;
}

async sellItem(userId: string, instanceId: string) {
if (!instanceId) throw new ForbiddenException('Item instance not found!');

const player = await this.playerService.getPlayerForUser(userId);
if (!player) throw new ForbiddenException('Player not found');

const itemRef = await this.inventoryService.getInventoryItemForUser(
userId,
instanceId,
);
if (!itemRef) throw new ForbiddenException('Item ref not found!');

const item = this.contentService.getItem(itemRef.itemId);
if (!item) throw new ForbiddenException('Item existence not found!');

const coinsGained = itemValue(item);
await this.inventoryService.removeInventoryItemForUser(userId, instanceId);

const playerPatches = await getPatchesAfterPropChanges<Player>(
player,
async (playerRef) => {
this.playerService.gainCoins(playerRef, coinsGained);
},
);

return { player: playerPatches };
}
}
3 changes: 2 additions & 1 deletion shared/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./xp";
export * from './item';
export * from './xp';
42 changes: 42 additions & 0 deletions shared/helpers/item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { IEquipment, IItem, Rarity, Stat } from '../interfaces/';

const multiplierPerRarity: Record<Rarity, number> = {
Common: 1,
Uncommon: 1.25,
Unusual: 1.75,
Rare: 2.5,
Masterful: 4,
Epic: 6.5,
Arcane: 9.5,
Divine: 11,
Unique: 13,
};

const valuePerStat: Record<Stat, number> = {
health: 1,
magic: 3,
power: 5,
resistance: 2,
special: 10,
toughness: 4,
};

export function itemValue(item: IItem): number {
const levelRequirement = (item as IEquipment).levelRequirement ?? 1;
const itemStats = (item as IEquipment).stats ?? {};
const multiplier = item.type === 'collectible' ? 50 : 1;

let value = 1;

Object.keys(itemStats).forEach((stat) => {
const statValue =
(itemStats[stat as Stat] ?? 0) * (valuePerStat[stat as Stat] ?? 0);
value += statValue;
});

value *= multiplier;
value *= multiplierPerRarity[item.rarity] ?? 1;
value *= Math.log(levelRequirement + 1);

return Math.max(1, Math.floor(value));
}

0 comments on commit 0740d47

Please sign in to comment.