proc-gen equipment drops from monsters can be picked up
[sketchy-heroes.git] / src / routes / inventory / pick.ts
diff --git a/src/routes/inventory/pick.ts b/src/routes/inventory/pick.ts
new file mode 100644 (file)
index 0000000..b7c29be
--- /dev/null
@@ -0,0 +1,155 @@
+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};
+});