Creating the Mod Table

10/14/25
ARPG Loot Sim #2
Let's see if we can derive a full mod table from the base mods and the item metadata

Introduction

I won't lie, the way I make the mod table is kind of messy, but it's really just 3 steps, and I'll have comments of what's happening and why.

Step 1: Creating Higher Tier Mods

First let's look at the structure at a base mod (subject to change) before showing how to extract higher tiers from it:

  'addedWeaponPhysicalDamage1': {
    type: 'addedPhysFlat',
    weights: [
      { name: 'weapon_physical', weight:  1000, max_tier: N },
      { name: 'ring',            weight:  250,  max_tier: N-4 }
    ],
    stats: [
      {
        name: 'minimum_weapon_physical_damage',
        type: 'flat',
        min: 5,
        max: 7
      },
      {
        name: 'maximum_weapon_physical_damage',
        type: 'flat',
        min: 10,
        max: 14
      },
    ],
    required_level: 1,
    breakpoint_index: 0
  },

And now the code to create higher tier modifiers:

  const allMods: BaseMods = {};
  for(let key in baseMods){
    let val = baseMods[key];
    allMods[key] = val; //all mods includes the first mod
    let curLevel = val.required_level;
    let weightMultiplier = 1.2; //these multipliers are subject to change
    let statMultiplier = 1.5;
    let tempKey = key;

    //create a mod for each level breakpoint
    //these are arbitrary and subject to change
    //right now im using [1,5,12,20,29,39,50,62]
    for(let i = val.breakpoint_index+1; i < levelBreakpoints.length; i++){
      curLevel = levelBreakpoints[i];
      
      //create the next tier of weights
      let newWeights = val.weights.map((w) => ({
        ...w,
        weight: Math.floor(w.weight / weightMultiplier)
      }));

      //same thing but with stats
      let newStats = val.stats.map((stat) => ({
        ...stat,
        min: Math.floor(stat.min * statMultiplier),
        max: Math.floor(stat.max * statMultiplier)
      }));

      //increase the key name e.g addedPhys1 becomes addedPhys2
      let suffix = Number(tempKey.slice(tempKey.length-1)) + 1;
      let prefix = tempKey.slice(0, tempKey.length-1);
      let newKey = prefix + suffix;
      tempKey = newKey;

      //add the new mod and increase the multipliers
      allMods[newKey] = {
        ...val,
        weights: newWeights,
        stats: newStats,
        required_level: curLevel,
        breakpoint_index: i
      }
      weightMultiplier += .3;
      statMultiplier += 1.1;
    }
  }

Step 2: Create a Tag Table

For this step we'll create a map with the keys as all the item tag names and values as sets of item_classes associated with that tag. We're making this to use it in the final step. First let's look at an item for context:

  { 
    name: "Twig",    
    item_class: 'wand',    
    weight: 800,    
    tags: ['wand', 'weapon', 'weapon_1h', '1h', 'weapon_magic'],
    drop_level: levelBreakpoints[0],
    implicits: [
      { 
        name: 'spell_damage',
        type: 'increased',
        min: 10,
        max: 20
      },
    ]  
  },

As you can see, there's an item_class and tags associated with this specific item. Now let's build the tag table:

  const tagTable: TagTable = {};
  for(let item of items){
    for(let tag of item.tags){
      if(tagTable[tag] === undefined){
        tagTable[tag] = new Set();
      }
      tagTable[tag].add(item.item_class);
    }
  }

Step 3: Building the Mod Table

The final step is to build the mod table, which we will use to roll the mods on items. Let's look at the code:

  const itemModMap: ItemModMap = {};

  //loop through the allMods map we made in step 1
  for(let key in allMods){
    let val = allMods[key];

    //loop through the weights of the mod
    //the weight.name will be a tag
    for(let weight of val.weights){
      let tag = weight.name;

      //ignore this, i dont think its necessary i just got lazy
      if(tagTable[tag] === undefined){
        tagTable[tag] = new Set();
      }

      //some items don't get all the tiers of the mod
      if(val.breakpoint_index > weight.max_tier) continue;


      //loop through the item_classes of that tag
      for(let item_class of tagTable[tag]){
        
        if(itemModMap[item_class] === undefined){
          itemModMap[item_class] = [];
        }
        
        
        itemModMap[item_class].push({
          name: key,
          type: val.type,
          weight: weight.weight,
          stats: val.stats,
          required_level: val.required_level,
          tier: val.breakpoint_index
        });
      }
    }
  }

Conclusion

Now we have map with the item_class as a key and all the mods for that class as a value. This allows us to easily look up all the mods of a given item class. Ok, that's all for this one. Bye.