diff --git a/README.txt b/README.txt index 52563fd..33638e0 100644 --- a/README.txt +++ b/README.txt @@ -1,14 +1,16 @@ Mod "Dungeon Loot" [dungeon_loot] ================================= Copyright (c) 2015 BlockMen +and Amoeba -Version: 1.0 alpha +Version: 1.1 alpha -A simple mod that add to dungeons with more than 4 rooms chests that contain some loot. +A simple mod that add to dungeons chests that contain some loot. +Configurable in many aspects, see "config.lua" for more details. -License: +License: ~~~~~~~~ Code: (c) Copyright 2015 BlockMen; modified zlib-License @@ -25,7 +27,7 @@ https://github.com/BlockMen/dungeon_loot Forum: ~~~~~~ -- +https://forum.minetest.net/viewtopic.php?id=13487 Changelog: diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..3c2d6ab --- /dev/null +++ b/config.lua @@ -0,0 +1,106 @@ +-- "Dungeon Loot" [dungeon_loot] +-- Original by BlockMen, this entire file by Amoeba +-- +-- config.lua +-- +-- Note: All positive heights (above water level) are treated as depth 0. +-- Also, no comma after the item of a list. + +-- Minimum number of rooms a dungeon should have for a chest to be generated +dungeon_loot.min_num_of_rooms = 4 +-- Items on basic lists have three depth ranges for their listed amount +-- maximums; they get max/2 before first increase point (minimum of 1 if +-- amount is >0), the given max between the 1st and 2nd increase point, +-- and max*2 after the 2nd. +dungeon_loot.depth_first_basic_increase = 200 +dungeon_loot.depth_second_basic_increase = 2000 + +-- The master list of loot types +-- Note that tools and weapons should always have max_amount = 1. +-- Chance is a probability between 0 (practically never) and 1 (always), +-- so change a chance to 0 if you don't want a type (eg. weapons) included +-- in your game (or -0.001 if you want to be REALLY sure). +dungeon_loot.loot_types = { + {name="treasure", max_amount = 10, chance = 0.7, type = "depth_cutoff"}, + {name="tools", max_amount = 1, chance = 0.5, type = "depth_cutoff"}, + {name="weapons", max_amount = 1, chance = 0.1, type = "depth_cutoff"}, + {name="consumables", max_amount = 80, chance = 0.9, type = "basic_list"}, + {name="seedlings", max_amount = 5, chance = 0.3, type = "basic_list"} +} + +-- Loot type lists; these names MUST be exactly of the format: +-- "dungeon_loot.name_list" where "name" is in the above list + +-- Depth cutoff lists +-- These must be in order of increasing depth (but can include the same item +-- more than once). Method: a random number between 1 and chest depth is +-- chosen, and the item in that range is added to the loot. Then, there's +-- a chance additional items of the same type are added to stack; if the +-- random number is much greater than the item's min_depth, the amount +-- can grow pretty big. +dungeon_loot.treasure_list = { + {name="default:steel_ingot", min_depth = 0}, + {name="default:bronze_ingot", min_depth = 20}, + {name="default:gold_ingot", min_depth = 45}, + {name="default:mese_crystal", min_depth = 50}, + {name="default:diamond", min_depth = 150}, + {name="default:gold_block", min_depth = 777}, + {name="default:mese", min_depth = 800}, + {name="default:diamond_block", min_depth = 1800}, + {name="default:mese", min_depth = 2000} +} + +dungeon_loot.tools_list = { + {name="default:pick_steel", min_depth = 0}, + {name="default:shovel_diamond", min_depth = 38}, + {name="default:pick_bronze", min_depth = 40}, + {name="default:axe_diamond", min_depth = 95}, + {name="default:pick_diamond", min_depth = 100} +} + +dungeon_loot.weapons_list = { + {name="default:sword_steel", min_depth = 0}, + {name="default:sword_bronze", min_depth = 50}, + {name="default:sword_mese", min_depth = 150}, + {name="default:sword_diamond", min_depth = 250} +} + + +-- Basic lists +-- These can be of two types, either with combined chance and amount, +-- or with the two variables separated. "chance" means each item has a +-- N/M chance of being chosen, where N is it's own chance and M is the +-- total sum of chances on the list. "amount" is the maximum amount of +-- items given at the middle depth range. +dungeon_loot.consumables_list = { + {name="default:apple", chance_and_amount = 20}, + {name="default:torch", chance_and_amount = 30}, + {name="default:stick", chance_and_amount = 10} +} + +dungeon_loot.seedlings_list = { + {name="default:sapling", chance = 5, amount = 2}, + {name="default:pine_sapling", chance = 10, amount = 2}, + {name="default:junglesapling", chance = 15, amount = 2}, + {name="default:acacia_sapling", chance = 15, amount = 2} +} + +-- Add items from other mods here inside the appropriate +-- "if ... then ... end" test +-- For basic lists, just using insert without a value works fine. +-- For depth cutoff lists, you can use insert with a table index, eg. +-- table.insert(dungeon_loot.treasure_list, 5, {name="your_mod:platinum_ingot", min_depth = 120} +-- The above would add a new item to the treasure list as the 5th item, +-- moving diamond and all below it one down in the list. Just make sure +-- that the increasing min_depth order is kept. +-- Tips: With multiple insertions in a depth cutoff list, start from the +-- last item and work towards the beginning, then you don't have to calculate +-- your number of additions. Also, trying to make sure too many different +-- mods work together in a single list will probably give you a headache; +-- just create a new list (or two) for mods with lots of additions. + +if minetest.get_modpath("farming") then + table.insert(dungeon_loot.consumables_list, {name="farming:bread", chance_and_amount = 10}) + table.insert(dungeon_loot.seedlings_list, {name="farming:seed_wheat", chance = 1, amount = 10}) + table.insert(dungeon_loot.seedlings_list, {name="farming:seed_cotton", chance = 20, amount = 5}) +end diff --git a/init.lua b/init.lua index 99b2a2c..375ca1e 100644 --- a/init.lua +++ b/init.lua @@ -19,18 +19,125 @@ -- 3. This notice may not be removed or altered from any source distribution. -- -local chest_stuff = { - {name="default:apple", max = 3}, - {name="default:steel_ingot", max = 2}, - {name="default:gold_ingot", max = 2}, - {name="default:diamond", max = 1}, - {name="default:pick_steel", max = 1}, - {name="default:pick_diamond", max = 1}, - {name="default:acacia_sapling", max = 3} -} -if minetest.get_modpath("farming") then - chest_stuff[8] = {name="farming:bread", max = 3} +-- Following Code (everything before fill_chest) by Amoeba +dungeon_loot = {} +dungeon_loot.version = 1.2 + +-- Load other file(s) +local modpath = minetest.get_modpath(minetest.get_current_modname()) +dofile(modpath.."/config.lua") -- All the constants for simple tuning + + +local function get_max_loot(loot_list, depth) + local loot_type = loot_list[1].name + local loot_min_depth = loot_list[1].min_depth + for i,v in ipairs(loot_list) do + if v.min_depth < depth then + loot_type = v.name + loot_min_depth = v.min_depth + else + break + end + end + return loot_type, loot_min_depth +end + +local function get_basic_loot(loot_list, depth) + local loot_type = "" + local loot_amount = 0 + local total_chance = 0 + for i,v in ipairs(loot_list) do + if v.chance_and_amount then + total_chance = total_chance + v.chance_and_amount + elseif v.chance then + total_chance = total_chance + v.chance + else + error("No chance_and_amount or chance found in basic_list table.") + return nil, 0 + end + end + local leftover = math.random(1,total_chance) + local type_amount = 0 + for i,v in ipairs(loot_list) do + if v.chance_and_amount then + leftover = leftover - v.chance_and_amount + elseif v.chance then + leftover = leftover - v.chance + end + if leftover < 1 then + loot_type = v.name + if v.chance_and_amount then + type_amount = v.chance_and_amount + else + type_amount = v.amount + end + break + end + end + if loot_type == "" then -- Paranoia + error("Unable to choose a loot_type from basic_list table.") + return nil, 0 + end + loot_amount = math.random(1,math.ceil(type_amount/2)) + if depth > dungeon_loot.depth_first_basic_increase then + loot_amount = math.random(1,type_amount) + end + if depth > dungeon_loot.depth_second_basic_increase then + loot_amount = math.random(1,type_amount*2) + end + return loot_type, loot_amount +end + +local function get_item_and_amount(list_item, actual_depth) + if list_item.chance < math.random() then + return nil, 0 + end + -- Suspicious trickery + list_name = nil + list_name_string = "dungeon_loot." .. list_item.name .. "_list" +-- list_name = _G[list_name_string] + lsf = loadstring("list_name = " .. list_name_string) + lsf() + if list_name == nil then + error("Unable to connect " .. list_name_string .. " to actual table") + return nil, 0 + end + local amount = 0 + local loot_type = "" + local loot_depth = 0 + local max_depth = 1 + if actual_depth < 0 then + max_depth = math.ceil(math.abs(actual_depth)) + end + if list_item.type == "depth_cutoff" then + local rnd_depth = math.random(1,max_depth) + loot_type, loot_depth = get_max_loot(list_name, rnd_depth) + if list_item.max_amount == 1 then -- For tools & weapons + amount = 1 + else + -- Stop large amounts of the first item + if loot_depth < 1 then + loot_depth = 5 + end + local leftover = rnd_depth + while leftover > 0 do + amount = amount + 1 + leftover = leftover - math.random(1,loot_depth) + leftover = leftover - math.ceil(loot_depth/2) + end + end + elseif list_item.type == "basic_list" then + loot_type, amount = get_basic_loot(list_name, max_depth) + else + error("Got unknown loot table type " .. list_item.type) + loot_type = nil + end + -- Hey, if you leave out the max_amount, you deserve what you get + if list_item.max_amount and amount > list_item.max_amount then + amount = list_item.max_amount + end + return loot_type, amount end local function fill_chest(pos) @@ -40,14 +147,11 @@ local function fill_chest(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() inv:set_size("main", 8*4) - if math.random(1, 10) < 7 then - return - end - for i=1,3,1 do - local stuff = chest_stuff[math.random(1, #chest_stuff)] - local stack = ItemStack({name = stuff.name, count = math.random(1, stuff.max)}) - if not inv:contains_item("main", stack) then - inv:set_stack("main", math.random(1, 32), stack) + for i,v in ipairs(dungeon_loot.loot_types) do + local item, num = get_item_and_amount(v,pos.y) + if item then + local stack = ItemStack({name = item, count = num, wear = 0, metadata = ""}) + inv:set_stack("main",i,stack) end end end @@ -57,10 +161,13 @@ end -- Place chest in dungeons local function place_spawner(tab) - local pos = tab[math.random(1, (#tab or 4))] + if tab == nil or #tab < 1 then + return + end + local pos = tab[math.random(1, #tab)] pos.y = pos.y - 1 - local n = core.get_node_or_nil(pos) - if n and n.name ~= "air" then + local below = core.get_node_or_nil(pos) + if below and below.name ~= "air" then pos.y = pos.y + 1 core.set_node(pos, {name = "default:chest"}) fill_chest(pos) @@ -70,7 +177,7 @@ end core.set_gen_notify("dungeon") core.register_on_generated(function(minp, maxp, blockseed) local ntf = core.get_mapgen_object("gennotify") - if ntf and ntf.dungeon and #ntf.dungeon > 3 then + if ntf and ntf.dungeon and #ntf.dungeon >= dungeon_loot.min_num_of_rooms then core.after(3, place_spawner, table.copy(ntf.dungeon)) end end)