In the last post, we created the mod table. The next logical step is to create gear which can use that mod table. I decided to skip making a class for now and go right into mongoose schemas because every single time a piece of gear is changed it needs to be saved to the DB. Below you'll see GearItem.ts, the main thing is the method 'rollItem'. This method uses the mod table from the last post. Right now all the types are loose because I'm not sure what a piece of gear will look like exactly yet.
import mongoose from "mongoose";
import { gearSlots } from "../types/gear/GearSlots";
import { itemClasses } from "../types/items/ItemClasses";
import { createModTiers } from "../setup/mods/createModTiers";
import { ModFinal, Mod } from "../setup/mods/createModTiers";
//a helper function to 'amount' to the stats
function toModFinal(mod: Mod): ModFinal{
return {
...mod,
stats: mod.stats.map((stat) => ({
...stat,
amount: stat.min + Math.floor(Math.random() * (stat.max - stat.min + 1))
}))
}
}
const MODS = createModTiers();
let itemRarityMap = {
'normal': 1,
'magic': 2,
'rare': 3
}
export interface IGearItem {
uid: mongoose.Types.ObjectId;
name: string;
base: string;
slots: string;
level: number;
equipped: boolean;
equipped_slot: string | null;
item_class: string;
rarity: 'normal' | 'magic' | 'rare';
mods: any[];
rollItem(): any;
extractStats(): any;
}
export const gearItemSchema = new mongoose.Schema<IGearItem>({
uid: { type: mongoose.Schema.Types.ObjectId, required: true },
name: { type: String, required: true },
base: { type: String, required: true },
slots: { type: String, enum: gearSlots, required: true },
level: { type: Number, required: true },
equipped: { type: Boolean, required: true },
equipped_slot: { type: String, enum: gearSlots, default: null },
item_class: { type: String, enum: itemClasses, required: true },
rarity: { type: String, enum: ['normal', 'magic', 'rare'], required: true },
mods: { type: mongoose.Schema.Types.Mixed, default: [] }
});
gearItemSchema.methods.rollItem = function(this: IGearItem){
//MODS is the mod table from last post
let mods = MODS[this.item_class];
//remove mods above the items ilvl
let filteredMods = mods.filter((mod) => mod.required_level <= this.level);
let totalWeight = filteredMods.reduce((acc, cur) => acc += cur.weight, 0);
let tempMods: ModFinal[] = [];
//the rarity of the item will determine how many mods it can have
//right now for simplicity each rarity has a fixed number of mods
for(let i = 0; i < itemRarityMap[this.rarity]; i++){
let randModRange = Math.floor(Math.random() * totalWeight);
let pointer = {
index: 0,
sum: 0
};
//brute force select a mod
while(pointer.sum < randModRange){
pointer.sum += filteredMods[pointer.index].weight;
pointer.index++;
}
if(pointer.index > 0) pointer.index--;
let selectedMod = toModFinal(mods[pointer.index]);
tempMods.push(selectedMod);
let selectedType = selectedMod.type;
//we need to select more mods, but they can't be the of the same type
//of the mod we just selected, so get rid of them
let totalWeightofSelectedType = filteredMods.filter((mod) => mod.type === selectedType)
.reduce((acc, cur) => acc += cur.weight, 0);
totalWeight -= totalWeightofSelectedType;
filteredMods = filteredMods.filter((mod) => mod.type !== selectedType);
}
//save the mods to the item
this.mods = tempMods;
//return the stats from the item for convenience
return this.extractStats();
}
gearItemSchema.methods.extractStats = function(this: IGearItem){
return this.mods.flatMap((mod: any) => mod.stats);
}
export default mongoose.model<IGearItem>('GearItem', gearItemSchema);Now we have gear that we can create, delete, and roll. This is all in the early stages and subject to change, also I haven't tested anything yet so if there's bugs I'm sorry. Until next time.