Module:LootChest

local p = {

calc_average_amount_this_item_per_chest = function( 			min_stacksize, max_stacksize,			min_chest_numstacks, max_chest_numstacks, 			item_weight, chest_total_item_weight ) local avg_stacksize = ( min_stacksize + max_stacksize ) / 2 local avg_numstacks = ( min_chest_numstacks + max_chest_numstacks ) / 2 return avg_stacksize * avg_numstacks * item_weight / chest_total_item_weight end, calc_chance_any_of_this_item_per_chest = function( 			min_chest_numstacks, max_chest_numstacks,			item_weight, chest_total_item_weight ) local avg_numstacks = ( min_chest_numstacks + max_chest_numstacks ) / 2 local relativeweight = item_weight / chest_total_item_weight return 1 - math.pow( 1 - relativeweight, avg_numstacks ) end, -- unused calc_num_chests_to_find_this_item = function( 			min_chest_numstacks, max_chest_numstacks,			item_weight, chest_total_item_weight ) local avg_numstacks = ( min_chest_numstacks + max_chest_numstacks ) / 2 local relativeweight = item_weight / chest_total_item_weight return 1 / ( 1 - math.pow( 1 - relativeweight, avg_numstacks ) ) end, -- accurate as of 1.8 -- these define which sprite, label and link to use -- NOTE: order in this list doesn't matter.

items = { ["acacia-wood"]        = { "block", link="Wood" }, ["apple"]              = { "item" }, ["bone"]               = { "item" }, ["book"]               = { "item" }, ["bread"]              = { "item" }, ["bucket"]             = { "item" }, ["coal"]               = { "item" }, ["compass"]            = { "item" }, ["diamond"]            = { "item" }, ["disc-13"]            = { "item", title="Music Disc (13)", link="Music Disc" }, ["disc-cat"]           = { "item", title="Music Disc (Cat)", link="Music Disc" }, ["emerald"]            = { "item" }, ["enchanted-book"]     = { "item" }, ["ender-pearl"]        = { "item" }, ["flint-and-steel"]    = { "item" }, ["golden-apple"]       = { "item", title="Golden Apple (Normal)" }, ["golden-chestplate"]  = { "item", link="Armor" }, ["golden-sword"]       = { "item", link="Sword" }, ["gold-ingot"]         = { "item" }, ["gunpowder"]          = { "item" }, ["horse-armor-diamond"] = { "item", title="Diamond Horse Armor", link="Horse Armor" }, ["horse-armor-gold"]   = { "item", title="Gold Horse Armor", link="Horse Armor" }, ["horse-armor-iron"]   = { "item", title="Iron Horse Armor", link="Horse Armor" }, ["iron-boots"]         = { "item", link="Armor" }, ["iron-chestplate"]    = { "item", link="Armor" }, ["iron-helmet"]        = { "item", link="Armor" }, ["iron-ingot"]         = { "item" }, ["iron-leggings"]      = { "item", link="Armor" }, ["iron-pickaxe"]       = { "item", link="Pickaxe" }, ["iron-sword"]         = { "item", link="Sword" }, ["lapis-lazuli"]       = { "item" }, ["map"]                = { "item", title="Empty Map" }, ["melon-seeds"]        = { "item" }, ["name-tag"]           = { "item" }, ["nether-wart"]        = { "item" }, ["oak-sapling"]        = { "block", link="Sapling" }, ["oak-wood"]           = { "block", link="Wood" }, ["obsidian"]           = { "block" }, ["paper"]              = { "item" }, ["pumpkin-seeds"]      = { "item" }, ["rail"]               = { "block", title="Rails" }, ["redstone"]           = { "item" }, ["rotten-flesh"]       = { "item" }, ["saddle"]             = { "item" }, ["stick"]              = { "item" }, ["stone-axe"]          = { "item", link="Axe" }, ["stone-pickaxe"]      = { "item", link="Pickaxe" }, ["string"]             = { "item" }, ["wheat"]              = { "item" }, ["wood-planks"]        = { "block", title="Oak Wood Planks" }, ["wooden-axe"]         = { "item", link="Axe" }, ["wooden-pickaxe"]     = { "item", link="Pickaxe" } },	-- accurate as of 1.8 -- NOTE: order here doesn't matter. --		 * chests will sort in alphabetical order --      * items will sort by chance, then by avg#, then alphabetically. -- NOTE: any invalid item-id here will print as ??? chests = { ["village-blacksmith"] = { numstacks  = {3,8}, totalweight = nil, header     = "Village", items = { ["diamond"]            = {1,3,3}, ["iron-ingot"]         = {1,5,10}, ["gold-ingot"]         = {1,3,5}, ["bread"]              = {1,3,15}, ["apple"]              = {1,3,15}, ["iron-pickaxe"]       = {1,1,5}, ["iron-sword"]         = {1,1,5}, ["iron-chestplate"]    = {1,1,5}, ["iron-helmet"]        = {1,1,5}, ["iron-leggings"]      = {1,1,5}, ["iron-boots"]         = {1,1,5}, ["obsidian"]           = {3,7,5}, ["oak-sapling"]        = {3,7,5}, ["horse-armor-iron"]   = {1,1,1}, ["horse-armor-gold"]   = {1,1,1}, ["horse-armor-diamond"] = {1,1,1}, ["saddle"]             = {1,1,3} }		},		["stronghold-altar"] = { numstacks  = {2,4}, totalweight = nil, header     = "Altar", superheader = "Stronghold", items      = { ["diamond"]            = {1,3,3}, ["iron-ingot"]         = {1,5,10}, ["gold-ingot"]         = {1,3,5}, ["bread"]              = {1,3,5}, ["apple"]              = {1,3,15}, ["iron-pickaxe"]       = {1,1,5}, ["iron-sword"]         = {1,1,5}, ["iron-chestplate"]    = {1,1,5}, ["iron-helmet"]        = {1,1,5}, ["iron-leggings"]      = {1,1,5}, ["iron-boots"]         = {1,1,5}, ["horse-armor-iron"]   = {1,1,1}, ["horse-armor-gold"]   = {1,1,1}, ["horse-armor-diamond"] = {1,1,1}, ["ender-pearl"]        = {1,1,10}, ["redstone"]           = {4,9,5}, ["golden-apple"]       = {1,1,1}, ["saddle"]             = {1,1,1}, ["enchanted-book"]     = {1,1,1} }		},		["stronghold-library"] = { numstacks  = {1,4}, totalweight = nil, header     = "Library", superheader = "Stronghold", items      = { ["enchanted-book"]     = {1,5,2}, ["book"]               = {1,3,20}, ["paper"]              = {2,7,20}, ["map"]                = {1,1,1}, ["compass"]            = {1,1,1} }		},		["stronghold-storeroom"] = { numstacks  = {1,4}, totalweight = nil, header     = "Storeroom", superheader = "Stronghold", items      = { ["iron-ingot"]         = {1,5,10}, ["gold-ingot"]         = {1,3,5}, ["bread"]              = {1,3,5}, ["apple"]              = {1,3,5}, ["iron-pickaxe"]       = {1,1,1}, ["redstone"]           = {4,9,5}, ["enchanted-book"]     = {1,1,1}, ["coal"]               = {3,8,10} }		},		["bonus"] = { numstacks  = {10,10}, totalweight = nil, header     = "Bonus", items      = { ["bread"]              = {2,3,3}, ["apple"]              = {2,3,5}, ["stick"]              = {1,3,10}, ["wood-planks"]        = {1,3,10}, ["oak-wood"]           = {1,3,10}, ["acacia-wood"]        = {1,3,10}, ["stone-axe"]          = {1,1,3}, ["wooden-axe"]         = {1,1,5}, ["stone-pickaxe"]      = {1,1,3}, ["wooden-pickaxe"]     = {1,1,5} }		},		["dungeon"] = { numstacks  = {8,8}, totalweight = nil, header     = "Dungeon", items      = { ["iron-ingot"]         = {1,4,10}, ["bread"]              = {1,4,10}, ["horse-armor-iron"]   = {1,1,5}, ["horse-armor-gold"]   = {1,1,2}, ["horse-armor-diamond"] = {1,1,1}, ["redstone"]           = {1,4,10}, ["golden-apple"]       = {1,1,1}, ["saddle"]             = {1,1,10}, ["wheat"]              = {1,4,10}, ["gunpowder"]          = {1,4,10}, ["string"]             = {1,4,10}, ["bucket"]             = {1,1,10}, ["disc-13"]            = {1,1,4}, ["disc-cat"]           = {1,1,4}, ["name-tag"]           = {1,1,10}, ["enchanted-book"]     = {1,1,1} }		},		["mineshaft"] = { numstacks  = {3,6}, totalweight = nil, header     = "Mineshaft", items      = { ["diamond"]            = {1,2,3}, ["iron-ingot"]         = {1,5,10}, ["gold-ingot"]         = {1,3,5}, ["bread"]              = {1,3,15}, ["iron-pickaxe"]       = {1,1,1}, ["horse-armor-iron"]   = {1,1,1}, ["redstone"]           = {4,9,5}, ["lapis-lazuli"]       = {4,9,5}, ["saddle"]             = {1,1,3}, ["enchanted-book"]     = {1,1,1}, ["coal"]               = {3,8,10}, ["rail"]               = {4,8,1}, ["melon-seeds"]        = {2,4,10}, ["pumpkin-seeds"]      = {2,4,10} }		},		["nether-fortress"] = { numstacks  = {2,5}, totalweight = nil, header     = "Nether fortress", items      = { ["diamond"]            = {1,3,5}, ["iron-ingot"]         = {1,5,5}, ["gold-ingot"]         = {1,3,15}, ["obsidian"]           = {2,4,2}, ["horse-armor-iron"]   = {1,1,5}, ["horse-armor-gold"]   = {1,1,8}, ["horse-armor-diamond"] = {1,1,3}, ["saddle"]             = {1,1,10}, ["golden-sword"]       = {1,1,5}, ["golden-chestplate"]  = {1,1,5}, ["flint-and-steel"]    = {1,1,5}, ["nether-wart"]        = {5,7,5} }		},		["desert-jungle-temple"] = { numstacks  = {2,6}, totalweight = nil, header     = "Desert & jungle temple", items      = { ["diamond"]            = {1,3,3}, ["iron-ingot"]         = {1,5,10}, ["gold-ingot"]         = {2,7,15}, ["emerald"]            = {1,3,2}, ["horse-armor-iron"]   = {1,1,1}, ["horse-armor-gold"]   = {1,1,1}, ["horse-armor-diamond"] = {1,1,1}, ["saddle"]             = {1,1,3}, ["enchanted-book"]     = {1,1,1}, ["bone"]               = {4,6,20}, ["rotten-flesh"]       = {3,7,16} }		}	},

synonyms = { ["desert"] = "desert-jungle-temple", ["jungle"] = "desert-jungle-temple", ["desert-temple"] = "desert-jungle-temple", ["jungle-temple"] = "desert-jungle-temple", ["nether"] = "nether-fortress", ["village"] = "village-blacksmith", ["blacksmith"] = "village-blacksmith", ["altar"] = "stronghold-altar", ["storeroom"] = "stronghold-storeroom", ["library"] = "stronghold-library" },	-- these descriptions are used: -- * in column abbr titles, -- * and above the table when only one column-type is chosen columns = { ["stacksize"] = 'the size of stacks (or for unstackable items, number) of this item found in this chest.', ["weight"] = 'the weight of this item relative to other items in the chest.', ["items"] = 'the number of items expected per chest, averaged over a large number of chests.', ["chance"] = 'the odds of getting any of this item in a single chest.', ["chests"] = 'the number of chests expected to search, to find any of this item.' },	base = function( ... ) local args = arg[1] if args == mw.getCurrentFrame then args = require( 'Module:ProcessArgs' ).merge( true ) else args = arg end -- transform args into usable list local chests, columns = q.massage_args( args ) if #chests == 0 then return " Module:LootChest: no valid arguments " end

q.fill_in_chest_total_weights( chests ) q.fill_in_chest_item_details( chests ) -- construct an ordered list dictating the order of the rows local ordered_item_rows = q.construct_ordered_item_rows( chests ) return q.print_table( chests, columns, ordered_item_rows )

end }

p.doc = function

local valid_args = {} for chest_name, val in pairs(p.chests) do		local synonyms = {} for syn, orig in pairs(p.synonyms) do			if orig == chest_name then table.insert( synonyms, syn ) end end if #synonyms > 0 then chest_name = chest_name .. " ( " .. table.concat( synonyms, ", " ) .. " )" end table.insert( valid_args, chest_name ) end table.sort( valid_args ) return table.concat( valid_args, ",\n " )

end

p.doc2 = function

local valid_args = {} for column_name, val in pairs(p.columns) do table.insert( valid_args, column_name .. ": " .. val ) end table.sort( valid_args ) return table.concat( valid_args, ",\n " )

end

q = {

tablelength = function(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end, massage_args = function( args )

-- find what columns to put local columns = {} for k, _arg in pairs(args) do			if p.columns[_arg] ~= nil then columns[_arg] = true end end if q.tablelength(columns) == 0 then for column_name, v in pairs(p.columns) do				columns[column_name] = true end end -- find what chests to show

local chests = {} for k, _arg in pairs(args) do			if p.chests[_arg] ~= nil then table.insert( chests, _arg ) elseif p.synonyms[_arg] ~= nil then table.insert( chests, p.synonyms[_arg] ) end end if #chests == 0 then for chest_name, chest in pairs(p.chests) do				table.insert( chests, chest_name ) end end table.sort( chests ) return chests, columns end,

sort_items = function( e1, e2 ) if e1.chanceany ~= e2.chanceany then return ( e1.chanceany > e2.chanceany ) end if e1.avgamount ~= e2.avgamount then return ( e1.avgamount > e2.avgamount ) end if e1.material == nil then e1.material = 0 if string.find( e1.itemname, "leather" ) ~= nil then e1.material = 1 end if string.find( e1.itemname, "iron" ) ~= nil then e1.material = 2 end if string.find( e1.itemname, "gold" ) ~= nil then e1.material = 3 end if string.find( e1.itemname, "diamond" ) ~= nil then e1.material = 4 end e1.armor = 0 if string.find( e1.itemname, "helmet" ) ~= nil or string.find( e1.itemname, "cap" ) ~= nil then e1.armor = 1 end if string.find( e1.itemname, "chestplate" ) ~= nil or string.find( e1.itemname, "tunic" ) ~= nil then e1.armor = 2 end if string.find( e1.itemname, "leggings" ) ~= nil or string.find( e1.itemname, "pants" ) ~= nil then e1.armor = 3 end if string.find( e1.itemname, "boots" ) ~= nil then e1.armor = 4 end end if e2.material == nil then e2.material = 0 if string.find( e2.itemname, "leather" ) ~= nil then e2.material = 1 end if string.find( e2.itemname, "iron" ) ~= nil then e2.material = 2 end if string.find( e2.itemname, "gold" ) ~= nil then e2.material = 3 end if string.find( e2.itemname, "diamond" ) ~= nil then e2.material = 4 end e2.armor = 0 if string.find( e2.itemname, "helmet" ) ~= nil or string.find( e2.itemname, "cap" ) ~= nil then e2.armor = 1 end if string.find( e2.itemname, "chestplate" ) ~= nil or string.find( e2.itemname, "tunic" ) ~= nil then e2.armor = 2 end if string.find( e2.itemname, "leggings" ) ~= nil or string.find( e2.itemname, "pants" ) ~= nil then e2.armor = 3 end if string.find( e2.itemname, "boots" ) ~= nil then e2.armor = 4 end end if e1.material ~= e2.material then return ( e1.material < e2.material ) end if e1.armor ~= e2.armor then return ( e1.armor < e2.armor ) end return ( e1.itemname < e2.itemname ) end, fill_in_chest_total_weights = function( chest_names )

for k, chest_name in pairs(chest_names) do			local chest = p.chests[chest_name] if chest == nil then break end local total_weight = 0 for itemname, item in pairs(chest.items) do				total_weight = total_weight + item[3] end chest.totalweight = total_weight end

end,

fill_in_chest_item_details = function( chest_names )

for key, chest_name in pairs(chest_names) do			local chest = p.chests[chest_name] if chest == nil then break end for item_name, item in pairs(chest.items) do				local min_stacksize = item[1] local max_stacksize = item[2] local min_chest_numstacks = chest.numstacks[1] local max_chest_numstacks = chest.numstacks[2] local item_weight = item[3] p.chests[chest_name].items[item_name].avgamount = p.calc_average_amount_this_item_per_chest( 						min_stacksize, max_stacksize, 						min_chest_numstacks, max_chest_numstacks, 						item_weight, chest.totalweight ) p.chests[chest_name].items[item_name].chanceany = p.calc_chance_any_of_this_item_per_chest( 						min_chest_numstacks, max_chest_numstacks, 						item_weight, chest.totalweight ) p.chests[chest_name].items[item_name].numchests = p.calc_num_chests_to_find_this_item( 						min_chest_numstacks, max_chest_numstacks, 						item_weight, chest.totalweight ) p.chests[chest_name].items[item_name].itemname = item_name -- for sorting later end end end,

construct_ordered_items_from_first_chest = function( chest_names )

local items_from_first_table = {} local item_chests = {} local item_names_ordered = {} for item_name, item in pairs( p.chests[chest_names[1]].items ) do			table.insert( items_from_first_table, item ) end table.sort( items_from_first_table, q.sort_items ) for k, item in pairs( items_from_first_table ) do			table.insert( item_names_ordered, item.itemname ) item_chests[item.itemname] = true end return item_names_ordered, item_chests

end,

get_ordered_items_from_other_chests = function( chest_names, item_chests )

local items_not_from_first_table = {} for chest_idx = 2, #chest_names do			for item_name, item in pairs( p.chests[chest_names[chest_idx]].items ) do				if item_chests[item_name] == nil then p.items[item_name].itemname = item_name table.insert( items_not_from_first_table, p.chests[chest_names[chest_idx]].items[item_name] ) item_chests[item_name] = true end end end

table.sort( items_not_from_first_table, q.sort_items ) return items_not_from_first_table end,

add_other_items_to_first_list = function( chest_names, item_names_ordered, item_chests, items_not_from_first_table )

for k, item in pairs( items_not_from_first_table ) do			table.insert( item_names_ordered, item.itemname ) end return item_names_ordered end,

set_up_ordered_item_rows = function( chest_names, item_names_ordered )

for k, itemname in pairs(item_names_ordered) do			item_names_ordered[k] = {itemname} for chest_idx = 1, #chest_names do				local item_data = p.chests[chest_names[chest_idx]].items[itemname] if item_data == nil then table.insert( item_names_ordered[k], false ) else table.insert( item_names_ordered[k], item_data ) end end end

return item_names_ordered end,

construct_ordered_item_rows = function( chest_names )

-- for the first chest, sort its by chance desc, then by avg amount desc, then alphabetically asc local item_names_ordered, item_chests = q.construct_ordered_items_from_first_chest( chest_names ) if #chest_names > 1 then -- after that, sort all the remaining items in list order local items_not_from_first_table = q.get_ordered_items_from_other_chests( chest_names, item_chests ) item_names_ordered = q.add_other_items_to_first_list( chest_names, item_names_ordered, item_chests, items_not_from_first_table ) end

-- set up item_names_ordered so that each is a row, representing chest values item_names_ordered = q.set_up_ordered_item_rows( chest_names, item_names_ordered ) return item_names_ordered end,

print_table = function( chest_names, columns, ordered_item_rows )

local html = "" local use_superheader = false local superheader_sizes = {} for i = 1, #chest_names do			sh = p.chests[chest_names[i]].superheader if sh ~= nil then if superheader_sizes[sh] == nil then superheader_sizes[sh] = 0 end superheader_sizes[sh] = superheader_sizes[sh] + 1 use_superheader = true end end local has_enchanted_book = false local rowspan = ( 1 + q.tern( #chest_names > 1, 1, 0 ) + q.tern( use_superheader, 1, 0 ) ) local hide_col_description = rowspan > 1 and q.tablelength(columns) == 1 if #chest_names == 1 then local chest_min = p.chests[chest_names[1]].numstacks[1] local chest_max = p.chests[chest_names[1]].numstacks[2] html = html .. "Each " .. string.gsub( chest_names[1], "-", " " ) .. " chest contains " .. q.tern( chest_min == chest_max, chest_min, "between " .. chest_min .. " and " .. chest_max ) .. " item stacks, with the following distribution: \n " end if hide_col_description then for column_name, v in pairs(columns) do html = html .. "Values represent " .. p.columns[column_name] .. "\n " end end html = html .. " "		if has_enchanted_book then html = html .. '  &uarr; Enchanted books in the same chest will have the same enchantments. Enchantment probabilities are the same as a level-30 enchantment on an Enchantment table, but the chance of multiple enchantments is not reduced. ' end return html end, titlecase = function( str ) local buf = {} for word in string.gfind(str, "%S+") do			if word == "and" then table.insert( buf, word ) else local first, rest = string.sub( word, 1, 1 ), string.sub( word, 2 ) table.insert( buf, string.upper(first) .. string.lower(rest) ) end end return table.concat( buf, " " ) end, tern = function( cond, T , F ) if cond then return T else return F end end }

string.lpad = function(str, len, char) if char == nil then char = ' ' end return string.rep(char, len - #(''..str)) .. str end

return p