From 99d0640d256f4aee4e41261079e898e8397ff002 Mon Sep 17 00:00:00 2001 From: LeMagnesium Date: Tue, 17 Mar 2015 22:42:21 +0100 Subject: [PATCH] Added treasurer and forceload as directories --- mods/forceload/README.md | 9 + mods/forceload/init.lua | 156 ++++++++ mods/forceload/textures/forceload_anchor.png | Bin 0 -> 463 bytes mods/treasurer/GROUPS_AND_PRECIOUSNESS | 83 +++++ mods/treasurer/README.md | 246 +++++++++++++ mods/treasurer/Treasurer_ANNOUNCEMENT | 48 +++ mods/treasurer/depends.txt | 0 mods/treasurer/descripton.txt | 1 + mods/treasurer/init.lua | 363 +++++++++++++++++++ 9 files changed, 906 insertions(+) create mode 100644 mods/forceload/README.md create mode 100644 mods/forceload/init.lua create mode 100644 mods/forceload/textures/forceload_anchor.png create mode 100644 mods/treasurer/GROUPS_AND_PRECIOUSNESS create mode 100644 mods/treasurer/README.md create mode 100644 mods/treasurer/Treasurer_ANNOUNCEMENT create mode 100644 mods/treasurer/depends.txt create mode 100644 mods/treasurer/descripton.txt create mode 100644 mods/treasurer/init.lua diff --git a/mods/forceload/README.md b/mods/forceload/README.md new file mode 100644 index 00000000..f9c5c1bb --- /dev/null +++ b/mods/forceload/README.md @@ -0,0 +1,9 @@ +Forceload Mod +============= + +This mod allows areas in the map to be keep loaded. +Use the forceload:anchor block. + +Created by rubenwardy + +License: LGPL 2.1 or later diff --git a/mods/forceload/init.lua b/mods/forceload/init.lua new file mode 100644 index 00000000..ff2a2d0c --- /dev/null +++ b/mods/forceload/init.lua @@ -0,0 +1,156 @@ +local _pts = minetest.pos_to_string +function minetest.pos_to_string(pos) + if not pos then + return "(-,-,-)" + end + return _pts(pos) +end + +-- Makes sure that force load areas are handled correctly +function ForceloadManager(filetoopen, hide_file_errors) + local blocks = {} + if filetoopen ~= nil then + local file = io.open(filetoopen, "r") + if file then + local table = minetest.deserialize(file:read("*all")) + file:close() + if type(table) == "table" then + blocks = table + end + elseif not hide_file_errors then + minetest.log("error", "File "..filetoopen.." does not exist!") + end + end + for i = 1, #blocks do + if not minetest.forceload_block(blocks[i]) then + minetest.log("error", "Failed to load block " .. minetest.pos_to_string(blocks[i])) + end + end + return { + _blocks = blocks, + load = function(self, pos) + if minetest.forceload_block(pos) then + table.insert(self._blocks, vector.new(pos)) + return true + end + minetest.log("error", "Failed to load block " .. minetest.pos_to_string(pos)) + return false + end, + unload = function(self, pos) + for i = 1, #self._blocks do + if vector.equals(pos, self._blocks[i]) then + minetest.forceload_free_block(pos) + table.remove(self._blocks, i) + return true + end + end + return false + end, + save = function(self, filename) + local file = io.open(filename, "w") + if file then + file:write(minetest.serialize(self._blocks)) + file:close() + end + end, + verify = function(self) + return self:verify_each(function(pos, block) + local name = "ignore" + if block ~= nil then + name = block.name + end + + if name == "ignore" then + if not pos.last or elapsed_time > pos.last + 15 then + pos.last = elapsed_time + if not minetest.forceload_block(pos) then + minetest.log("error", "Failed to force load " .. minetest.pos_to_string(pos)) + pos.remove = true + end + end + return false + elseif name == "forceload:anchor" then + pos.last = elapsed_time + return true + else + minetest.log("error", minetest.pos_to_string(pos) .. " shouldn't be loaded") + pos.remove = true + return false + end + end) + end, + verify_each = function(self, func) + local not_loaded = {} + for i = 1, #self._blocks do + local res = minetest.get_node(self._blocks[i]) + if not func(self._blocks[i], res) then + --[[table.insert(not_loaded, { + pos = self._blocks[i], + i = i, + b = res })]]-- + end + end + return not_loaded + end, + clean = function(self) + local i = 1 + while i <= #self._blocks do + if self._blocks[i].remove then + minetest.forceload_free_block(self._blocks[i]) + table.remove(self._blocks, i) + else + i = i + 1 + end + end + end + } +end + +local flm = ForceloadManager(minetest.get_worldpath().."/flm.json", true) + +minetest.register_privilege("forceload", "Allows players to use forceload block anchors") + +minetest.register_node("forceload:anchor",{ + description = "Block Anchor", + walkable = false, + tiles = {"forceload_anchor.png"}, + groups = {cracky = 3, oddly_breakable_by_hand = 2}, + after_destruct = function(pos) + flm:unload(pos) + flm:save(minetest.get_worldpath().."/flm.json") + end, + after_place_node = function(pos, placer) + if not minetest.check_player_privs(placer:get_player_name(), + {forceload = true}) then + minetest.chat_send_player(placer:get_player_name(), "The forceload privilege is required to do that.") + elseif flm:load(pos) then + flm:save(minetest.get_worldpath().."/flm.json") + return + end + minetest.set_node(pos, {name="air"}) + return true + end +}) + +minetest.register_craft({ + output = "forceload:anchor", + recipe = { + {"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"}, + {"default:mese_crystal", "wool:blue", "default:mese_crystal"}, + {"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"} + } +}) + +local elapsed_time = 0 +local count = 0 +minetest.register_globalstep(function(dtime) + count = count + dtime + elapsed_time = elapsed_time + dtime + if count > 5 then + count = 0 + --print("Verifying...") + flm:verify() + flm:clean() + end +end) + diff --git a/mods/forceload/textures/forceload_anchor.png b/mods/forceload/textures/forceload_anchor.png new file mode 100644 index 0000000000000000000000000000000000000000..2a5f46c0caf0f84e9e5da67b09d2c81984cf9507 GIT binary patch literal 463 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfxXNX$C5WSLllI>BT^B9x7-CYx@)^uQtbZig@l%+Qac`*Z0sh zQ?>bv&i0f&-`SX$(|j+$=!UCv^D$njSoRwcNB>%`D&6GNWxPZA5Z{$IcJc=UPSoOL(RbDPOBSrLyY~Nr_uRVw{%YS#KJU%nJ?G;^(~>Q+O~I#*`yJjFFXsAu zbK$3>G1|`#B}N`A)V1^QWL)B$laUm}{{N4Vzx{0+vFwA!O`SmBFnGH9xvX returns a random or pseudorandom number between 0 (inclusive) and 1 (exclusive) + prob_func is entirely optional, if it’s not used, treasurer will default to math.random. + You can use prob_func to define your own random function, in case you don’t like an even + distribution + +format of treasurer_groups: + This is just a table of strings, each string stands for a group name. +]] + + +--[=[ + part 2: Treasurer API +]=] + +--[[ + treasurer.register_treasure - registers a new treasure + (this means the treasure will be ready to be spawned by treasure spawning mods. + + name: name of resulting ItemStack, e.g. “mymod:item” + rarity: rarity of treasure on a scale from 0 to 1 (inclusive). lower = rarer + preciousness: preciousness of treasure on a scale from 0 (“scorched stuff”) to 10 (“diamond block”). + count: optional value which specifies the multiplicity of the item. Default is 1. See count syntax help in this file. + wear: optional value which specifies the wear of the item. Default is 0, which disables the wear. See wear syntax help in this file. + treasurer_groups: (optional) a table of group names to assign this treasure to. If omitted, the treasure is added to the default group. + This function does some basic parameter checking to catch the most obvious mistakes. If invalid parameters have been passed, the input is rejected and the function returns false. However, it does not cover every possible mistake, so some invalid treasures may slip through. + + returns: true on success, false on failure +]] +function treasurer.register_treasure(name, rarity, preciousness, count, wear, treasurer_groups ) + --[[ We don’t trust our input, so we first check if the parameters + have the correct types and refuse to add the treasure if a + parameter is malformed. + What follows is a bunch of parameter checks. + ]] + + -- check wheather name is a string + if type(name) ~= "string" then + minetest.log("error","[treasure] I rejected a treasure because the name was of type \""..type(name).."\" instead of \"string\".") + return false + end + -- first check if rarity is even a number + if type(rarity) == "number" then + -- then check wheather the rarity lies in the allowed range + if rarity < 0 or rarity > 1 then + minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because it’s rarity value is out of bounds. (it was "..tostring(rarity)..".)") + return false + end + else + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of rarity. Given type was \""..type(rarity).."\".") + return false + end + + -- check if preciousness is even a number + if type(preciousness) == "number" then + -- then check wheather the preciousness lies in the allowed range + if preciousness < 0 or preciousness > 10 then + minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because it’s preciousness value is out of bounds. (it was "..tostring(preciousness)..".)") + return false + end + else + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of preciousness. Given type was \""..type(preciousness).."\".") + return false + end + + + -- first check if count is of a correct type + if type(count) ~= "number" and type(count) ~= "nil" and type(count) ~= "table" then + minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of “count”. Given type was \""..type(count).."\".") + return false + end + -- if count’s a table, check if it’s format is correct + if type(count) == "table" then + if(not (type(count[1]) == "number" and type(count[2]) == "number" and (type(count[3]) == "function" or type(count[3]) == "nil"))) then + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had a malformed table for the count parameter.") + return false + end + end + + -- now do the same for wear: + -- first check if wear is of a correct type + if type(wear) ~= "number" and type(wear) ~= "nil" and type(wear) ~= "table" then + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of “wear”. Given type was \""..type(wear).."\".") + return false + end + -- if wear’s a table, check if it’s format is correct + if type(wear) == "table" then + if(not (type(wear[1]) == "number" and type(wear[2]) == "number" and (type(wear[3]) == "function" or type(wear[3]) == "nil"))) then + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had a malformed table for the wear parameter.") + return false + end + end + + -- check type of treasurer_group + if type(treasurer_groups) ~= "table" and type(treasurer_groups) ~= "nil" and type(treasurer_groups) ~= "string" then + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because the treasure_group parameter is of type "..tosting(type(treasurer_groups)).." (expected: nil, string or table).") + return false + end + + + + + --[[ End of checks. If we reached this point of the code, all checks have been passed + and we finally register the treasure.]] + + -- default count is 1 + if count == nil then count = 1 end + -- default wear is 0 + if wear == nil then wear = 0 end + local treasure = { + name = name, + rarity = rarity, + count = count, + wear = wear, + preciousness = preciousness, + metadata = "", + } + table.insert(treasurer.treasures, treasure) + + --[[ Assign treasure to Treasurer group(s) or default if not provided ]] + -- default Treasurer group is default + if treasurer_groups == nil then treasurer_groups = "default" end + + if(type(treasurer_groups) == "string") then + if(treasurer.groups.treasurer[treasurer_groups] == nil) then + treasurer.groups.treasurer[treasurer_groups] = {} + end + table.insert(treasurer.groups.treasurer[treasurer_groups], treasure) + elseif(type(treasurer_groups) == "table") then + for i=1,#treasurer_groups do + -- assign to Treasurer group (create table if it does not exist yet) + if(treasurer.groups.treasurer[treasurer_groups[i]] == nil) then + treasurer.groups.treasurer[treasurer_groups[i]] = {} + end + table.insert(treasurer.groups.treasurer[treasurer_groups[i]], treasure) + end + + end + + minetest.log("info","[treasurer] Treasure successfully registered: "..name) + return true +end + + +--[=[ + part 3: Treasure spawning mod (TSM) handling +]=] + +--[[ + treasurer.select_random_treasures - request some treasures from treasurer + parameters: + count: (optional) amount of items in the treasure. If this value is nil, treasurer assumes a default of 1. + min_preciousness: (optional) don’t consider treasures with a lower preciousness. nil = no lower limit + max_preciousness: (optional) don’t consider treasures with a higher preciousness. nil = no lower limit + treasurer_groups: (optional): Only consider treasures which are members of at least one of the members of the provided Treasurer group table. nil = consider all groups + returns: + a table of ItemStacks (the requested treasures) - may be empty + on error, it returns false +]] +function treasurer.select_random_treasures(count, min_preciousness, max_preciousness, treasurer_groups) + if #treasurer.treasures == 0 and count >= 1 then + minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I can’t return any because no treasure was registered to me.") + return {} + end + if count == nil then count = 1 end + local sum = 0 + local cumulate = {} + local randoms = {} + + -- copy treasures into helper table + local p_treasures = {} + if(treasurer_groups == nil) then + -- if the group filter is not used (defaul behaviour), copy all treasures + for i=1,#treasurer.treasures do + table.insert(p_treasures, treasurer.treasures[i]) + end + + -- if the group filter IS used, copy only the treasures from the said groups + elseif(type(treasurer_groups) == "string") then + if(treasurer.groups.treasurer[treasurer_groups] ~= nil) then + for i=1,#treasurer.groups.treasurer[treasurer_groups] do + table.insert(p_treasures, treasurer.groups.treasurer[treasurer_groups][i]) + end + else + minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I can’t return any because no treasure which fits to the given Treasurer group “"..treasurer_groups.."”.") + return {} + end + elseif(type(treasurer_groups) == "table") then + for t=1,#treasurer_groups do + if(treasurer.groups.treasurer[treasurer_groups[t]] ~= nil) then + for i=1,#treasurer.groups.treasurer[treasurer_groups[t]] do + table.insert(p_treasures, treasurer.groups.treasurer[treasurer_groups[t]][i]) + end + end + end + else + minetest.log("error","[treasurer] treasurer.select_random_treasures was called with a malformed treasurer_groups parameter!") + return false + end + + if(min_preciousness ~= nil) then + -- filter out too unprecious treasures + for t=#p_treasures,1,-1 do + if((p_treasures[t].preciousness) < min_preciousness) then + table.remove(p_treasures,t) + end + end + end + + if(max_preciousness ~= nil) then + -- filter out too precious treasures + for t=#p_treasures,1,-1 do + if(p_treasures[t].preciousness > max_preciousness) then + table.remove(p_treasures,t) + end + end + end + + for t=1,#p_treasures do + sum = sum + p_treasures[t].rarity + cumulate[t] = sum + end + for c=1,count do + randoms[c] = math.random() * sum + end + + local treasures = {} + for c=1,count do + for t=1,#p_treasures do + if randoms[c] < cumulate[t] then + table.insert(treasures, p_treasures[t]) + break + end + end + end + + local itemstacks = {} + for i=1,#treasures do + itemstacks[i] = treasurer.treasure_to_itemstack(treasures[i]) + end + if #itemstacks < count then + minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I could only return "..(#itemstacks)..".") + end + return itemstacks +end + +--[=[ + Part 4: internal functions +]=] + +--[[ treasurer.treasure_to_itemstack - converts a treasure table to an + ItemStack + parameter: + treasure: a treasure (see format in the head of this file) + returns: + an ItemStack +]] +function treasurer.treasure_to_itemstack(treasure) + local itemstack = {} + itemstack.name = treasure.name + itemstack.count = treasurer.determine_count(treasure) + itemstack.wear = treasurer.determine_wear(treasure) + itemstack.metadata = treasure.metadata + + return ItemStack(itemstack) +end + +--[[ + This determines the count of a treasure by taking the various different + possible types of the count value into account + This function assumes that the treasure table is valid. + returns: the count +]] +function treasurer.determine_count(treasure) + if(type(treasure.count)=="number") then + return treasure.count + else + local min,max,prob = treasure.count[1], treasure.count[2], treasure.count[3] + if(prob == nil) then + return(math.floor(min + math.random() * (max-min))) + else + return(math.floor(min + prob() * (max-min))) + end + end +end + +--[[ + This determines the wear of a treasure by taking the various different + possible types of the wear value into account. + This function assumes that the treasure table is valid. + returns: the count +]] +function treasurer.determine_wear(treasure) + if(type(treasure.wear)=="number") then + return treasure.wear + else + local min,max,prob = treasure.wear[1], treasure.wear[2], treasure.wear[3] + if(prob == nil) then + return(math.floor(min + math.random() * (max-min))) + else + return(math.floor(min + prob() * (max-min))) + end + end +end +