large equipment modification to support integer ids for shop items
authorxangelo <git@xangelo.ca>
Fri, 16 Jun 2023 18:00:55 +0000 (14:00 -0400)
committerxangelo <git@xangelo.ca>
Fri, 16 Jun 2023 18:01:34 +0000 (14:01 -0400)
For now we only support being able to buy items from the stores..
eventually you'll be able to craft stuff and probably get custom item
drops.

We also support inventory being created via airtable

migrations/20230616170746_shop_item_int_ids.ts [new file with mode: 0644]
seeds/shop_items.ts
src/events/windcross-events/server.ts
src/server/api.ts
src/server/equipment.ts
src/server/inventory.ts
src/server/shopItem.ts
src/shared/equipped.ts
src/shared/inventory.ts

diff --git a/migrations/20230616170746_shop_item_int_ids.ts b/migrations/20230616170746_shop_item_int_ids.ts
new file mode 100644 (file)
index 0000000..ae98204
--- /dev/null
@@ -0,0 +1,16 @@
+import { Knex } from "knex";
+
+
+export async function up(knex: Knex): Promise<void> {
+  return knex.schema.alterTable('shop_items', function(table) {
+    table.dropColumn('id');
+  }).alterTable('shop_items', function(table) {
+    table.increments('id').primary();
+    table.string('location_id');
+  });
+}
+
+
+export async function down(knex: Knex): Promise<void> {
+}
+
index a704be2a4f45ab6ca866530386f6f8f264fc18cb..26796146e68da804b7416a0dd9d72d23e33e2cee 100644 (file)
@@ -1,84 +1,56 @@
-import {ShopItem, InventoryType, EquipmentSlot} from "../src/shared/inventory";
-import { Knex } from "knex";
-import { join } from 'path';
-import { readFile } from 'fs';
-import { promisify } from 'util';
-import {Profession} from "../src/shared/profession";
-import {SkillID} from "../src/shared/skills";
-import { parse } from "csv-parse/sync";
+import { config as dotenv } from 'dotenv';
+import { db } from "../src/server/lib/db";
+import Airtable from 'airtable';
 
-const read = promisify(readFile);
+dotenv();
 
-export async function seed(knex: Knex): Promise<void> {
-  // Deletes ALL existing entries
-  await knex("shop_items").del();
-  await knex("equipped").del();
-  await knex("inventory").del();
+Airtable.configure({
+  apiKey: process.env.AIRTABLE_API_KEY
+});
 
-  const raw = await read(join(__dirname, '..', 'data', 'inventory.csv'), 'utf8');
-  const stats = {
-    created: 0,
-    skipped: 0
-  };
+const base = Airtable.base('appDfPLPajPNog5Iw');
 
-  const items: Omit<ShopItem, 'id'>[] = [];
+export async function createShopItems(): Promise<void> {
+  return new Promise(async (resolve) => {
+    base('Shop Items').select().eachPage(async (records, next) => {
+      await db('shop_items').insert(records.map(r => {
+        return {
+          id: r.fields.id,
+          name: r.fields.Name,
+          type: r.fields['Equipment Type'],
+          equipment_slot: r.fields['Equipment Slot'],
+          cost: r.fields.Cost,
+          count: 1,
+          requirements: {
+            level: r.fields['Required Level'],
+            strength: r.fields['Required STR'],
+            constitution: r.fields['Required CON'],
+            dexterity: r.fields['Required DEX'],
+            intelligence: r.fields['Required INT']
+          },
+          boosts: {
+            strength: r.fields['Boost STR'],
+            constitution: r.fields['Boost CON'],
+            dexterity: r.fields['Boost DEX'],
+            intelligence: r.fields['Boost INT'],
+            damage: r.fields['Boost DMG']
+          },
+          currentAp: r.fields['Armour Points'],
+          maxAp: r.fields['Armour Points'],
+          affectedSkills: JSON.stringify(r.fields['Affected Skills']),
+          location_id: r.fields.location_id[0]
+        };
 
-  const records = parse(raw, { bom: true, skip_empty_lines: true });
-  records.slice(1).forEach((pieces: string[]) => {
-    if(pieces.length === 17 && pieces[0].length) {
-      console.log('Adding', pieces[0]);
-      stats.created++;
-      const [
-        name,
-        type,
-        equipment_slot,
-        profession,
-        affectedSkills,
-        cost,
-        requirement_level,
-        requirement_strength,
-        requirement_constitution,
-        requirement_dexterity,
-        requirement_intelligence,
-        boost_strength,
-        boost_constitution,
-        boost_dexterity,
-        boost_intelligence,
-        boost_damage,
-        maxAp
-      ] = pieces;
-      items.push({
-        name,
-        type: type as InventoryType,
-        equipment_slot: equipment_slot as EquipmentSlot,
-        profession: profession as Profession,
-        cost: parseInt(cost),
-        count: 1,
-        requirements: {
-          level: parseInt(requirement_level),
-          strength: parseInt(requirement_strength),
-          constitution: parseInt(requirement_constitution),
-          dexterity: parseInt(requirement_dexterity),
-          intelligence: parseInt(requirement_intelligence)
-        },
-        boosts: {
-          strength: parseInt(boost_strength),
-          constitution: parseInt(boost_constitution),
-          dexterity: parseInt(boost_dexterity),
-          intelligence: parseInt(boost_intelligence),
-          damage: parseInt(boost_damage),
-        },
-        currentAp: parseInt(maxAp),
-        maxAp: parseInt(maxAp),
-        affectedSkills: JSON.stringify(affectedSkills.split(',')) as unknown as SkillID[]
-      });
-
-    }
-    else {
-      stats.skipped++;
-    }
+      })).onConflict('id').merge();
+      next();
+    }).finally(() => resolve());
   });
+}
 
-  await knex('shop_items').insert(items);
-  console.log(stats);
-};
+createShopItems().then(() => {
+  console.log('Complete');
+  process.exit(0);
+}).catch(e => {
+  console.log(e);
+  process.exit(1);
+})
index 34897604409ab2fa57a87f115bb510c956b2b8fa..2c3b9cf267256358d6ae0e7ffc4e60a22f2aeea3 100644 (file)
@@ -47,7 +47,8 @@ export const armourer: SocketEvent = {
   eventName: 'city:stores:armourer',
   handler: async (api) => {
     const shopItems = await listShopItems({
-      type: 'ARMOUR'
+      type: 'ARMOUR',
+      location_id: 'armourer'
     })
     api.socket.emit('city:store:armourer', shopItems);
   }
@@ -57,8 +58,9 @@ export const blacksmith: SocketEvent = {
   eventName: 'city:stores:blacksmith',
   handler: async (api) => {
     const shopItems = await listShopItems({
-      type: 'WEAPON'
-    })
+      type: 'WEAPON',
+      location_id: 'blacksmith'
+    });
     api.socket.emit('city:store:blacksmith', shopItems);
   }
 }
@@ -67,7 +69,8 @@ export const mageshop: SocketEvent = {
   eventName: 'city:stores:mageshop',
   handler: async (api) => {
     const shopItems = await listShopItems({
-      type: 'SPELL'
+      type: 'SPELL',
+      location_id: 'mageshop'
     })
     api.socket.emit('city:store:mageshop', shopItems);
   }
index bc83886bf51b611c8ad36251d14c9fc090cbcd26..6eda0fbff233a912260f911c71fc62a57dac2924 100644 (file)
@@ -15,7 +15,6 @@ import {FightRound} from '../shared/fight';
 import {addInventoryItem, deleteInventoryItem, getEquippedItems, getInventory, updateAp} from './inventory';
 import {Monster, MonsterForFight, MonsterForList} from '../shared/monsters';
 import {getShopItem } from './shopItem';
-import { v4 as uuid } from 'uuid';
 import {EquippedItemDetails} from '../shared/equipped';
 import {ArmourEquipmentSlot, EquipmentSlot} from '../shared/inventory';
 import { getAllPaths, getAllServices, getCityDetails } from './map';
@@ -149,7 +148,6 @@ io.on('connection', async socket => {
     const shopItem = await getShopItem(data.id);
 
     if(shopItem) {
-      shopItem.id = uuid();
       if(player.gold < shopItem.cost) {
         socket.emit('alert', {
           type: 'error',
@@ -320,7 +318,6 @@ io.on('connection', async socket => {
     const playerFinalHeal = Math.floor(boost.hp + hpHealAfterMasteries);
 
     roundData.roundDetails.push(`You targeted the monsters ${data.target.toUpperCase()} with ${attackType} damage!`);
-    console.log(monster);
     if(data.target === 'arms') {
       if(monster.armsAp > 0) {
         monster.armsAp -= playerFinalDamage;
index b413707d736f148f764938f43baf60d327fee03a..ad34640b05ee830264c48904b9750649434b23f9 100644 (file)
@@ -1,8 +1,8 @@
 import {db} from "./lib/db";
-import {EquippedItem, EquippedItemDetails} from "../shared/equipped";
+import {EquippedItemDetails} from "../shared/equipped";
 import {EquipmentSlot, InventoryItem} from "../shared/inventory";
 
-export async function getEquippedItems(playerId: string): Promise<EquippedItem[]> {
+export async function getEquippedItems(playerId: string): Promise<EquippedItemDetails[]> {
   return db.raw<EquippedItemDetails[]>(`
                 select i.player_id, 
                 i.item_id,
index 5682eea459ebd3048c36b10c1f1dc6a579bf94bc..1b0c76326aff786dfe65a4e0bbc854a04626e580 100644 (file)
@@ -1,4 +1,5 @@
 import {InventoryItem, ShopItem} from "../shared/inventory";
+import { v4 as uuid } from 'uuid';
 import { db} from './lib/db';
 import {EquippedItemDetails} from "../shared/equipped";
 
@@ -6,7 +7,7 @@ import {EquippedItemDetails} from "../shared/equipped";
 export async function addInventoryItem(playerId: string, item: ShopItem) {
   const inventoryItem: InventoryItem = {
     player_id: playerId,
-    item_id: item.id,
+    item_id: uuid(),
     name: item.name,
     type: item.type,
     equipment_slot: item.equipment_slot,
index 3be63d13d32a9f796c541686ff7e5d8da66235e5..65c2ead2e232cbf7d963ce47fd7a87be240af9a3 100644 (file)
@@ -9,7 +9,7 @@ export function listShopItems(where: Partial<ShopItem>): Promise<ShopItem[]> {
         .orderBy('equipment_slot');
 }
 
-export function getShopItem(id: string): Promise<ShopItem> {
+export function getShopItem(id: number): Promise<ShopItem> {
   return db.select('*').from<ShopItem>('shop_items').where({
     id
   }).first();
index adfdc397424896d8354873f435449758595f24c8..2dc33a793e9e48650421454db91285720e349c0f 100644 (file)
@@ -1,9 +1,10 @@
-import {InventoryItem, InventoryType} from "./inventory";
+import {EquipmentSlot, InventoryItem, InventoryType} from "./inventory";
 
 export type EquippedItem = {
-  item_id: string;
+  item_id: number;
   player_id: string;
   type: InventoryType;
+  equipment_slot: EquipmentSlot
 }
 
 export type EquippedItemDetails = InventoryItem & {
index 32565573b8535ceec3b03b582a19b7ae5a48ab12..0051cf40de65e40405ca72293737255428b5f1b1 100644 (file)
@@ -10,7 +10,7 @@ export type EquipmentSlot = ArmourEquipmentSlot | WeaponEquipmentSlot;
 
 
 export type ShopItem = {
-  id: string;
+  id: number;
   name: string;
   type: InventoryType;
   profession:  Profession;
@@ -34,9 +34,10 @@ export type ShopItem = {
   currentAp: number;
   maxAp: number;
   affectedSkills: SkillID[];
+  location_id: string;
 }
 
-export type InventoryItem = Omit<ShopItem, 'id'> & {
+export type InventoryItem = Omit<ShopItem, 'id' | 'location_id'> & {
   item_id: string;
   player_id: string;
 };