123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- default = default or {}
- utility = utility or {}
- utility.modpath = minetest.get_modpath("utility")
- -- Localize for performance.
- local vector_round = vector.round
- local math_floor = math.floor
- local math_random = math.random
- -- Dummy function.
- fireambiance = {}
- function fireambiance.on_flame_addremove(pos)
- end
- function utility.trim_remove_special_chars(msg)
- local sub = string.gsub
- msg = sub(msg, "%z", "") -- Zero byte.
- msg = sub(msg, "%c", "") -- Control bytes.
- -- Trim whitespace.
- msg = sub(msg, "^%s+", "")
- msg = sub(msg, "%s+$", "")
- return msg
- end
- -- `level = 0/1, snappy = 3` enables quick digging via shears.
- -- Otherwise item cannot be dug by shears at all.
- --
- -- The 'hand' only digs items `level = 0` or `level = 1`, with some additional
- -- restrictions. See tool-data file for details.
- --
- -- Important/hard-to-craft nodes should be level 0 (like machines) otherwise
- -- player will lose the item if dug with a tool without a high enough level.
- --
- -- Shears only dig nodes with `level = 0/1, snappy = 3`.
- --
- -- `oddly_breakable_by_hand` only works if `level = 0/1`. HOWEVER, node drops
- -- can ONLY be obtained if the node's level is 0! Level 1 nodes may be dug if
- -- they're `oddly_breakable_by_hand`, but the player won't get the drops.
- --
- -- Base hardness for regular stone is `level = 2, cracky = 2`. Cobble is at
- -- `level = 1, cracky = 3`. These are both carefully tuned to allow new players
- -- to advance: wooden pick digs cobble to make stone pick, and a stone pick is
- -- able to dig regular stone. All other rocks/stones in the game should have
- -- their hardness calculated around regular stone/cobble.
- --
- -- The base hardness for tree trunks is `level = 2, choppy = 2`. Wood is
- -- calculated at `level = 2, choppy = 3`. All other wooden nodes should be
- -- calculated around these two.
- local dig_groups = {}
- -- Special undiggable group.
- dig_groups["ignore"] = {}
- -- Cracky stuff (stones/rocks/minerals).
- dig_groups["stone"] = {level = 2, cracky = 2} -- Carefully tuned dig-params! Do not modify.
- dig_groups["softstone"] = {level = 2, cracky = 3} -- Like sandstone.
- dig_groups["cobble"] = {level = 1, cracky = 3} -- Must be `cracky=3` otherwise cannot be dug by wooden pick.
- dig_groups["softcobble"] = {level = 1, cracky = 3, crumbly = 1} -- Can be dug by wodden pick.
- dig_groups["clay"] = {level = 0, cracky = 2, crumbly = 2}
- dig_groups["hardore"] = {level = 2, cracky = 1}
- dig_groups["hardclay"] = {level = 1, cracky = 2}
- dig_groups["ice"] = {level = 0, cracky = 2}
- dig_groups["hardice"] = {level = 1, cracky = 1}
- dig_groups["glass"] = {level = 1, cracky = 3}
- dig_groups["netherack"] = {level = 1, cracky = 3, oddly_breakable_by_hand = 1} -- Easiest thing to dig, practically!
- dig_groups["mineral"] = {level = 2, cracky = 3}
- dig_groups["hardmineral"] = {level = 2, cracky = 1}
- dig_groups["obsidian"] = {level = 3, cracky = 1} -- Obsidian, etc. Hardest possible.
- dig_groups["hardstone"] = {level = 3, cracky = 2} -- Granites, marbles.
- dig_groups["crystal"] = {level = 2, cracky = 3}
- -- Cracky stuff (building blocks/walls/bricks/etc).
- dig_groups["wall"] = {level = 1, cracky = 2}
- dig_groups["brick"] = {level = 1, cracky = 1}
- dig_groups["block"] = {level = 1, cracky = 1} -- Stone blocks, metal blocks, etc.
- -- Crumbly stuff (loose earth material).
- dig_groups["gravel"] = {level = 2, crumbly = 2} -- Cannot be dug by hand (level 1).
- dig_groups["dirt"] = {level = 2, crumbly = 3}
- dig_groups["sand"] = {level = 1, crumbly = 2}
- dig_groups["snow"] = {level = 0, crumbly = 3, oddly_breakable_by_hand = 3}
- dig_groups["mud"] = {level = 0, crumbly = 3, oddly_breakable_by_hand = 1}
- dig_groups["racksand"] = {level = 2, crumbly = 3}
- -- Choppy stuff (trees/wood).
- dig_groups["tree"] = {level = 2, choppy = 2} -- Carefully tuned dig-params! Do not change.
- dig_groups["deadtree"] = {level = 0, choppy = 2, oddly_breakable_by_hand = 1}
- dig_groups["wood"] = {level = 2, choppy = 3} -- Also wooden 'blocklike'. Planks & stuff.
- dig_groups["nyan"] = {level = 3, choppy = 1}
- -- Choppy stuff (crafted building materials).
- dig_groups["hardwood"] = {level = 1, choppy = 1}
- dig_groups["softwood"] = {level = 1, choppy = 3} -- Cactus, etc.
- -- Snappy stuff (plants/crops/leaves).
- -- Plants/crops can be dug by hand, but leaves cannot without proper tool.
- -- Addendum: player can dig leaves by hand but won't get drops.
- dig_groups["leaves"] = {level = 1, snappy = 3, choppy = 2, oddly_breakable_by_hand = 1} -- Must be `snappy=3` otherwise shears won't work.
- dig_groups["seeds"] = {level = 1, snappy = 2, oddly_breakable_by_hand = 3}
- dig_groups["plant"] = {level = 0, snappy = 3, choppy = 2} -- Must be `snappy=3` otherwise shears won't work.
- dig_groups["crop"] = {level = 0, snappy = 3, choppy = 2} -- Ditto ^^^. Also diggable by hand.
- dig_groups["straw"] = {level = 1, snappy = 2, choppy = 1, oddly_breakable_by_hand = 1}
- dig_groups["shroom"] = {level = 1, snappy = 2, choppy = 3, oddly_breakable_by_hand = 1}
- -- Misc items (items/machines/furniture/etc).
- dig_groups["wool"] = {level = 1, snappy = 3, choppy = 3}
- dig_groups["pane_wood"] = {level = 1, choppy = 3}
- dig_groups["pane_metal"] = {level = 1, cracky = 2}
- dig_groups["pane_glass"] = {level = 1, cracky = 3}
- dig_groups["fence_metal"] = {level = 1, cracky = 2}
- dig_groups["fence_wood"] = {level = 1, choppy = 2}
- dig_groups["furniture"] = {level = 0, snappy = 1, choppy = 3, oddly_breakable_by_hand = 3}
- dig_groups["item"] = {level = 0, dig_immediate = 3}
- dig_groups["bigitem"] = {level = 0, dig_immediate = 2}
- dig_groups["door_metal"] = {level = 1, cracky = 1}
- dig_groups["door_glass"] = {level = 1, cracky = 2}
- dig_groups["door_wood"] = {level = 1, choppy = 2}
- dig_groups["door_woodglass"]= {level = 1, choppy = 1}
- dig_groups["door_stone"] = {level = 1, cracky = 1}
- dig_groups["scaffolding"] = {level = 0, dig_immediate = 2}
- dig_groups["chest"] = {level = 0, choppy = 3, oddly_breakable_by_hand = 3}
- dig_groups["metalchest"] = {level = 0, cracky = 3, oddly_breakable_by_hand = 3}
- dig_groups["machine"] = {level = 0, cracky = 3} -- Must be level 0, or player may lose machine when dug!
- -- Get dig groups for a node based on its broad category.
- -- When choosing a name for a node, choose the name closest to the node's main material.
- function utility.dig_groups(name, ex)
- local groups = {}
- if not dig_groups[name] then
- minetest.log("error", "Could not find data for digging group '" .. name .. "'!")
- end
- local dig = dig_groups[name] or {level = 1, oddly_breakable_by_hand = 3}
- for k, v in pairs(dig) do
- groups[k] = v
- end
- -- Let custom groups override, include other stuff from groups.
- if ex then
- for k, v in pairs(ex) do
- groups[k] = v
- end
- end
- return groups
- end
- -- Copy standard/builtin groups only.
- -- Used mainly to ensure that stairs/microblock nodes don't include strange groups
- -- that should ONLY apply to their parent fullblock nodes.
- --
- -- Shall not return any groups used in crafting recipes!
- -- Shall return any/all groups required by tools!
- function utility.copy_builtin_groups(old_groups)
- local groups = {}
- groups.level = old_groups.level or 1
- if old_groups.crumbly then
- groups.crumbly = old_groups.crumbly
- end
- if old_groups.cracky then
- groups.cracky = old_groups.cracky
- end
- if old_groups.snappy then
- groups.snappy = old_groups.snappy
- end
- if old_groups.choppy then
- groups.choppy = old_groups.choppy
- end
- if old_groups.oddly_breakable_by_hand then
- groups.oddly_breakable_by_hand = old_groups.oddly_breakable_by_hand
- end
- if old_groups.flammable then
- groups.flammable = old_groups.flammable
- end
- if old_groups.dig_immediate then
- groups.dig_immediate = old_groups.dig_immediate
- end
- return groups
- end
- function utility.inventory_count_items(inv, listname, itemname)
- local list = inv:get_list(listname)
- local count = 0
- for i = 1, #list, 1 do
- if list[i]:get_name() == itemname then
- count = count + list[i]:get_count()
- end
- end
- return count
- end
- function utility.get_short_desc(str)
- if string.find(str, "[\n%(]") then
- str = string.sub(str, 1, string.find(str, "[\n%(]")-1)
- end
- str = string.gsub(str, "^%s+", "")
- str = string.gsub(str, "%s+$", "")
- return str
- end
- dofile(utility.modpath .. "/particle_override.lua")
- dofile(utility.modpath .. "/mapsave.lua")
- dofile(utility.modpath .. "/functions.lua")
- -- Get a player's foot position, given the player's position.
- -- Should help compatibility going into 0.5.0 and beyond.
- function utility.get_foot_pos(pos)
- return vector.add(pos, {x=0, y=0, z=0})
- end
- function utility.get_middle_pos(pos)
- return vector.add(pos, {x=0, y=1, z=0})
- end
- function utility.get_head_pos(pos)
- return vector.add(pos, {x=0, y=1.75, z=0})
- end
- -- Get rounded position of node player is standing on.
- function utility.node_under_pos(pos)
- return vector_round(vector.add(pos, {x=0, y=-0.05, z=0}))
- end
- -- Global multipliers for ABMs. Performance setting.
- default.ABM_TIMER_MULTIPLIER = 1
- default.ABM_CHANCE_MULTIPLIER = 2
- -- Global player-movement multiplier values.
- default.ROAD_SPEED = 1.3
- default.ROAD_SPEED_NETHER = 1.1
- default.ROAD_SPEED_CAVERN = 1.15
- default.SLOW_SPEED = 0.7
- default.SLOW_SPEED_NETHER = 0.85
- default.SLOW_SPEED_ICE = 0.85
- default.SLOW_SPEED_GRASS = 0.85
- default.SLOW_SPEED_PLANTS = 0.55
- default.NORM_SPEED = 1.0
- default.ROPE_SPEED = 1.1
- default.FAST_JUMP = 1.0
- default.SLOW_JUMP = 1.0
- default.NORM_JUMP = 1.0
- function utility.transform_nodebox(nodebox)
- for k, v in ipairs(nodebox) do
- for m, n in ipairs(v) do
- local p = nodebox[k][m]
- p = p / 16
- p = p - 0.5
- nodebox[k][m] = p
- end
- end
- return nodebox
- end
- -- Public API function. Sort two positions such that the first is always less than the second.
- utility.sort_positions = function(p1, p2)
- local pos1 = {x=p1.x, y=p1.y, z=p1.z}
- local pos2 = {x=p2.x, y=p2.y, z=p2.z}
-
- if pos1.x > pos2.x then pos2.x, pos1.x = pos1.x, pos2.x end
- if pos1.y > pos2.y then pos2.y, pos1.y = pos1.y, pos2.y end
- if pos1.z > pos2.z then pos2.z, pos1.z = pos1.z, pos2.z end
-
- return pos1, pos2
- end
- --
- -- optimized helper to put all items in an inventory into a drops list
- --
- function default.get_inventory_drops(pos, inventory, drops)
- local inv = minetest.get_meta(pos):get_inventory()
- local n = #drops
- for i = 1, inv:get_size(inventory) do
- local stack = inv:get_stack(inventory, i)
- if stack:get_count() > 0 then
- drops[n+1] = stack:to_table()
- n = n + 1
- end
- end
- end
- --
- -- dig upwards
- --
- function default.dig_up(pos, node, digger)
- if digger == nil then return end
- local np = {x = pos.x, y = pos.y + 1, z = pos.z}
- local nn = minetest.get_node(np)
- if nn.name == node.name then
- minetest.node_dig(np, nn, digger)
- end
- end
- --
- -- Checks if specified volume intersects a protected volume
- --
- function default.intersects_protection(minp, maxp, player_name, interval)
- -- 'interval' is the largest allowed interval for the 3D lattice of checks
- -- Compute the optimal float step 'd' for each axis so that all corners and
- -- borders are checked. 'd' will be smaller or equal to 'interval'.
- -- Subtracting 1e-4 ensures that the max co-ordinate will be reached by the
- -- for loop (which might otherwise not be the case due to rounding errors).
- local d = {}
- for _, c in pairs({"x", "y", "z"}) do
- if maxp[c] > minp[c] then
- d[c] = (maxp[c] - minp[c]) / math.ceil((maxp[c] - minp[c]) / interval) - 1e-4
- elseif maxp[c] == minp[c] then
- d[c] = 1 -- Any value larger than 0 to avoid division by zero
- else -- maxp[c] < minp[c], print error and treat as protection intersected
- minetest.log("error", "maxp < minp in 'default.intersects_protection()'")
- return true
- end
- end
- for zf = minp.z, maxp.z, d.z do
- local z = math_floor(zf + 0.5)
- for yf = minp.y, maxp.y, d.y do
- local y = math_floor(yf + 0.5)
- for xf = minp.x, maxp.x, d.x do
- local x = math_floor(xf + 0.5)
- if minetest.test_protection({x = x, y = y, z = z}, player_name) then
- return true
- end
- end
- end
- end
- return false
- end
- default.get_raillike_selection_box = function()
- return {
- type = "fixed",
- -- but how to specify the dimensions for curved and sideways rails?
- fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
- }
- end
- default.get_raillike_collision_box = function()
- return {
- type = "fixed",
- fixed = {
- {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
- },
- }
- end
- --
- -- Sapling 'on place' function to check protection of node and resulting tree volume
- --
- function default.sapling_on_place(itemstack, placer, pointed_thing,
- sapling_name, minp_relative, maxp_relative, interval)
- -- Position of sapling
- local pos = pointed_thing.under
- local node = minetest.get_node_or_nil(pos)
- local pdef = node and minetest.reg_ns_nodes[node.name]
- if pdef and pdef.on_rightclick and not placer:get_player_control().sneak then
- return pdef.on_rightclick(pos, node, placer, itemstack, pointed_thing)
- end
- if not pdef or not pdef.buildable_to then
- pos = pointed_thing.above
- node = minetest.get_node_or_nil(pos)
- pdef = node and minetest.reg_ns_nodes[node.name]
- if not pdef or not pdef.buildable_to then
- return itemstack
- end
- end
- local player_name = placer:get_player_name()
- -- Check sapling position for protection
- if minetest.is_protected(pos, player_name) then
- minetest.record_protection_violation(pos, player_name)
- return itemstack
- end
- -- Check tree volume for protection
- if default.intersects_protection(
- vector.add(pos, vector.add(minp_relative, {x=-1, y=-1, z=-1})),
- vector.add(pos, vector.add(maxp_relative, {x=1, y=1, z=1})),
- player_name,
- interval) then
- minetest.record_protection_violation(pos, player_name)
- -- Print extra information to explain
- minetest.chat_send_player(player_name, "# Server: Tree will intersect protection!")
- return itemstack
- end
- minetest.log("action", player_name .. " places node "
- .. sapling_name .. " at " .. minetest.pos_to_string(pos))
- local take_item = not minetest.setting_getbool("creative_mode")
- local newnode = {name = sapling_name}
- local ndef = minetest.reg_ns_nodes[sapling_name]
- minetest.set_node(pos, newnode)
- -- Run callback
- if ndef and ndef.after_place_node then
- -- Deepcopy place_to and pointed_thing because callback can modify it
- if ndef.after_place_node(table.copy(pos), placer,
- itemstack, table.copy(pointed_thing)) then
- take_item = false
- end
- end
- -- Run script hook
- for _, callback in ipairs(minetest.registered_on_placenodes) do
- -- Deepcopy pos, node and pointed_thing because callback can modify them
- if callback(table.copy(pos), table.copy(newnode),
- placer, table.copy(node or {}),
- itemstack, table.copy(pointed_thing)) then
- take_item = false
- end
- end
- -- Notify other hooks.
- dirtspread.on_environment(pos)
- droplift.notify(pos)
- if take_item then
- itemstack:take_item()
- end
- return itemstack
- end
- --
- -- NOTICE: This method is not an official part of the API yet!
- -- This method may change in future.
- --
- function utility.can_interact_with_node(player, pos)
- if player then
- if minetest.check_player_privs(player, "protection_bypass") then
- return true
- end
- else
- return false
- end
- local meta = minetest.get_meta(pos)
- local owner = meta:get_string("owner") or ""
- if owner == "" or owner == player:get_player_name() then
- -- Owner can access the node to any time
- return true
- end
- -- is player wielding the right key?
- local item = player:get_wielded_item()
- if item:get_name() == "key:key" or item:get_name() == "key:chain" then
- local key_meta = item:get_meta()
- if key_meta:get_string("secret") == "" then
- local key_oldmeta = item:get_metadata()
- if key_oldmeta == "" or not minetest.parse_json(key_oldmeta) then
- return false
- end
- key_meta:set_string("secret", minetest.parse_json(key_oldmeta).secret)
- item:set_metadata("")
- end
- return meta:get_string("key_lock_secret") == key_meta:get_string("secret")
- end
- return false
- end
- -- Called from bones mod to ensure the map is loaded before placing bones.
- -- Dunno if this has any real effect on the problem of bones disappearing.
- function utility.ensure_map_loaded(minp, maxp)
- local vm = minetest.get_voxel_manip()
- vm:read_from_map(minp, maxp)
- return vm:get_emerged_area() -- Return area actually loaded.
- end
- function table.shuffle(t, from, to, random)
- from = from or 1
- to = to or #t
- random = random or math_random
- local n = to - from + 1
- while n > 1 do
- local r = from + n-1
- local l = from + random(0, n-1)
- t[l], t[r] = t[r], t[l]
- n = n-1
- end
- end
|