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 by argument order, unless 'all', then this 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 temple & 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" },	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 = 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, 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.insert( valid_args, "all" ) table.sort( valid_args ) return table.concat( valid_args, ",\n" )

end

q = {

massage_args = function( chests )

if #chests == 0 then chests = {"all"} end -- if argument 'all' exists, repopulate 'chests' arguments with all chests

isall = false for k, _arg in pairs(chests) do			if p.synonyms[_arg] ~= nil then chests[k] = p.synonyms[_arg] elseif _arg == "all" then chests = {} isall = true for chest_name, chest in pairs(p.chests) do					table.insert( chests, chest_name ) end break end end -- otherwise, remove invalid chest arguments

if not isall then for idx = #chests, 1, -1 do				if p.chests[chests[idx]] == nil then table.remove( chests, idx ) end end end

return chests 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, ordered_item_rows )

-- do some printing

-- local returnable = " " -- return returnable -- TODO: -- column headers -- make item rows that are sorted appropriately to the first chest, with the remaining items sorted alphabetically

local returnable = "" if #chest_names == 1 then returnable = returnable .. "Each " .. string.gsub( chest_names[1], "-", " " ) .. " chest contains " if p.chests[chest_names[1]].numstacks[1] == p.chests[chest_names[1]].numstacks[2] then returnable = returnable .. p.chests[chest_names[1]].numstacks[1] else returnable = returnable .. "between " .. p.chests[chest_names[1]].numstacks[1] .. " and " .. p.chests[chest_names[1]].numstacks[2] end returnable = returnable .. " item stacks, with the following distribution: \n" end local has_enchanted_book = false for i = 1, #ordered_item_rows do			if type( ordered_item_rows[i] ) == "table" then for j = 1, #ordered_item_rows[i] do					local chest_item = ordered_item_rows[i][j] if j == 1 and chest_item == "enchanted-book" then has_enchanted_book = true end if type( chest_item ) == "table" then local avg_amount = string.format("%.3f", chest_item.avgamount) local chance_any = string.format("%.1f", chest_item.chanceany*100) .. "%"						local num_chests = string.format("%.1f", chest_item.numchests) returnable = returnable .. "\n[" .. chest_item[1] if chest_item[1] ~= chest_item[2] then returnable = returnable .. "–" .. chest_item[2] end returnable = returnable .. "," .. chest_item[3] .. "/" .. p.chests[chest_names[j-1]].totalweight .. "," .. avg_amount .. "," .. chance_any .. "," .. num_chests .. "]"					elseif type( chest_item ) == "boolean" then returnable = returnable .. "\n" .. tostring(chest_item) else local item = p.items[chest_item] returnable = returnable .. "\n" if chest_item == "enchanted-book" then returnable = returnable .. " "						end end end returnable = returnable .. "\n" else returnable = returnable .. "\n" .. ordered_item_rows[i] end end if has_enchanted_book then returnable = returnable .. "\n" end return returnable 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 }

return p