--helpers
local module_frame = mw.getCurrentFrame()
tier_text = {module_frame:expandTemplate{title="Low"},
module_frame:expandTemplate{title="Common"},
module_frame:expandTemplate{title="Moderate"},
module_frame:expandTemplate{title="High"},
module_frame:expandTemplate{title="Epic"},
module_frame:expandTemplate{title="Legendary"},
module_frame:expandTemplate{title="Artifact"},
}
metal_text = {"Bronze", "Iron", "Steel", "Cobalt", "Bloodsteel", "Orichalcum"}
wood_text = {"Wooden", "Bamboo", "Hardwood", "Ironwood", "Frostwood", "Eldwood"}
upper_first = function(str)
return str:sub(1, 1):upper() .. str:sub(2)
end
str_split_iter = function(str, sep, escape)
if escape then
sep = "%"..sep
end
return string.gmatch(str, "[^"..sep.."]+")
end
format_probability = function(p_flt)
-- format probability string
local p_perc = p_flt * 100
local p_str = tostring(p_perc)
if p_str ~= tostring(math.floor(p_perc)) then
p_str = string.format("%.2f", p_perc)
end
return p_str
end
-- worker function
process = function(frame, txt, entries, depth, p_prev, reuse_p)
local p_order = {}
local hidep
if not reuse_p then
--sort indices by drop chance
for idx, entry in ipairs(entries) do
p_order[idx] = {idx, entry[1]}
end
table.sort(p_order, function(a, b) return a[2] > b[2] end)
hidep = 0
else
-- use entries directly (re-using same probability)
p_order = entries
hidep = 1
end
-- process entries
for _, val in ipairs(p_order) do
-- unpack data
local entry
if not reuse_p then
entry = entries[val[1]]
else
entry = val
end
local p_flt, box
if not reuse_p then
p_flt = entry[1] * p_prev
box = entry[2]
else
p_flt = p_prev
box = entry
end
local box_type = box.t
local box_content = box.c
-- format probability string
local p_str = format_probability(p_flt)
-- handle box type
if box_type == "Item" then
-- single item row
local item = box_content[2]
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=depth, item=item, n=1, p=p_str, hidep=hidep} }
elseif box_type == "Nothing" then
-- single row
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=depth, item="Nothing", p=p_str, hidep=hidep, noimg=1} }
elseif box_type == "MultiDrop" then
-- multidrop
local nested_box = box_content[1]
local nested_type = nested_box.t
local low = box_content[2]
local high = box_content[3]
local n_str
if low == high then
n_str = tostring(low)
else
n_str = string.format("%s - %s", low, high)
end
if nested_type == "Item" then
-- multiple of one item
local item = nested_box.c[2]
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=depth, item=item, n=n_str, p=p_str, hidep=hidep} }
elseif nested_type == "SubTable" then
-- multidrop from nested table
local sub_depth = depth + 1
local sub_box = nested_box.c[1]
local sub_path = nested_box.c[2]
local path_parts = {}
for part in str_split_iter(sub_path, '.', true) do
path_parts[#path_parts+1] = part
end
local item_category = path_parts[3]
local last_part = path_parts[#path_parts]
local name
local collapsed = 1
--material sub tables (hard-code matching for now)
if item_category == "food" then
if last_part == "wild_ingredients" then
name = "Wild Ingredients"
elseif last_part == "prepared" then
name = "Prepared Food"
elseif last_part == "farm_ingredients" then
name = "Farm Ingredients"
else
name = "Food"
end
elseif item_category == "materials" then
local name
if last_part == "gems" then
name = "Gems"
elseif last_part == "underground" then
name = "Underground Materials"
else
name = "Materials"
end
else
name = "Misc"
collapsed = 0
end
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, collapsed=collapsed, title=name, n=n_str, p=p_str, hidep=hidep} }
txt = process(frame, txt, sub_box, sub_depth, p_flt, false)
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootEndNested'}
elseif nested_type == "All" then
local sub_depth = depth + 1
local sub_box_content = nested_box.c
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, title="All", collapsed=0, p=p_str, hidep=hidep} }
txt = process(frame, txt, sub_box_content, sub_depth, p_flt, true)
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootEndNested'}
-- spacer row approach
-- txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowSpacer'}
-- txt = process(frame, txt, box_content, sub_depth, p_flt/100, true)
-- txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowSpacer'}
else
-- other case (unhandled for now)
end
elseif box_type == "SubTable" then
local sub_depth = depth + 1
local sub_box = box_content[1]
local sub_path = box_content[2]
local override = false
-- sub table header
local path_sections = {}
for section in str_split_iter(sub_path, '.', true) do
-- for section in string.gmatch(sub_path, "[^%.]+") do
path_sections[#path_sections+1] = section
end
local last_section = path_sections[#path_sections]
local second_last_section = path_sections[#path_sections-1]
local is_modular = false
if last_section ~= nil then
is_modular = "tier" == string.sub(last_section, 1, 4)
end
local idx_tier
if is_modular then
idx_tier = 1 + tonumber(string.sub(last_section, -1, -1))
end
local item_category = path_sections[3]
if item_category == "weapons" then
if not is_modular then
-- non-modular weapon
local name = upper_first(last_section).." Weapon"
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, title=name, p=p_str, hidep=hidep} }
else
if second_last_section == "components" then
-- primary component
local name = tier_text[idx_tier].." Primary Component"
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, title=name, n=1, p=p_str, hidep=hidep} }
local metal = metal_text[idx_tier]
local wood = wood_text[idx_tier]
local p_wep = string.format("%.2f", 100 * p_flt / 6)
local page = "Modular Crafting" -- page link override doesn't seem to work
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=metal.." Sword Blade", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=metal.." Axe Head", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=metal.." Hammer Head", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=wood.." Bow Limbs", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=wood.." Sceptre Shaft", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=wood.." Staff Shaft", p=p_wep, noimg=1, page=page} }
-- **TODO**
-- MediaWiki template for primary components of parameterised tier (sans outer header and end)
override = true
elseif second_last_section == "secondary" then
-- secondary component
-- **TODO**
-- MediaWiki template for secondary components (sans outer header and end)
override = true
else
-- modular weapon
local name = tier_text[idx_tier].." Weapon"
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, title=name, n=1, p=p_str, hidep=hidep} }
local metal = metal_text[idx_tier]
local wood = wood_text[idx_tier]
local p_wep = string.format("%.2f", 100 * p_flt / 6)
local page = "Weapons" -- page link override doesn't seem to work
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=metal.." Sword", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=metal.." Axe", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=metal.." Hammer", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=wood.." Bow", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=wood.." Sceptre", p=p_wep, noimg=1, page=page} }
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=sub_depth, item=wood.." Staff", p=p_wep, noimg=1, page=page} }
-- **TODO**
-- MediaWiki template for complete modular weapon of parameterised tier (sans outer header and end)
override = true
end
end
elseif item_category == "armor" then
if not is_modular then
-- specific armor
local name = upper_first(last_section)
if name == "Cloth" then
name = tier_text[1].." Armor"
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, title=name, n=1, p=p_str, hidep=hidep} }
else
if name == "Wool" then
name = "Woolen"
elseif name == "Silk" then
name = "Silken"
elseif name == "Lifecloth" then
name = "Druid"
elseif name == "Plate" then
name = "Primal"
end
local image = name.." Armor Set.png"
name = name.." Armor"
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, title=name, image=image, p=p_str, hidep=hidep} }
end
else
-- modular armor tier
local name = tier_text[idx_tier].." Armor"
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, title=name, n=1, p=p_str, hidep=hidep} }
end
elseif item_category == "food" then
--food sub tables (hard-code matching for now)
local name
if last_section == "wild_ingredients" then
name = "Wild Ingredients"
elseif last_section == "prepared" then
name = "Prepared Food"
elseif last_section == "farm_ingredients" then
name = "Farm Ingredients"
else
name = "Food"
end
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, collapsed=1, title=name, p=p_str, hidep=hidep} }
elseif item_category == "materials" then
local name
if last_section == "gems" then
name = "Gems"
elseif last_section == "underground" then
name = "Underground Materials"
else
name = "Materials"
end
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, collapsed=1, title=name, p=p_str, hidep=hidep} }
else
-- non weeapon/armor sub table
-- local name = last_section..second_last_section
local name = "Misc"
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, collapsed=0, title=name, p=p_str, hidep=hidep} }
end
-- sub table rows (unless direct template was used)
if not override then
txt = process(frame, txt, sub_box, sub_depth, p_flt, false)
end
-- sub table end
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootEndNested'}
elseif box_type == "All" then
local sub_depth = depth + 1
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable',
args={depth=sub_depth, title="All", collapsed=0, p=p_str, hidep=hidep} }
txt = process(frame, txt, box_content, sub_depth, p_flt, true)
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootEndNested'}
-- spacer row approach
-- txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowSpacer'}
-- txt = process(frame, txt, box_content, sub_depth, p_flt/100, true)
-- txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowSpacer'}
else
-- fallback (shouldn't reach)
local name = "Error"
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootRowVariable',
args={depth=depth, item=name, p=0, hidep=1, noimg=1} }
end
end
return txt
end
-- mainline code
local module = {}
local drops_json = mw.text.jsonDecode(module_frame:expandTemplate{title="User:Horblegorble/Sandbox/Template/LootData"})
local sources_json = mw.text.jsonDecode(module_frame:expandTemplate{title="User:Horblegorble/Sandbox/Template/SourceData"})
module.drops = function(frame)
-- generates drops table for an entity
-- handle args
-- local entity = tostring(frame.args[1])
-- local loot_page = "User:"..entity.."Loot"
-- local json = mw.text.jsonDecode(frame:expandTemplate{title=loot_page})
-- get entity name
-- local page_sections = {}
-- for idx, section in ipairs(str_split_iter(frame.args[1])) do
-- page_sections[idx] = section
-- end
-- local entity = page_sections[#page_sections]
local entity = frame.args[1]
local export_date = drops_json[1]
-- find data entry for entity
local data = false
for _, entry in ipairs(drops_json[2]) do
if entry[1] == entity then
data = entry[2]
break
end
end
if not data then
return "entity not found in drops data"
end
-- table start
local txt = frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootHeaderVariable'}
-- do the work
txt = process(frame, txt, data, 0, 1.0, false)
-- table end
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/LootEnd',
args={date=export_date} }
return txt
end
module.sources = function(frame)
-- generates table of entity drop sources for an item
local item = frame.args[1]
local export_date = sources_json[1]
local data = false
for _, entry in ipairs(sources_json[2]) do
if entry[1] == item then
data = entry[2]
break
end
end
if not data then
return frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/NoSources',
args={date=export_date}}
end
-- sort entries by average
local p_order = {}
for idx, entry in ipairs(data) do
p_order[idx] = {idx, entry[8]}
end
table.sort(p_order, function(a, b) return a[2] > b[2] end)
-- table start
local txt = frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/SourceHeader',
args={sortable=1}}
-- do the work
for _, val in ipairs(p_order) do
entry = data[val[1]]
local entity = entry[1]
if entity ~= "" then
local variant = entry[2]
local amount = entry[3]
local n_sort = entry[4]
local chance = entry[5]
local rolls = entry[6]
local p_sort = entry[7] * 100
-- local avg = entry[8]
if amount[1] == amount[2] then
amount = tostring(amount[1])
else
amount = string.format("%i-%i", amount[1], amount[2])
end
if rolls[1] == rolls[2] then
if rolls[1] == 1 then
rolls = ''
else
rolls = tostring(rolls[1])
end
else
rolls = string.format("%i-%i", rolls[1], rolls[2])
end
local p_str = format_probability(chance)
local page = ""
if string.sub(entity, 1, 1) == "#" then
page = "Containers"
entity = string.sub(entity, 2, -1)
end
txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/SourceRow',
args={entity=entity, variant=variant, n=amount, n_sort=n_sort,
p=p_str, rolls=rolls, p_sort=p_sort, page=page}}
end
end
-- table end
local txt = txt .. frame:expandTemplate{title='User:Horblegorble/Sandbox/Template/SourceEnd',
args={date=export_date} }
return txt
end
return module