--- /dev/null
+import { server } from '@lib/server';
+import { Static, Type } from '@sinclair/typebox';
+import { prisma } from '@lib/db';
+import { ForbiddenError, NotFoundError } from '@lib/http-errors';
+import { logger } from '@lib/logger';
+import {weighted} from '@lib/weighted';
+import {Inventory, Rarity} from '@prisma/client';
+import Decimal from 'decimal.js';
+import { random, sample } from 'lodash';
+
+type rarityDropRates = {
+ rarity: Rarity,
+ weight: number
+};
+
+type StatModifierStat = {
+ name: string;
+ min: number;
+ max: number;
+}
+type StatModifiers = {
+ stats: StatModifierStat[],
+ required: string[]
+};
+
+const WorldDropInput = Type.Object({
+ params: Type.Object({
+ playerId: Type.String(),
+ dropId: Type.String()
+ })
+});
+
+type InputType = Static<typeof WorldDropInput>;
+
+export type pickWorldDropOutput = {
+ item: Inventory
+}
+
+export const pickWorldDrop = server.post<InputType, pickWorldDropOutput>('/v1/accounts/:playerId/pick/:dropId', {
+ schema: WorldDropInput,
+ authenicated: true
+}, async req => {
+ const player = await prisma.player.findUnique({
+ where: {
+ id: req.params.playerId
+ }
+ });
+
+ if(!player) {
+ throw new ForbiddenError();
+ }
+ const drop = await prisma.worldDrop.findUnique({
+ where: {
+ id: req.params.dropId
+ },
+ include: {
+ baseItem: true
+ }
+ });
+
+ if(!drop) {
+ throw new NotFoundError(`Drop ${req.params.dropId} is not valid`);
+ }
+
+ // figure out the item stats and award to the player
+ // 1. get rarity - this functions as a modifier for everything else
+ let selectedRarity = weighted<rarityDropRates>(drop.baseItem.rarityDropRates as rarityDropRates[], i => i.weight);
+
+ if(selectedRarity === undefined) {
+ selectedRarity = {
+ rarity: Rarity.COMMON,
+ weight: 1
+ };
+ }
+
+ console.log(selectedRarity);
+
+ // 2. figure out durability
+ //We're just going to bump durability by ~25% for each rarity type
+ const rarityDurabilityMap = {
+ [Rarity.COMMON]: 1,
+ [Rarity.UNCOMMON]: 1.15,
+ [Rarity.RARE]: 1.26
+ }
+ const durability = Math.ceil(drop.baseItem.durability * rarityDurabilityMap[selectedRarity.rarity]);
+
+ console.log('durability', durability, drop.baseItem, rarityDurabilityMap, selectedRarity.rarity);
+
+ // 3. figure out stat modifiers
+ const stats: Record<string, number> = {
+ pow: 0,
+ zest: 0,
+ woosh: 0,
+ luck: 0,
+ aha: 0,
+ wow: 0,
+ stamina: 0,
+ hp: 0
+ };
+ let selectedStats = 0;
+ const mods = drop.baseItem.statModifiers as StatModifiers;
+ mods.required.forEach(name => {
+ mods.stats.filter(s => s.name === name).forEach(stat => {
+ stats[stat.name] += random(stat.min, stat.max);
+ selectedStats++;
+ });
+ });
+
+ // the item now has the "Common" number of stat modifiers.
+ // uncommon will have 1 extra stat modifier
+ // rare will have 2 extra stat modifiers
+ if(selectedRarity.rarity === Rarity.UNCOMMON) {
+ const stat = sample<StatModifierStat>(mods.stats);
+ if(stat) {
+ selectedStats++;
+ stats[stat.name] += random(stat.min, stat.max);
+ }
+ }
+
+ if(selectedRarity.rarity === Rarity.RARE) {
+ for(let i = 0; i < 2; ++i) {
+ const stat = sample<StatModifierStat>(mods.stats);
+ if(stat) {
+ selectedStats++;
+ stats[stat.name] += random(stat.min, stat.max);
+ }
+ }
+ }
+
+ // 4. Calculate uses until discovery
+ // each stat assigned to an item increases its uses
+ // by 20.
+ // Each rarity level adds another 20
+ const rarityDiscovery = [Rarity.COMMON, Rarity.UNCOMMON, Rarity.RARE];
+ const discover = selectedStats * 20 + (rarityDiscovery.indexOf(selectedRarity.rarity) > 0 ? rarityDiscovery.indexOf(selectedRarity.rarity) * 20 : 0);
+
+
+ // 5. Give the player the new item, but set it to undiscovered
+ const inventoryItem = await prisma.inventory.create({
+ data: {
+ playerId: player.id,
+ itemId: drop.itemId,
+ discovered: false,
+ usesUntilDiscovery: discover,
+ maxDurability: durability,
+ currentDurability: durability,
+ rarity: selectedRarity.rarity,
+ type: drop.baseItem.type,
+ statModifiers: stats,
+ stackable: false
+ }
+ });
+
+ return {item: inventoryItem};
+});