Morwen's Loot Generator

From Infinite Worlds
Jump to navigation Jump to search

Loot Table Generator[edit]

Description[edit]

The Loot Table Generator is a dynamic system for creating randomized loot items in Infinite Worlds. It generates a fresh table of items each turn based on your configuration, with full control over which properties (name, value, weight) are included in the output.


Configuration[edit]

Configuration Sections[edit]

Basic Settings[edit]

Controls the size of the generated table and which properties (name, value, weight) are included in each loot item.

loot_types[edit]

Defines the categories of items that can be generated, with probability weights determining their frequency. Weights must sum to 1.0.

rarities[edit]

Determines the distribution of rarity levels across generated items. Weights must sum to 1.0.

value_ranges[edit]

Sets the minimum and maximum gold value for each rarity level, used when value generation is enabled.

type_weights[edit]

Establishes the base weight range for each item type, used for encumbrance calculations when weight generation is enabled.

materials[edit]

Lists available materials for each item type. Material becomes a core property of the generated item and affects naming and weight.

keywords[edit]

Defines optional modifiers that can be applied to items. Can be simple (single probability) or complex (with probability and multiple values).

type_subtypes[edit]

Specifies the specific variants available within each loot type category, becoming the item's subtype property.

name_templates[edit]

Provides naming patterns for each type, supporting placeholders like {subtype}, {elemental}, {material}, and {effect} that are replaced during generation.

defaults[edit]

Fallback values used when no keyword of a specific type is selected during generation, ensuring all placeholders can be filled.

Full Configuration Example[edit]

# ===== BASIC SETTINGS =====
# Controls which properties are included in generated items
table_size: 10
include_name: true      # Include name property in output
include_weight: false   # Include weight property in output
include_value: false    # Include value property in output

# ===== ITEM CATEGORIZATION =====
# Loot types with probability weights (must sum to 1.0)
loot_types:
  weapon: 0.35
  armor: 0.25
  potion: 0.20
  scroll: 0.15
  gem: 0.05

# Rarities with probability weights (must sum to 1.0)
rarities:
  common: 0.60
  uncommon: 0.25
  rare: 0.10
  epic: 0.04
  legendary: 0.01

# ===== VALUE SYSTEM =====
# Gold value ranges assigned by rarity
value_ranges:
  common: [1, 50]
  uncommon: [51, 200]
  rare: [201, 500]
  epic: [501, 1000]
  legendary: [1001, 5000]

# Value multipliers for keywords
keyword_value_multipliers:
  magical: 1.5
  cursed: 0.8
  blessed: 2.0
  ancient: 1.8
  elemental: 1.2
  effect: 1.2

# ===== WEIGHT SYSTEM =====
# Base weight ranges by item type
type_weights:
  weapon: [2, 8]
  armor: [5, 20]
  potion: [0.1, 2.0]
  scroll: [0.1, 1.0]
  gem: [0.1, 3.0]

# Weight multipliers for specific materials
material_weight_multipliers:
  mithril: 0.7
  steel: 1.2
  iron: 1.5
  wood: 0.8
  bone: 0.6

# ===== MATERIAL PROPERTIES =====
# Materials available for each type (becomes item.material)
materials:
  weapon: [iron, steel, silver, gold, mithril, wood, bone]
  armor: [leather, iron, steel, silver, mithril, plate]
  potion: [glass, crystal, clay, alchemical]
  scroll: [parchment, paper, vellum, enchanted]
  gem: [raw, cut, polished, flawless]

# ===== KEYWORD MODIFIERS =====
# Keywords that can be applied to items with their probabilities
keywords:
  # Simple keywords (probability is the value)
  magical: 0.30
  cursed: 0.10
  blessed: 0.05
  ancient: 0.15

  # Complex keywords (have probability and values)
  elemental:
    probability: 0.25
    values: [fire, water, air, earth, lightning, ice]
  effect:
    probability: 0.15
    values: [health, power, force, flying, stability]

# ===== ITEM VARIATION =====
# Subtypes for each loot type (becomes item.subtype)
type_subtypes:
  weapon: [sword, axe, mace, dagger, spear, bow, staff]
  armor: [helmet, chestplate, gauntlets, leggings, shield, boots]
  potion: [health, mana, stamina, strength, defense, poison]
  scroll: [fireball, heal, teleport, identify, protection, curse]
  gem: [ruby, sapphire, emerald, diamond, amethyst, topaz]

# Name templates for each type
# Supports placeholders: {subtype}, {elemental}, {material}, {effect}
name_templates:
  weapon:
    - '{material} {subtype} of {elemental}'
    - '{subtype} of {elemental}'
    - '{subtype} of {effect}'
    - '{material} {subtype}'
  armor:
    - '{material} {subtype} of {elemental}'
    - '{subtype} of {effect}'
    - '{subtype} of {elemental}'
    - '{material} {subtype}'
  potion:
    - 'Potion of {elemental}'
    - 'Potion of {effect}'
    - '{elemental} Potion'
  scroll:
    - 'Scroll of {elemental}'
    - 'Scroll of {effect}'
    - '{elemental} Scroll'
  gem:
    - '{elemental} {material} Gem'
    - '{material} Gem of {elemental}'

# ===== FALLBACKS =====
# Default values used when no keyword of that type is selected
defaults:
  elemental: [power, mystery, ancients, stars]
  material: [iron, steel, wooden, leather]
  effect: [power, strength, dexterity]

Generation Script[edit]

# ===========================================
# LOOT TABLE GENERATOR
# Run this every turn to refresh loot_table
# ===========================================

$loot_table = list()

set $table_size = if($loot_config.contains('table_size'), $loot_config.table_size, 10)

for each $i in range($table_size)

  set $total_type = 0
  for each $t in $loot_config.loot_types.keys()
    set $total_type = $total_type + $loot_config.loot_types.item($t)

  set $rand = random() * $total_type
  set $cumulative = 0
  set $type = ''
  for each $t in $loot_config.loot_types.keys()
    if $type = ''
      set $cumulative = $cumulative + $loot_config.loot_types.item($t)
      if $rand < $cumulative
        set $type = $t

  set $subtypes = $loot_config.type_subtypes.item($type)
  set $subtype = $subtypes.item(random_between(1, $subtypes.count()))

  set $total_rarity = 0
  for each $r in $loot_config.rarities.keys()
    set $total_rarity = $total_rarity + $loot_config.rarities.item($r)

  set $rand = random() * $total_rarity
  set $cumulative = 0
  set $rarity = ''
  for each $r in $loot_config.rarities.keys()
    if $rarity = ''
      set $cumulative = $cumulative + $loot_config.rarities.item($r)
      if $rand < $cumulative
        set $rarity = $r

  set $value_range = $loot_config.value_ranges.item($rarity)
  set $value = random_between($value_range.item(1), $value_range.item(2))

  set $keywords = list()
  set $multiplier = 1.0

  for each $kw in $loot_config.keywords.keys()
    set $kc = $loot_config.keywords.item($kw)
    set $prob = try($kc.item('probability'), $kc)

    if random() < $prob
      set $has_values = try($kc.contains('values'), false)

      if $has_values
        set $vals = $kc.item('values')
        set $kw_val = $vals.item(random_between(1, $vals.count()))
        $keywords.append($kw & ':' & $kw_val)
      else
        $keywords.append($kw)

      if $loot_config.keyword_value_multipliers.contains($kw)
        set $multiplier = $multiplier * $loot_config.keyword_value_multipliers.item($kw)

  set $value = round($value * $multiplier)

  set $elemental = ''
  set $effect = ''
  for each $kw in $keywords
    set $p = $kw.split(':')
    if $p.count() = 2
      if $p.item(1) = 'elemental'
        set $elemental = $p.item(2)
      else if $p.item(1) = 'effect'
        set $effect = $p.item(2)

  if $elemental = ''
    set $default_elementals = $loot_config.defaults.item('elemental')
    set $elemental = $default_elementals.item(random_between(1, $default_elementals.count()))

  if $effect = ''
    set $default_effects = $loot_config.defaults.item('effect')
    set $effect = $default_effects.item(random_between(1, $default_effects.count()))

  set $material = ''
  set $materials = $loot_config.materials.item($type)
  if $materials
    set $material = $materials.item(random_between(1, $materials.count()))

  if $material = ''
    set $default_materials = $loot_config.defaults.item('material')
    set $material = $default_materials.item(random_between(1, $default_materials.count()))

  set $templates = $loot_config.name_templates.item($type)
  set $template = $templates.item(random_between(1, $templates.count()))
  set $name = $template
  set $name = $name.replace('{subtype}', $subtype)
  set $name = $name.replace('{elemental}', $elemental)
  set $name = $name.replace('{material}', $material)
  set $name = $name.replace('{effect}', $effect)
  set $name = $name.replace('  ', ' ').trim().title_case()

  set $weight_range = $loot_config.type_weights.item($type)
  set $weight = random_between($weight_range.item(1), $weight_range.item(2))

  if $loot_config.material_weight_multipliers.contains($material)
    set $weight = $weight * $loot_config.material_weight_multipliers.item($material)

  set $item = record()
  $item.item('id') = $i
  if $loot_config.item('include_name') = true
    $item.item('name') = $name
  $item.item('type') = $type
  $item.item('subtype') = $subtype
  $item.item('rarity') = $rarity
  $item.item('material') = $material
  $item.item('keywords') = $keywords
  if $loot_config.item('include_value') = true
    $item.item('value') = $value
  if $loot_config.item('include_weight') = true
    $item.item('weight') = $weight.round(1)

  $loot_table.append($item)

Usage[edit]

Quick Start[edit]

1. Create a tracked item named Loot Config (API must be loot_config) with the configuration above

  • Type: YAML
  • Visibility: invisible to AI and player

2. Create an empty tracked item named Loot Table (API must be loot_table)

  • Type: YAML
  • Visibility: at least visible to the AI
  • Not updateable by the AI

3. Add a trigger that runs the generation script at the start of each turn

4. Access loot via the `$loot_table` variable

Pulling Loot[edit]

# Pull a single random item
set $index = random_between(1, $loot_table.count())
set $loot = $loot_table.item($index)

# Add to player inventory
if not $player.inventory
  $player.inventory = list()
end
$player.inventory.append($loot)

Displaying Loot[edit]

# Show all loot names
<<$loot_table.format_each('{name}').join(', ')>>

# Show rare+ items
<<$loot_table.where(rarity = 'rare' or rarity = 'epic' or rarity = 'legendary').count()>> rare items

# Show a random item's details
<<$loot_table.item(random_between(1, $loot_table.count())).name>> (<<$loot_table.item(random_between(1, $loot_table.count())).rarity>>)

Configuration Presets[edit]

Minimal[edit]

table_size: 5
include_name: false
include_weight: false
include_value: false

loot_types:
  weapon: 0.5
  armor: 0.5

rarities:
  common: 0.8
  uncommon: 0.2

value_ranges:
  common: [1, 10]
  uncommon: [11, 20]

type_weights:
  weapon: [1, 5]
  armor: [2, 8]

type_subtypes:
  weapon: [sword, axe]
  armor: [helmet, chestplate]

materials:
  weapon: [iron, steel]
  armor: [leather, iron]

keywords: {}

keyword_value_multipliers: {}

material_weight_multipliers: {}

name_templates:
  weapon: ['{subtype}']
  armor: ['{subtype}']

defaults:
  elemental: [power]
  material: [iron]
  effect: [power]

Full RPG[edit]

table_size: 20
include_name: true
include_weight: true
include_value: true

loot_types:
  weapon: 0.30
  armor: 0.25
  potion: 0.20
  scroll: 0.15
  gem: 0.10

rarities:
  common: 0.55
  uncommon: 0.25
  rare: 0.15
  epic: 0.04
  legendary: 0.01

value_ranges:
  common: [1, 100]
  uncommon: [101, 500]
  rare: [501, 2000]
  epic: [2001, 5000]
  legendary: [5001, 20000]

type_weights:
  weapon: [3, 10]
  armor: [8, 25]
  potion: [0.5, 3.0]
  scroll: [0.2, 2.0]
  gem: [0.1, 5.0]

materials:
  weapon: [iron, steel, silver, mithril, adamantium]
  armor: [leather, iron, steel, mithril, plate]
  potion: [glass, crystal, alchemical]
  scroll: [parchment, vellum, enchanted]
  gem: [raw, cut, polished, flawless]

keywords:
  magical: 0.40
  cursed: 0.15
  blessed: 0.10
  ancient: 0.20
  elemental:
    probability: 0.30
    values: [fire, ice, lightning, holy, dark, nature]
  effect:
    probability: 0.25
    values: [health, mana, strength, speed, protection, regeneration]

keyword_value_multipliers:
  magical: 1.8
  cursed: 0.7
  blessed: 2.5
  ancient: 2.0
  elemental: 1.5
  effect: 1.3

material_weight_multipliers:
  mithril: 0.6
  adamantium: 0.5
  steel: 1.3
  iron: 1.5

type_subtypes:
  weapon: [sword, axe, mace, dagger, spear, bow, staff, wand]
  armor: [helmet, chestplate, gauntlets, leggings, shield, boots, cloak]
  potion: [health, mana, stamina, strength, defense, speed, poison, antidote]
  scroll: [fireball, heal, teleport, identify, protection, curse, summon]
  gem: [ruby, sapphire, emerald, diamond, amethyst, topaz, opal]

name_templates:
  weapon:
    - '{material} {subtype} of {elemental}'
    - '{elemental} {material} {subtype}'
    - '{subtype} of {effect}'
    - 'Ancient {subtype}'
    - '{material} {subtype}'
  armor:
    - '{material} {subtype} of {elemental}'
    - '{elemental} {material} {subtype}'
    - '{subtype} of {effect}'
    - '{material} {subtype}'
  potion:
    - 'Potion of {elemental} {effect}'
    - '{elemental} Potion of {effect}'
    - '{effect} Potion'
    - '{material} Potion'
  scroll:
    - 'Scroll of {elemental} {effect}'
    - '{elemental} Scroll of {effect}'
    - '{effect} Scroll'
  gem:
    - '{elemental} {material} Gem'
    - '{material} Gem of {elemental}'
    - 'Gem of {effect}'

defaults:
  elemental: [power, mystery, ancients, stars, light]
  material: [iron, steel, leather, cloth]
  effect: [power, strength, dexterity, vitality, protection]

Customization Guide[edit]

Adding New Item Types[edit]

  1. Add to loot_types with probability weight
  2. Add to type_subtypes with possible subtypes
  3. Add to type_weights with weight range
  4. Add to materials with available materials
  5. Add to name_templates with naming patterns

Adding New Keywords[edit]

Simple keyword: Direct probability value

  keywords:
    new_keyword: 0.20


Complex keyword: With sub-values

  keywords:
    new_keyword:
      probability: 0.20
      values: [option1, option2, option3]

Adjusting Balance[edit]

  • Modify value_ranges to change gold values by rarity
  • Adjust keyword_value_multipliers to change keyword impact on value
  • Change type_weights and material_weight_multipliers for encumbrance system

Notes[edit]

  • All probability weights must sum to 1.0 within their category
  • The loot table refreshes completely every turn the script runs
  • Use //seed(42)// for testing with repeatable results
  • For best performance, keep table_size under 50
  • Material is a core property, not a keyword