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 and label to use, in the result table -- NOTE: order in this list doesn't matter. --      * in the grid, will sort by chance, then by avg#, then alphabetically, --      * then finally by the ordinal number z in {x,y,z}

items = { ["bread"]              = { "item", "Bread", 0 }, ["apple"]              = { "item", "Apple", 0 }, ["stick"]              = { "item", "Stick", 1 }, ["wood-planks"]        = { "block", "Oak Planks", 2 }, ["oak-wood"]           = { "block", "Oak Wood", 3 }, ["acacia-wood"]        = { "block", "Acacia Wood", 4 }, ["coal"]               = { "item", "Coal", 5 }, ["paper"]              = { "item", "Paper", 5 }, ["oak-sapling"]        = { "block", "Oak Sapling", 6 }, ["flint-and-steel"]    = { "item", "Flint and Steel", 6 }, ["string"]             = { "item", "String", 6 }, ["wheat"]              = { "item", "Wheat", 6 }, ["bone"]               = { "item", "Bone", 7 }, ["rotten-flesh"]       = { "item", "Rotten Flesh", 7 }, ["stone-axe"]          = { "item", "Stone Axe", 50 }, ["wooden-axe"]         = { "item", "Wooden Axe", 51 }, ["stone-pickaxe"]      = { "item", "Stone Pickaxe", 52 }, ["wooden-pickaxe"]     = { "item", "Wooden Pickaxe", 53 }, ["iron-pickaxe"]       = { "item", "Iron Pickaxe", 54 }, ["iron-sword"]         = { "item", "Iron Sword", 55 }, ["iron-chestplate"]    = { "item", "Iron Chestplace", 56 }, ["iron-helmet"]        = { "item", "Iron Helmet", 57 }, ["iron-leggings"]      = { "item", "Iron Leggings", 58 }, ["iron-boots"]         = { "item", "Iron Boots", 59 }, ["golden-sword"]       = { "item", "Golden Sword", 60 }, ["golden-chestplate"]  = { "item", "Golden Chestplate", 61 }, ["melon-seeds"]        = { "item", "Melon Seeds", 70 }, ["pumpkin-seeds"]      = { "item", "Pumpkin Seeds", 70 }, ["nether-wart"]        = { "item", "Nether Wart", 71 }, ["gunpowder"]          = { "item", "Gunpowder", 72 }, ["bucket"]             = { "item", "Bucket", 72 }, ["rail"]               = { "block", "Rail", 72 }, ["obsidian"]           = { "block", "Obsidian", 72 }, ["ender-pearl"]        = { "item", "Ender Pearl", 80 }, ["book"]               = { "item", "Book", 80 }, ["map"]                = { "item", "Map", 81 }, ["compass"]            = { "item", "Compass", 81 }, ["iron-ingot"]         = { "item", "Iron Ingot", 90 }, ["gold-ingot"]         = { "item", "Gold Ingot", 91 }, ["redstone"]           = { "item", "Redstone", 92 }, ["lapis-lazuli"]       = { "item", "Lapis Lazuli", 93 }, ["emerald"]            = { "item", "Emerald", 94 }, ["diamond"]            = { "item", "Diamond", 95 }, ["disc-13"]            = { "item", "Record 13", 110 }, ["disc-cat"]           = { "item", "Record Cat", 110 }, ["saddle"]             = { "item", "Saddle", 120 }, ["name-tag"]           = { "item", "Nametag", 120 }, ["horse-armor-iron"]   = { "item", "Iron Horse Armor", 130 }, ["horse-armor-gold"]   = { "item", "Gold Horse Armor", 131 }, ["horse-armor-diamond"] = { "item", "Diamond Horse Armor", 132 }, ["golden-apple"]       = { "item", "Golden Apple", 140 }, ["enchanted-book"]     = { "item", "Enchanted Book", 141 } },	-- 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"] = { 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} }		}	},

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 "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_debug( ordered_item_rows ) return q.print_table( chests, ordered_item_rows )

end,

doc = function

local keys = {} for key, val in pairs(p.chests) do			table.insert( keys, key ) end table.insert( keys, "all" ) table.sort(keys) return table.concat( keys, ", " )

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 _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,

fill_in_chest_total_weights = function( chests )

for k, chestname in pairs(chests) do			local chest = p.chests[chestname] 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( chests )

for key, chestname in pairs(chests) do			local chest = p.chests[chestname] if chest == nil then break end for itemname, 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[chestname].items[itemname].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[chestname].items[itemname].chanceany = p.calc_chance_any_of_this_item_per_chest( 						min_chest_numstacks, max_chest_numstacks, 						item_weight, chest.totalweight ) p.chests[chestname].items[itemname].numchests = p.calc_num_chests_to_find_this_item( 						min_chest_numstacks, max_chest_numstacks, 						item_weight, chest.totalweight ) p.chests[chestname].items[itemname].itemname = itemname -- for sorting later end end end,

construct_ordered_items_from_first_chest = function( chests )

local items_from_first_table = {} local item_chests = {} local item_names_ordered = {} for item_name, item in pairs( p.chests[chests[1]].items ) do			table.insert( items_from_first_table, item ) end table.sort( items_from_first_table, function( e1, e2 )			if not ( e1.chanceany == e2.chanceany ) then return ( e1.chanceany > e2.chanceany ) end			if not ( e1.avgamount == e2.avgamount ) then return ( e1.avgamount > e2.avgamount ) end			return ( e1.itemname < e2.itemname )		end ) 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( item_chests )

local items_not_from_first_table = {} for item_name, item in pairs( p.items ) do			if item_chests[item_name] == nil then p.items[item_name].itemname = item_name table.insert( items_not_from_first_table, p.items[item_name] ) end end table.sort( items_not_from_first_table, function( e1, e2 )			return ( e1[3] < e2[3] )		end ) return items_not_from_first_table end,

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

for k, item in pairs( items_not_from_first_table ) do			for idx = 2, #chests do				if not ( p.chests[chests[idx]].items[item.itemname] == nil ) and ( item_chests[item.itemname] == nil ) then table.insert( item_names_ordered, item.itemname ) item_chests[item.itemname] = true break end end end return item_names_ordered end,

set_up_ordered_item_rows = function( chests, item_names_ordered )

for k, itemname in pairs(item_names_ordered) do			item_names_ordered[k] = {itemname} for idx = 1, #chests do				local item_data = p.chests[chests[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( chests )

-- 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( chests ) if #chests > 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( item_chests ) item_names_ordered = q.add_other_items_to_first_list( chests, 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( chests, item_names_ordered ) return item_names_ordered end,

print_table = function( chests, item_names_ordered )

-- 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 #chests == 1 then returnable = returnable .. "Each " .. chests[1] .. " chest contains " if p.chests[chests[1]].numstacks[1] == p.chests[chests[1]].numstacks[2] then returnable = returnable .. p.chests[chests[1]].numstacks[1] else returnable = returnable .. "between " .. p.chests[chests[1]].numstacks[1] .. " and " .. p.chests[chests[1]].numstacks[2] end returnable = returnable .. " item stacks, with the following distribution: \n" end returnable = returnable .. "["		-- print header local has_enchanted_book = false for i = 1, #item_names_ordered do			if type( item_names_ordered[i] ) == "table" then returnable = returnable .. " ["				local item_name = nil for j = 1, #item_names_ordered[i] do					local chest_item = item_names_ordered[i][j] if j == 1 then item_name = chest_item 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 not (chest_item[1] == chest_item[2]) then returnable = returnable .. "–" .. chest_item[2] end returnable = returnable .. "," .. chest_item[3] .. "/" .. p.chests[chests[j-1]].totalweight .. "," .. avg_amount .. "," .. chance_any .. "," .. num_chests .. "]"					elseif type( chest_item ) == "boolean" then returnable = returnable .. "\n   " .. tostring(chest_item) else returnable = returnable .. "\n   " .. chest_item end end returnable = returnable .. "\n ]" else returnable = returnable .. "\n " .. item_names_ordered[i] end end returnable = returnable .. "\n]"

if has_enchanted_book then returnable = returnable .. "\nEnchanted 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 returnable end }

return p