diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..2376c413 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +version.lua export-subst diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 00000000..ba6434ea --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,3 @@ +globals = {"minetest", "ItemStack", "VoxelArea", "vector", "nodecore", "include", "SecureRandom"} +color = false +quiet = 1 diff --git a/.lualocals b/.lualocals new file mode 100644 index 00000000..b2a5979c --- /dev/null +++ b/.lualocals @@ -0,0 +1,7 @@ +minetest +ItemStack +VoxelArea +vector +nodecore +include +SecureRandom diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 00000000..90672147 --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,26 @@ +======================================================================== + +To contribute to this project, you must understand and agree to the +terms in this document at the time of contribution. + +- A "contribution" consists of all files, patches and changes, source + control commits, comments and other metadata submitted to the project + maintainer(s) for inclusion or other use in the project. + +- All contributions must consist only of your own original work, to + which you retain copyright, or to which copyright is not applicable. + +- All copyrightable portions must be non-exclusively, perpetually and + irrevocably licensed under the same license terms as the overall + project. + +- You will receive attribution in the project's license, in the form of + "Portions (C) ...", which shall cover all portions of all + contributions. You agree that this constitutes adequate attribution + under the terms of the license. + +- You may request to be attributed pseudonymously at the time of + submission. Unless otherwise specified, source control metadata + will be used for attribution. + +------------------------------------------------------------------------ diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f2db39d1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (C)2018 Aaron Suen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README b/README new file mode 100644 index 00000000..472f3e46 --- /dev/null +++ b/README @@ -0,0 +1,32 @@ +======================================================================== +CORE DESIGN PRINCIPLES +------------------------------------------------------------------------ + +- Do as much in node-space in the world as possible. + - Minimize use of off-grid entities. + - Avoid encapsulating things in inventories, machines, GUIs. + - Crafting and transforming in-world. +- Minimal set of primitive composable functions. + - Each node should do one job (or one part of a job). + - Only include the most primitive, fungible components. + - Avoid redundant functionality, include fewest possible + different elements. + - Complex emergent gameplay by combining simple nodes. +- Challenging and constrained gameplay. + - Limited inventories, very restricted item storage, e.g. one + stack per node. + - Large, complex machines to design and build for resource + transformations. + - Subtle environmental hazards, like deadfalls and pestilence. +- Rich, subtle interactions. + - Digging, placing, punching and battering. + - Different effects from different tools (including empty hand). + - Different faces of node may have different effects. +- Focus on puzzle-oriented single-player/cooperative gameplay. + - Avoid dependence on action, combat, PvP. + - Slow-moving hazards, players have a chance to think and plan. + - Acessible for slow reflexes, slow networks, mobile devices. + + +........................................................................ +======================================================================== diff --git a/docs/longterm.txt b/docs/longterm.txt new file mode 100644 index 00000000..a0dd64f4 --- /dev/null +++ b/docs/longterm.txt @@ -0,0 +1,85 @@ +======================================================================== +LONG-TERM DESIGN IDEAS +------------------------------------------------------------------------ +NEW CONTENT + +- New materials to craft with. + - Dungeon materials, stonework? + - Decorations for dungeons + - Tree sap from stumps? Resin? Rubber? Shellac? + - Small plants? Reeds? Mallows? + - Sea stars, sponges, anenome, coral, other underwater things? + - Fungi, esp. tree-destroying ones, blight? + - Oil, natural gas? Fossils and fossil fuels? + - Geode, hydra crystals, radioactives? + - Shipwrecks or alien tech + - Spice worms + - Non-portable things, like "spawners" or wormholes + - Lightning, meteor strikes? + - Tubers and taproots, cacti, and other "defensive" plantlife + +- System for transporting cargo. + - Trains of minecarts? + - Conveyors, elevators? + +- Tool level differences. + - Advanced tools are NOT just the same as regular tools with + certain buffs. + - Different metals and materials require different processes. + Top-tier tools, e.g. diamond-tipped, are very different from + metal/stone/wood, require new processes. + - Higher-tier tools may "silk touch" and skip certain transforms + e.g. pick up stone as non-loose cobble or raw stone, so + players have a way to get some things in inventory. + +- Sane indoor lighting. + - Maintaining fires or sunlight are not sustainable enough. + - Access to light is a barrier to deep mining. + - Use digital logic components for this? + +- Hazards and "monsters". + - Cellular automata hazards. + - From exploration, deliving too deep, leaving things to rot, etc. + - Some spread explosively (fire), some creeping (blights). + - Healing, possibly from "relaxation" and peaceful environment. + - HC death/spawn. + +- Cultivation and farming + - Plant-like CA animals, like bee nests and clouds of bees? + Termine mounds? Ant colonies? Coral? + +- More biomes, exploration diversity. + - Water and lava springs. + - Lava deep underground as a bedrock replacement? + - Watershed mapgen? + +------------------------------------------------------------------------ +USABILITY + +- Hint system updates. + - Need a "witness" system for spontaneous in-world processes, + such as tree growth or lode cooling. Credit players within + a certain distance and line-of-sight. + - Use recipes for hint guide instead of just items in hand. + - Feedback/issues URL? + - Look for new URL buttons in 5.0+ + +- Social features + - Randomize player appearance/colors. + - Players show damage on texture? + - Players show wielditem on texture or floating ent? + +------------------------------------------------------------------------ +REFACTORS AND REFINEMENTS + +- Make neighbor/distance calcs consistent. + - We're using scan_flood in some places, face checks in others, + and find_nodes_in_area (cuboid) in others. + - Diamond shape would be most consistent. + - Should require face touching, not diagonal. + +- Make item updates search farther after they've been settled + for longer. Allow some upwards motion...? + +........................................................................ +======================================================================== diff --git a/docs/roadmap.txt b/docs/roadmap.txt new file mode 100644 index 00000000..10b9ab1c --- /dev/null +++ b/docs/roadmap.txt @@ -0,0 +1,64 @@ +======================================================================== +ROADMAP (SHORT-TERM) +------------------------------------------------------------------------ + +- Fix bug allowing totes to be placed into shelves. +- Update README. + - Add "gameplay basics" + - Crafting in-world + - Pummeling + - Sensitivity to which node face + - Check inventory screen for hints + - Installation guide? +- Digital logic + lighting via optics. + - Mirrors + - Change light angle + - Focus sunlight? + - Prism Node + - 1 face (back) is VCC face, must be fed light to do anything. + - 3 faces (front, left, right) are output faces, emit collimated light. + - 2 faces (top, bottom) are interference faces, switch off output if fed light. + - Create a light source (airlike) wherever prism beams hit, if not another prism. + - Beams can pass through one another. + - Lamp Crystal Node + - Inert when placed, reverts to inert form when dug. + - When provided a light source over time, it starts to glow. + - Glows perpetually once charged, until dug. + - Light can be used as input for prisms. + - Buttons/touchplates, no switches (build a latch for state). + - Pistons or rack&pinions for affecting world stuff. +- Visceral threats. + - Need some kind of "oh shit"-factor hazards for e.g. deeper mining. + - Flammable gas? Toxic gas? Radioactives? + - Monsters: stone-lurkers, mimics. +- Fire tech. + - Fire rework. + - Migrate to first-class API. + - Proper API for burning up items + - on_burn? burns_to? + - Move heat and quench calcs all into API. + - Cumulative heat helpers? + - Maybe generalized time-cumulative helpers that + could be used with plant growth too? + - Unify heat transformations into recipe system with + cooking recipes? + - Make fuel consumed slower by reducing flame exposure. + - Encourages designing a better furnace. + - Smothering produces charcoal instead of ash. + - Can be reburned. Maybe easier to ignite? + Maybe rejuvenate fuel in exchange for effort? + - Make ash whiter, charcoal black. +- Plant life stuff + - Tree growth catchup. + - Set last time calc at place-time and in ABM + - Define growth rate as gaussian, compute cumulative + growth as a single box-mueller transform upon ABM + - Visually show progress, e.g. display a sprout or + something above, to give feedback. + - Require light. + - Leaves settle into grass as podzol? Composting/decay? + - Dirt/sand/etc should self-compact if buried or over time. + OR loose dirt should grow grass. + +........................................................................ +======================================================================== diff --git a/game.conf b/game.conf new file mode 100644 index 00000000..06edd785 --- /dev/null +++ b/game.conf @@ -0,0 +1 @@ +name = NodeCore diff --git a/menu/background.png b/menu/background.png new file mode 100644 index 00000000..7e5a8d2f Binary files /dev/null and b/menu/background.png differ diff --git a/menu/header.png b/menu/header.png new file mode 100644 index 00000000..55e318f2 Binary files /dev/null and b/menu/header.png differ diff --git a/menu/header.svg b/menu/header.svg new file mode 100644 index 00000000..eab9ef7e --- /dev/null +++ b/menu/header.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/menu/icon.png b/menu/icon.png new file mode 100644 index 00000000..0ab15bc5 Binary files /dev/null and b/menu/icon.png differ diff --git a/menu/icon.svg b/menu/icon.svg new file mode 100644 index 00000000..aa5f5af6 --- /dev/null +++ b/menu/icon.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/nc_api/fx_digparticles.lua b/mods/nc_api/fx_digparticles.lua new file mode 100644 index 00000000..22d3aad8 --- /dev/null +++ b/mods/nc_api/fx_digparticles.lua @@ -0,0 +1,35 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore, pairs + = math, minetest, nodecore, pairs +local math_ceil, math_floor, math_random + = math.ceil, math.floor, math.random +-- LUALOCALS > --------------------------------------------------------- + +function nodecore.digparticles(nodedef, partdef) + local img = {} + if nodedef.tiles then + for i = 1, 6 do + img[#img + 1] = nodedef.tiles[i > #nodedef.tiles and #nodedef.tiles or i] + end + elseif nodedef.inventory_image then + img[1] = nodedef.inventory_image + end + if #img < 1 then return minetest.log("no pummel tile images found!") end + img = nodecore.pickrand(img) + if img.name then img = img.name end + + partdef.amount = partdef.amount and math_ceil(partdef.amount / 4) or 4 + + local t = {} + for i = 1, 4 do + partdef.texture = img .. "^[mask:[combine\\:16x16\\:" + .. math_floor(math_random() * 12) .. "," + .. math_floor(math_random() * 12) .. "=nc_api_pummel.png" + t[#t + 1] = minetest.add_particlespawner(partdef) + end + return function() + for k, v in pairs(t) do + minetest.delete_particlespawner(v) + end + end +end diff --git a/mods/nc_api/init.lua b/mods/nc_api/init.lua new file mode 100644 index 00000000..991abd5c --- /dev/null +++ b/mods/nc_api/init.lua @@ -0,0 +1,47 @@ +-- LUALOCALS < --------------------------------------------------------- +-- SKIP: nodecore +local dofile, include, minetest, rawget, rawset, table + = dofile, include, minetest, rawget, rawset, table +local table_concat, table_insert + = table.concat, table.insert +-- LUALOCALS > --------------------------------------------------------- + +local nodecore = rawget(_G, "nodecore") or {} +rawset(_G, "nodecore", nodecore) + +local include = rawget(_G, "include") or function(...) + local parts = {...} + table_insert(parts, 1, minetest.get_modpath(minetest.get_current_modname())) + if parts[#parts]:sub(-4) ~= ".lua" then + parts[#parts] = parts[#parts] .. ".lua" + end + minetest.log(table_concat(parts, "/")) + return dofile(table_concat(parts, "/")) +end +rawset(_G, "include", include) + +nodecore.version = include("version") + +include("issue7020") + +include("util_misc") +include("util_scan_flood") +include("util_logtrace") +include("util_node_is") +include("util_toolcaps") +include("util_stack") +include("util_phealth") +include("match") + +include("fx_digparticles") + +include("register_limited_abm") +include("mapgen_shared") + +include("item_on_register") +include("item_drop_in_place") +include("item_falling_repose") +include("item_alternate_loose") +include("item_group_visinv") +include("item_oldnames") +include("item_tool_wears_to") diff --git a/mods/nc_api/issue7020.lua b/mods/nc_api/issue7020.lua new file mode 100644 index 00000000..d8f21078 --- /dev/null +++ b/mods/nc_api/issue7020.lua @@ -0,0 +1,22 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, pairs, setmetatable, type + = minetest, pairs, setmetatable, type +-- LUALOCALS > --------------------------------------------------------- + +local bifn = minetest.registered_entities["__builtin:falling_node"] +local falling = { + set_node = function(self, node, meta, ...) + meta = meta or {} + if type(meta) ~= "table" then meta = meta:to_table() end + for k1, v1 in pairs(meta.inventory or {}) do + for k2, v2 in pairs(v1) do + if type(v2) == "userdata" then + v1[k2] = v2:to_string() + end + end + end + return bifn.set_node(self, node, meta, ...) + end +} +setmetatable(falling, bifn) +minetest.register_entity(":__builtin:falling_node", falling) diff --git a/mods/nc_api/item_alternate_loose.lua b/mods/nc_api/item_alternate_loose.lua new file mode 100644 index 00000000..2e443af7 --- /dev/null +++ b/mods/nc_api/item_alternate_loose.lua @@ -0,0 +1,89 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, pairs, type + = minetest, nodecore, pairs, type +-- LUALOCALS > --------------------------------------------------------- + +--[[ +Nodes that have an "alternate_loose = { ... }" definition when +registered will be registered as a pair, one being the "loose" version +and the other being the normal "solid" one. Solid-specific attributes +can be set via "alternate_solid = { ... }". The solid version will +transform to the loose one when dug, and the loose to solid when +pummeled. +--]] + +local looseimg = "^nc_api_loose.png" + +local function can_repack(level) + return function(pos, node, stats) + return nodecore.toolspeed(stats.puncher:get_wielded_item(), {thumpy = level}) + end +end + +local function repack_node(mult, replace) + if type(replace) ~= "table" then replace = {name = replace} end + return function (pos, node, stats) + if stats.duration < (mult * stats.check) then return end + nodecore.wear_current_tool(stats.puncher, {thumpy = 1}) + minetest.set_node(pos, replace) + return true + end +end + +nodecore.register_on_register_item(function(name, def) + if def.type ~= "node" then return end + + local loose = def.alternate_loose + if not loose then return end + + if not loose.tiles then + loose.tiles = nodecore.underride({}, def.tiles) + for k, v in pairs(loose.tiles) do + if type(v) == "string" then + loose.tiles[k] = v .. looseimg + elseif type(v) == "table" then + loose.tiles[k] = underride({ + name = v.name .. looseimg + }, v) + end + end + end + + nodecore.underride(loose, def) + + loose.name = name .. "_loose" + if loose.oldnames then + for k, v in pairs(loose.oldnames) do + loose.oldnames[k] = v .. "_loose" + end + end + loose.description = "Loose " .. loose.description + loose.groups = nodecore.underride({}, loose.groups or {}) + loose.groups.falling_node = 1 + + if loose.groups.crumbly and not loose.no_repack then + minetest.after(0, function() + nodecore.register_craft({ + label = "repack " .. loose.name, + action = "pummel", + nodes = { + {match = loose.name, replace = name} + }, + toolgroups = {thumpy = loose.repack_level or 1}, + }) + end) + end + + loose.alternate_loose = nil + loose.alternate_solid = nil + minetest.register_node(loose.name, loose) + + local solid = nodecore.underride(def.alternate_solid or {}, def) + solid.drop_in_place = solid.drop_in_place or loose.name + + solid.alternate_loose = nil + solid.alternate_solid = nil + minetest.register_node(name, solid) + + return true + end) diff --git a/mods/nc_api/item_drop_in_place.lua b/mods/nc_api/item_drop_in_place.lua new file mode 100644 index 00000000..cbc711de --- /dev/null +++ b/mods/nc_api/item_drop_in_place.lua @@ -0,0 +1,21 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, type + = minetest, nodecore, type +-- LUALOCALS > --------------------------------------------------------- + +--[[ +Nodes with a "drop_in_place" spec transform on node drop by dropping +into place of existing node instead of digger inventory. +--]] + +nodecore.register_on_register_item(function(name, def) + if def.type ~= "node" then return end + local dip = def.drop_in_place + if dip then + if type(dip) ~= "table" then dip = {name = dip} end + def.drop = def.drop or "" + def.after_dig_node = def.after_dig_node or function(pos) + minetest.set_node(pos, dip) + end + end + end) diff --git a/mods/nc_api/item_falling_repose.lua b/mods/nc_api/item_falling_repose.lua new file mode 100644 index 00000000..1b4b7ec9 --- /dev/null +++ b/mods/nc_api/item_falling_repose.lua @@ -0,0 +1,110 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore, pairs, type + = math, minetest, nodecore, pairs, type +local math_random + = math.random +-- LUALOCALS > --------------------------------------------------------- + +--[[ +Nodes with the "falling_repose" group will not only fall if unsupported +from below, but if there is a sufficient drop off the sides, so simulate +an "angle of repose." +--]] + +function nodecore.falling_repose_drop(posfrom, posto, node) + minetest.spawn_falling_node(posto, node, minetest.get_meta(posfrom)) + minetest.remove_node(posfrom) + posfrom.y = posfrom.y + 1 + return minetest.check_for_falling(posfrom) +end + +nodecore.register_on_register_item(function(name, def) + if def.type ~= "node" then return end + + def.groups = def.groups or {} + + if def.groups.falling_repose then def.groups.falling_node = 1 end + + def.repose_drop = def.repose_drop or nodecore.falling_repose_drop + end) + +function minetest.spawn_falling_node(pos, node, meta) + node = node or minetest.get_node(pos) + if node.name == "air" or node.name == "ignore" then + return false + end + local obj = minetest.add_entity(pos, "__builtin:falling_node") + if obj then + obj:get_luaentity():set_node(node, meta or minetest.get_meta(pos):to_table()) + minetest.remove_node(pos) + return true + end + return false +end + +local function check_empty(pos, dx, dy, dz) + for ndy = dy, 1 do + local p = {x = pos.x + dx, y = pos.y + ndy, z = pos.z + dz} + local node = minetest.get_node(p) + if not minetest.registered_nodes[node.name].buildable_to then return end + end + return {x = pos.x + dx, y = pos.y, z = pos.z + dz} +end +function nodecore.falling_repose_check(pos) + if minetest.check_single_for_falling(pos) then return end + local node = minetest.get_node(pos) + local def = minetest.registered_nodes[node.name] + local repose = def.groups.falling_repose + if not repose then return end + + -- Reposing nodes can always sit comfortably atop + -- a non-moving node; it's only when stacked on other + -- falling nodes that they can slip off. + if not (minetest.registered_nodes[minetest.get_node( + {x = pos.x, y = pos.y - 1, z = pos.z}).name].groups + or {}).falling_node + then return end + + local open = {} + local ok = check_empty(pos, 1, -repose, 0) + if ok then open[1] = ok end + ok = check_empty(pos, -1, -repose, 0) + if ok then open[#open + 1] = ok end + ok = check_empty(pos, 0, -repose, 1) + if ok then open[#open + 1] = ok end + ok = check_empty(pos, 0, -repose, -1) + if ok then open[#open + 1] = ok end + if #open < 1 then return end + return def.repose_drop(pos, open[math_random(1, #open)], node) +end + +local reposeq +local qqty +local qmax = 100 +local function reposeall() + for _, v in pairs(reposeq) do v() end + reposeq = nil + qqty = nil +end +nodecore.register_limited_abm({ + label = "Falling Repose", + nodenames = {"group:falling_repose"}, + neighbors = {"air"}, + interval = 2, + chance = 5, + action = function(pos, node) + if not reposeq then + reposeq = {} + qqty = 0 + minetest.after(0, reposeall) + end + local f = function() nodecore.falling_repose_check(pos) end + if #reposeq > qmax then + local i = math_random(1, qqty) + if i < qmax then reposeq[i] = f end + else + reposeq[#reposeq + 1] = f + end + qqty = qqty + 1 + end + }) diff --git a/mods/nc_api/item_group_visinv.lua b/mods/nc_api/item_group_visinv.lua new file mode 100644 index 00000000..5d404693 --- /dev/null +++ b/mods/nc_api/item_group_visinv.lua @@ -0,0 +1,158 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore, pairs, type + = math, minetest, nodecore, pairs, type +local math_floor, math_pi, math_random, math_sqrt + = math.floor, math.pi, math.random, math.sqrt +-- LUALOCALS > --------------------------------------------------------- + +--[[ +Helpers for visible inventory. Use "visinv" node group. +Sets up on_construct, after_destruct and an ABM to manage +the visual entities. +--]] + +local modname = minetest.get_current_modname() + +------------------------------------------------------------------------ +-- VISIBLE STACK ENTITY + +local function stackentprops(stack, func, rot) + rot = rot or 1 + local t = { + hp_max = 1, + physical = false, + collide_with_objects = false, + collisionbox = {0, 0, 0, 0, 0, 0}, + visual = "wielditem", + visual_size = {x = 0.4, y = 0.4 }, + textures = {""}, + spritediv = {x = 1, y = 1}, + initial_sprite_basepos = {x = 0, y = 0}, + is_visible = false + } + if stack then + t.is_visible = true + t.textures[1] = stack:get_name() + local s = 0.2 + 0.1 * stack:get_count() / stack:get_stack_max() + t.visual_size = {x = s, y = s} + local max = stack:get_stack_max() + local ratio = max / stack:get_count() + t.automatic_rotate = rot * (ratio == 1 and max > 1 + and 0.05 or 0.15) * math_sqrt(ratio) + if func then func(s) end + end + return t +end + +minetest.register_entity(modname .. ":stackent", { + initial_properties = stackentprops(), + is_stack = true, + itemcheck = function(self) + local pos = self.object:getpos() + local stack = nodecore.stack_get(pos) + if not stack or stack:is_empty() then return self.object:remove() end + self.rot = self.rot or math_random(1, 2) * 2 - 3 + self.object:set_properties(stackentprops(stack, function(s) + pos.y = math_floor(pos.y + 0.5) - 0.5 + s + self.object:setpos(pos) + end, self.rot)) + self.object:set_yaw(math_random() * math_pi * 2) + if pos.y - math_floor(pos.y) ~= 1/64 then + pos.y = pos.y + 1/64 + self.object:setpos(pos) + end + end, + on_activate = function(self) + self.cktime = 0.00001 + end, + on_step = function(self, dtime) + self.cktime = (self.cktime or 0) - dtime + if self.cktime > 0 then return end + self.cktime = 1 + return self:itemcheck() + end + }) + +function nodecore.visinv_update_ents(pos, node) + node = node or minetest.get_node(pos) + local def = minetest.registered_nodes[node.name] + local max = def and def.groups and def.groups.visinv and 1 or 0 + + local found = {} + for k, v in pairs(minetest.get_objects_inside_radius(pos, 0.5)) do + if v and v.get_luaentity and v:get_luaentity() + and v:get_luaentity().is_stack then + found[#found + 1] = v + end + end + + if #found < max then + minetest.add_entity(pos, modname .. ":stackent") + else + while #found > max do + found[#found]:remove() + found[#found] = nil + end + end +end + +------------------------------------------------------------------------ +-- NODE REGISTRATION HELPERS + +function nodecore.visinv_on_construct(pos) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + inv:set_size("solo", 1) + nodecore.visinv_update_ents(pos) +end + +function nodecore.visinv_after_destruct(pos) + nodecore.visinv_update_ents(pos) + minetest.after(0, function() + minetest.check_for_falling(pos) + end) +end + +nodecore.register_on_register_item(function(name, def) + if def.type ~= "node" then return end + + def.groups = def.groups or {} + + if def.groups.visinv then + def.on_construct = def.on_construct or nodecore.visinv_on_construct + def.after_destruct = def.after_destruct or nodecore.visinv_after_destruct + end + end) + +nodecore.register_limited_abm({ + label = "VisInv Check", + nodenames = {"group:visinv"}, + interval = 1, + chance = 1, + action = function(...) return nodecore.visinv_update_ents(...) end + }) + +------------------------------------------------------------------------ +-- DIG INVENTORY + +local digpos +local old_node_dig = minetest.node_dig +minetest.node_dig = function(pos, ...) + local function helper(...) + digpos = nil + return ... + end + digpos = pos + return helper(old_node_dig(pos, ...)) +end +local old_get_node_drops = minetest.get_node_drops +minetest.get_node_drops = function(...) + local drops = old_get_node_drops(...) + if not digpos then return drops end + drops = drops or {} + local stack = nodecore.stack_get(digpos) + if stack and not stack:is_empty() then + drops[#drops + 1] = stack + end + return drops +end diff --git a/mods/nc_api/item_oldnames.lua b/mods/nc_api/item_oldnames.lua new file mode 100644 index 00000000..2d298ca4 --- /dev/null +++ b/mods/nc_api/item_oldnames.lua @@ -0,0 +1,12 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, pairs + = minetest, nodecore, pairs +-- LUALOCALS > --------------------------------------------------------- + +nodecore.register_on_register_item(function(name, def) + if def.oldnames then + for k, v in pairs(def.oldnames) do + minetest.register_alias(v, name) + end + end + end) diff --git a/mods/nc_api/item_on_register.lua b/mods/nc_api/item_on_register.lua new file mode 100644 index 00000000..0fc85533 --- /dev/null +++ b/mods/nc_api/item_on_register.lua @@ -0,0 +1,17 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, minetest, nodecore + = ipairs, minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +nodecore.register_on_register_item, +nodecore.registered_on_register_item += nodecore.mkreg() + +local oldreg = minetest.register_item +function minetest.register_item(name, def, ...) + for _, v in ipairs(nodecore.registered_on_register_item) do + local x = v(name, def, ...) + if x then return x end + end + return oldreg(name, def, ...) +end diff --git a/mods/nc_api/item_tool_wears_to.lua b/mods/nc_api/item_tool_wears_to.lua new file mode 100644 index 00000000..63d3f0d7 --- /dev/null +++ b/mods/nc_api/item_tool_wears_to.lua @@ -0,0 +1,20 @@ +-- LUALOCALS < --------------------------------------------------------- +local ItemStack, minetest, nodecore + = ItemStack, minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +nodecore.register_on_register_item(function(name, def) + if def.tool_wears_to then + def.after_use = def.after_use or function(what, who, node, dp) + what:add_wear(dp.wear) + if what:get_count() == 0 then + if def.sound and def.sound.breaks then + minetest.sound_play(def.sound.breaks, + {pos = who:get_pos(), gain = 0.5}) + end + return ItemStack(def.tool_wears_to) + end + return what + end + end + end) diff --git a/mods/nc_api/mapgen_shared.lua b/mods/nc_api/mapgen_shared.lua new file mode 100644 index 00000000..a673b97c --- /dev/null +++ b/mods/nc_api/mapgen_shared.lua @@ -0,0 +1,43 @@ +-- LUALOCALS < --------------------------------------------------------- +local VoxelArea, ipairs, math, minetest, nodecore, table + = VoxelArea, ipairs, math, minetest, nodecore, table +local math_floor, table_insert + = math.floor, table.insert +-- LUALOCALS > --------------------------------------------------------- + +local mapgens = {} +nodecore.registered_mapgen_shared = mapgens + +local prios = {} + +function nodecore.register_mapgen_shared(func, prio) + prio = prio or 0 + local min = 1 + local max = #mapgens + 1 + while max > min do + local try = math_floor((min + max) / 2) + local oldp = prios[try] + if prio < oldp then + min = try + 1 + else + max = try + end + end + table_insert(mapgens, min, func) + table_insert(prios, min, prio) +end + +minetest.register_on_generated(function(minp, maxp) + local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") + local data = vm:get_data() + local area = VoxelArea:new({MinEdge = emin, MaxEdge = emax}) + + for _, v in ipairs(mapgens) do + v(minp, maxp, area, data, vm, emin, emax) + end + + vm:set_data(data) + vm:set_lighting({day = 0, night = 0}) + vm:calc_lighting() + vm:write_to_map() + end) diff --git a/mods/nc_api/match.lua b/mods/nc_api/match.lua new file mode 100644 index 00000000..68c281d0 --- /dev/null +++ b/mods/nc_api/match.lua @@ -0,0 +1,74 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, pairs, type + = minetest, nodecore, pairs, type +-- LUALOCALS > --------------------------------------------------------- + +local match_skip = { + name = true, + param2 = true, + param = true, + groups = true, + stack = true, + count = true, + wear = true +} + +function nodecore.match(thing, crit) + if not thing then return end + + if type(crit) == "string" then crit = {name = crit} end + + thing.count = thing.count or 1 + + thing = nodecore.underride({}, thing) + if thing.stack then + thing.name = thing.stack:get_name() + thing.count = thing.stack:get_count() + thing.wear = thing.stack:get_wear() + thing.stacked = true + end + if not thing.name then + thing = nodecore.underride(thing, minetest.get_node(thing)) + end + local def = minetest.registered_items[thing.name] + if (not thing.stacked) and def.groups and def.groups.is_stack_only then + local stack = nodecore.stack_get(thing) + if stack and not stack:is_empty() then + thing.name = stack:get_name() + def = minetest.registered_items[thing.name] + thing.count = stack:get_count() + thing.wear = stack:get_wear() + end + thing.stacked = true + end + + if crit.name and thing.name ~= crit.name then return end + if crit.param2 and thing.param2 ~= crit.param2 then return end + if crit.param and thing.param ~= crit.param then return end + if crit.count and thing.count ~= crit.count then return end + if crit.count == nil and thing.count ~= 1 then return end + if crit.wear then + if crit.wear < 1 then crit.wear = crit.wear * 65535 end + if thing.wear > crit.wear then return end + end + + if crit.groups then + if not def or not def.groups then return end + for k, v in pairs(crit.groups) do + if v == true then + if not def.groups[k] then return end + elseif v == false then + if def.groups[k] then return end + else + if def.groups[k] ~= v then return end + end + end + end + for k, v in pairs(crit) do + if not match_skip[k] then + if not def or def[k] ~= v then return end + end + end + + return thing +end diff --git a/mods/nc_api/register_limited_abm.lua b/mods/nc_api/register_limited_abm.lua new file mode 100644 index 00000000..c7b05d46 --- /dev/null +++ b/mods/nc_api/register_limited_abm.lua @@ -0,0 +1,69 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore, pairs, unpack + = math, minetest, nodecore, pairs, unpack +local math_random + = math.random +-- LUALOCALS > --------------------------------------------------------- + +local genlabels = 0 + +function nodecore.register_limited_abm(def) + def = nodecore.underride(def, { + limited_queue = {}, + limited_seen = {}, + limited_qty = 0, + limited_max = 1000, + limited_interval = 1, + limited_jitter = 0.05, + limited_action = def.action or function() end, + catch_up = false + }) + + if not def.label then + def.label = minetest.get_current_modname() .. ":" .. genlabels + genlabels = genlabels + 1 + end + def.limited_alert = def.limited_alert or def.limited_max + + def.action = function(pos, ...) + local hash = minetest.hash_node_position(pos) + local seen = def.limited_seen + if seen[hash] then return end + seen[hash] = true + + local q = def.limited_queue + local max = def.limited_max + local nqty = def.limited_qty + 1 + if #q < max then + q[#q + 1] = {pos, ...} + else + local r = math_random(1, nqty) + if r <= #q then q[r] = {pos, ...} end + end + def.limited_qty = nqty + end + + local function pumpq() + minetest.after(def.limited_interval + - def.limited_jitter + + def.limited_jitter * math_random() * 2, + pumpq) + + if def.limited_qty >= def.limited_alert then + minetest.log("limited abm \"" .. def.label .. "\" filled (" + .. def.limited_qty .. "/" .. def.limited_max .. ")") + end + + local act = def.limited_action + for _, args in pairs(def.limited_queue) do + act(unpack(args)) + end + + def.limited_queue = {} + def.limited_seen = {} + def.limited_qty = 0 + end + pumpq() + + return minetest.register_abm(def) +end diff --git a/mods/nc_api/textures/nc_api_loose.png b/mods/nc_api/textures/nc_api_loose.png new file mode 100644 index 00000000..03e11d0e Binary files /dev/null and b/mods/nc_api/textures/nc_api_loose.png differ diff --git a/mods/nc_api/textures/nc_api_pummel.png b/mods/nc_api/textures/nc_api_pummel.png new file mode 100644 index 00000000..b86a5947 Binary files /dev/null and b/mods/nc_api/textures/nc_api_pummel.png differ diff --git a/mods/nc_api/util_logtrace.lua b/mods/nc_api/util_logtrace.lua new file mode 100644 index 00000000..dbd65020 --- /dev/null +++ b/mods/nc_api/util_logtrace.lua @@ -0,0 +1,47 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, minetest, nodecore, pairs, print, table, type + = ipairs, minetest, nodecore, pairs, print, table, type +local table_concat + = table.concat +-- LUALOCALS > --------------------------------------------------------- + +minetest.register_privilege("logtrace", "Receive server debug/trace messages.") + +core.register_chatcommand("logtrace", { + description = "Toggle debug trace messages", + privs = {logtrace = true}, + func = function(name) + local player = minetest.get_player_by_name(name) + if not player then return end + local old = player:get_attribute("logtrace") or "" + return player:set_attribute("logtrace", (old == "") and "1" or "") + end, + }) + +function nodecore.logtrace(...) + local t = {"#", ...} + for i, v in ipairs(t) do + if type(v) == "table" then + t[i] = minetest.serialize(v):sub(("return "):length()) + end + end + local msg = table_concat(t, " ") + for _, p in pairs(minetest.get_connected_players()) do + local n = p:get_player_name() + if minetest.get_player_privs(n).logtrace then + local a = p:get_attribute("logtrace") + if a and a ~= "" then + minetest.chat_send_player(n, msg) + end + end + end +end + +local function tracify(func) + return function(...) + nodecore.logtrace(...) + return func(...) + end +end +print = tracify(print) +minetest.log = tracify(minetest.log) diff --git a/mods/nc_api/util_misc.lua b/mods/nc_api/util_misc.lua new file mode 100644 index 00000000..f234c5b2 --- /dev/null +++ b/mods/nc_api/util_misc.lua @@ -0,0 +1,203 @@ +-- LUALOCALS < --------------------------------------------------------- +local ItemStack, ipairs, math, minetest, nodecore, pairs, type + = ItemStack, ipairs, math, minetest, nodecore, pairs, type +local math_random + = math.random +-- LUALOCALS > --------------------------------------------------------- + +for k, v in pairs(minetest) do + if type(v) == "function" then + -- Late-bind in case minetest methods overridden. + nodecore[k] = function(...) return minetest[k](...) end + else + nodecore[k] = v + end +end + +local function underride(t, u, v, ...) + if v then underride(u, v, ...) end + for k, v in pairs(u) do + if t[k] == nil then + t[k] = v + elseif type(t[k]) == "table" and type(v) == "table" then + underride(t[k], v) + end + end + return t +end +nodecore.underride = underride + +function nodecore.mkreg() + local t = {} + local f = function(x) t[#t + 1] = x end + return f, t +end + +function nodecore.memoize(func) + local cache + return function() + if cache then return cache[1] end + cache = {func()} + return cache[1] + end +end + +function nodecore.dirs() + return { + {x = 1, y = 0, z = 0}, + {x = -1, y = 0, z = 0}, + {x = 0, y = 1, z = 0}, + {x = 0, y = -1, z = 0}, + {x = 0, y = 0, z = 1}, + {x = 0, y = 0, z = -1} + } +end + +function nodecore.pickrand(tbl, weight) + weight = weight or function() end + local t = {} + local max = 0 + for k, v in pairs(tbl) do + local w = weight(v) or 1 + if w > 0 then + max = max + w + t[#t + 1] = {w = w, k = k, v = v} + end + end + if max <= 0 then return end + max = math_random() * max + for i, v in ipairs(t) do + max = max - v.w + if max <= 0 then return v.v, v.k end + end +end + +function nodecore.extend_item(name, func) + local orig = minetest.registered_items[name] + local copy = {} + for k, v in pairs(orig) do copy[k] = v end + copy = func(copy, orig) or copy + minetest.register_item(":" .. name, copy) +end + +function nodecore.fixedbox(...) return {type = "fixed", fixed = {...}} end + +function nodecore.interact(player) + if type(player) ~= "string" then + player = player:get_player_name() + end + return minetest.get_player_privs(player).interact +end + +function nodecore.wieldgroup(who, group) + local wielded = who and who:get_wielded_item() + local nodedef = minetest.registered_nodes[wielded:get_name()] + if nodedef then return nodedef.groups and nodedef.groups[group] end + local caps = wielded and wielded:get_tool_capabilities() + return caps and caps.groupcaps and caps.groupcaps[group] +end + +function nodecore.toolspeed(what, groups) + if not what then return end + local dg = what:get_tool_capabilities().groupcaps + local t + for gn, lv in pairs(groups) do + local gt = dg[gn] + gt = gt and gt.times + gt = gt and gt[lv] + if gt and (not t or t > gt) then t = gt end + end + return t +end + +function nodecore.interval(after, func) + local function go() + minetest.after(after, go) + return func() + end + minetest.after(after, go) +end + +function nodecore.wear_wield(player, groups, qty) + local wielded = player:get_wielded_item() + if wielded then + local wdef = wielded:get_definition() + local tp = wielded:get_tool_capabilities() + local dp = minetest.get_dig_params(groups, tp) + if wdef and wdef.after_use then + wielded = wdef.after_use(wielded, player, nil, dp) or wielded + else + if not minetest.settings:get_bool("creative_mode") then + wielded:add_wear(dp.wear * (qty or 1)) + if wielded:get_count() <= 0 and wdef.sound + and wdef.sound.breaks then + minetest.sound_play(wdef.sound.breaks, + {pos = pos, gain = 0.5}) + end + end + end + return player:set_wielded_item(wielded) + end +end + +function nodecore.consume_wield(player, qty) + local wielded = player:get_wielded_item() + if wielded then + local wdef = wielded:get_definition() + if wdef.stack_max > 1 and qty then + local have = wielded:get_count() - qty + if have <= 0 then + wielded = ItemStack("") + else + wielded:set_count(have) + end + end + return player:set_wielded_item(wielded) + end +end + +function nodecore.loaded_mods() + local t = {} + for _, v in pairs(minetest.get_modnames()) do + t[v] = true + end + return t +end + +function nodecore.node_group(name, pos, node) + node = node or minetest.get_node(pos) + local def = minetest.registered_nodes[node.name] + return def and def.groups and def.groups[name] +end + +function nodecore.item_eject(pos, stack, speed, qty, vel) + stack = ItemStack(stack) + speed = speed or 0 + vel = vel or {x = 0, y = 0, z = 0} + if speed == 0 and vel.x == 0 and vel.y == 0 and vel.z == 0 + and nodecore.place_stack and minetest.get_node(pos).name == "air" then + stack:set_count(stack:get_count() * (qty or 1)) + return nodecore.place_stack(pos, stack) + end + for i = 1, (qty or 1) do + local v = { + x = vel.x + (math_random() - 0.5) * speed, + y = vel.y + math_random() * speed, + z = vel.z + (math_random() - 0.5) * speed, + } + local p = { + x = v.x > 0 and pos.x + 0.4 or v.x < 0 and pos.x - 0.4 or pos.x, + y = pos.y + 0.25, + z = v.z > 0 and pos.z + 0.4 or v.z < 0 and pos.z - 0.4 or pos.z, + } + local obj = minetest.add_item(p, stack) + if obj then obj:setvelocity(v) end + end +end + +function nodecore.quenched(pos) + return #minetest.find_nodes_in_area( + {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1}, + {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, + {"group:coolant"}) > 0 +end diff --git a/mods/nc_api/util_node_is.lua b/mods/nc_api/util_node_is.lua new file mode 100644 index 00000000..b5cc4ef8 --- /dev/null +++ b/mods/nc_api/util_node_is.lua @@ -0,0 +1,8 @@ +-- LUALOCALS < --------------------------------------------------------- +local nodecore + = nodecore +-- LUALOCALS > --------------------------------------------------------- + +function nodecore.buildable_to(node_or_pos) + return nodecore.match(node_or_pos, {buildable_to = true}) +end diff --git a/mods/nc_api/util_phealth.lua b/mods/nc_api/util_phealth.lua new file mode 100644 index 00000000..d06883a2 --- /dev/null +++ b/mods/nc_api/util_phealth.lua @@ -0,0 +1,26 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, nodecore, tonumber, tostring + = math, nodecore, tonumber, tostring +local math_ceil + = math.ceil +-- LUALOCALS > --------------------------------------------------------- + +local function getphealth(player) + return player:get_hp() + + tonumber(player:get_attribute("dhp") or "0") +end +nodecore.getphealth = getphealth + +local function setphealth(player, hp) + if hp > 20 then hp = 20 end + if hp < 0 then hp = 0 end + local whole = math_ceil(hp) + local dhp = hp - whole + player:set_attribute("dhp", tostring(dhp)) + return player:set_hp(whole) +end +nodecore.setphealth = setphealth + +function nodecore.addphealth(player, hp) + return setphealth(player, getphealth(player) + hp) +end diff --git a/mods/nc_api/util_scan_flood.lua b/mods/nc_api/util_scan_flood.lua new file mode 100644 index 00000000..3e6530cb --- /dev/null +++ b/mods/nc_api/util_scan_flood.lua @@ -0,0 +1,41 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, math, minetest, next, nodecore, pairs, table + = ipairs, math, minetest, next, nodecore, pairs, table +local math_random, table_insert + = math.random, table.insert +-- LUALOCALS > --------------------------------------------------------- + +local dirs = nodecore.dirs() + +function nodecore.scan_flood(pos, range, func) + local q = {pos} + local seen = { } + for d = 0, range do + local next = {} + for i, p in ipairs(q) do + local res = func(p, d) + if res then return res end + if res == nil then + for k, v in pairs(dirs) do + local np = { + x = p.x + v.x, + y = p.y + v.y, + z = p.z + v.z + } + local nk = minetest.hash_node_position(np) + if not seen[nk] then + seen[nk] = true + np.dir = v + table_insert(next, np) + end + end + end + end + if #next < 1 then break end + for i = 1, #next do + local j = math_random(1, #next) + next[i], next[j] = next[j], next[i] + end + q = next + end +end diff --git a/mods/nc_api/util_stack.lua b/mods/nc_api/util_stack.lua new file mode 100644 index 00000000..c4cc3964 --- /dev/null +++ b/mods/nc_api/util_stack.lua @@ -0,0 +1,27 @@ +-- LUALOCALS < --------------------------------------------------------- +local ItemStack, minetest, nodecore + = ItemStack, minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +function nodecore.node_inv(pos) + return minetest.get_meta(pos):get_inventory() +end + +function nodecore.stack_get(pos) + return nodecore.node_inv(pos):get_stack("solo", 1) +end + +function nodecore.stack_set(pos, stack) + return nodecore.node_inv(pos):set_stack("solo", 1, ItemStack(stack)) +end + +function nodecore.stack_add(pos, stack) + return nodecore.node_inv(pos):add_item("solo", ItemStack(stack)) +end + +function nodecore.stack_giveto(pos, player) + local stack = nodecore.stack_get(pos) + stack = player:get_inventory():add_item("main", stack) + nodecore.stack_set(pos, stack) + return stack:is_empty() +end diff --git a/mods/nc_api/util_toolcaps.lua b/mods/nc_api/util_toolcaps.lua new file mode 100644 index 00000000..98b57015 --- /dev/null +++ b/mods/nc_api/util_toolcaps.lua @@ -0,0 +1,35 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, nodecore, pairs + = math, nodecore, pairs +local math_pow + = math.pow +-- LUALOCALS > --------------------------------------------------------- + +local basetimes = { + cracky = 3, + thumpy = 2, + choppy = 2, + crumbly = 0.5, + snappy = 0.4, +} + +function nodecore.toolcaps(opts) + if opts.uses == nil then opts.uses = 1 end + local gcaps = {} + for gn, bt in pairs(basetimes) do + local lv = opts[gn] + if lv then + local times = {} + for n = 1, lv do + local tt = math_pow(0.5, lv - n) * bt + if tt < 0.25 then tt = 0.25 end + times[n] = tt + end + gcaps[gn] = { + times = times, + uses = 5 * math_pow(3, lv) * opts.uses + } + end + end + return { groupcaps = gcaps } +end diff --git a/mods/nc_api/version.lua b/mods/nc_api/version.lua new file mode 100644 index 00000000..dc63d374 --- /dev/null +++ b/mods/nc_api/version.lua @@ -0,0 +1,12 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, tonumber + = math, tonumber +local math_floor + = math.floor +-- LUALOCALS > --------------------------------------------------------- + +local stamp = tonumber("$Format:%at$") +if not stamp then return end +stamp = math_floor((stamp - 1540612800) / 60) +stamp = ("00000000" .. stamp):sub(-8) +return stamp .. "-$Format:%h$" diff --git a/mods/nc_api_craft/craft_check.lua b/mods/nc_api_craft/craft_check.lua new file mode 100644 index 00000000..5a8650e7 --- /dev/null +++ b/mods/nc_api_craft/craft_check.lua @@ -0,0 +1,110 @@ +-- LUALOCALS < --------------------------------------------------------- +local ItemStack, ipairs, minetest, nodecore, pairs, type + = ItemStack, ipairs, minetest, nodecore, pairs, type +-- LUALOCALS > --------------------------------------------------------- + +local function craftcheck(recipe, pos, node, data, xx, xz, zx, zz) + local function rel(x, y, z) + return { + x = pos.x + xx * x + zx * z, + y = pos.y + y, + z = pos.z + xz * x + zz * z + } + end + data.wield = ItemStack(data.wield or data.crafter and data.crafter:get_wielded_item()) + if recipe.check and not recipe.check(pos, data, rel) then return end + if recipe.wield and (not data.wield or not nodecore.match( + {stack = data.wield}, recipe.wield)) then return end + if recipe.normal then + if data.pointed.type ~= "node" or + recipe.normal.y ~= data.pointed.above.y - data.pointed.under.y then return end + local rx = recipe.normal.x * xx + recipe.normal.z * zx + if rx ~= data.pointed.above.x - data.pointed.under.x then return end + local rz = recipe.normal.x * xz + recipe.normal.z * zz + if rz ~= data.pointed.above.z - data.pointed.under.z then return end + end + local mindur = recipe.duration or 0 + if recipe.toolgroups then + if not data.wield then return end + local dg = data.wield:get_tool_capabilities().groupcaps + local t + for gn, lv in pairs(recipe.toolgroups) do + local gt = dg[gn] + gt = gt and gt.times + gt = gt and gt[lv] + if gt and (not t or t > gt) then t = gt end + end + if not t then return end + mindur = mindur + t + end + if mindur > 0 and (not data.duration or data.duration < mindur) then + if data.inprogress then return data.inprogress(data, recipe) end + return + end + for _, v in pairs(recipe.nodes) do + if v ~= recipe.root and v.match then + local p = rel(v.x, v.y, v.z) + if not nodecore.match(p, v.match) then return end + end + end + if recipe.before then recipe.before(pos, rel, data) end + for _, v in pairs(recipe.nodes) do + if v.replace then + local p = rel(v.x, v.y, v.z) + local r = v.replace + while type(r) == "function" do + r = r(p, v) + end + if r and type(r) == "string" then + r = {name = r} + end + if r then minetest.set_node(p, r) end + end + end + if recipe.items then + for _, v in pairs(recipe.items) do + nodecore.item_eject(rel(v.x or 0, v.y or 0, v.z or 0), + v.name, v.scatter, v.count, v.velocity) + end + end + if recipe.consumewield then + nodecore.consume_wield(data.crafter, recipe.consumewield) + elseif recipe.toolgroups and recipe.toolwear and data.crafter then + nodecore.wear_wield(data.crafter, recipe.toolgroups, recipe.toolwear) + end + if recipe.after then recipe.after(pos, rel, data) end + if nodecore.player_stat_add then + nodecore.player_stat_add(1, data.crafter, "craft", recipe.label) + end + minetest.log((data.crafter and data.crafter:get_player_name() or "unknown") + .. " completed recipe \"" .. recipe.label .. "\" at " .. + minetest.pos_to_string(pos) .. " upon " .. node.name) + return true +end + +function nodecore.craft_check(pos, node, data) + data = data or {} + local function go(rc, xx, xz, zx, zz) + return craftcheck(rc, pos, node, data, xx, xz, zx, zz) + end + node.x = pos.x + node.y = pos.y + node.z = pos.z + data.node = node + for _, rc in ipairs(nodecore.craft_recipes) do + if nodecore.match(node, rc.root.match) and data.action == rc.action then + if go(rc, 1, 0, 0, 1) then return true end + if not rc.norotate then + if go(rc, 0, -1, 1, 0) then return true end + if go(rc, -1, 0, 0, -1) then return true end + if go(rc, 0, 1, -1, 0) then return true end + if not rc.nomirror then + if go(rc, -1, 0, 0, 1) then return true end + if go(rc, 0, 1, 1, 0) then return true end + if go(rc, 1, 0, 0, -1) then return true end + if go(rc, 0, -1, -1, 0) then return true end + end + end + end + end +end diff --git a/mods/nc_api_craft/depends.txt b/mods/nc_api_craft/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_api_craft/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_api_craft/init.lua b/mods/nc_api_craft/init.lua new file mode 100644 index 00000000..51dc9a5c --- /dev/null +++ b/mods/nc_api_craft/init.lua @@ -0,0 +1,9 @@ +-- LUALOCALS < --------------------------------------------------------- +local include + = include +-- LUALOCALS > --------------------------------------------------------- + +include('register_craft') +include('craft_check') +include('item_place_node') +include('on_punchnode') diff --git a/mods/nc_api_craft/item_place_node.lua b/mods/nc_api_craft/item_place_node.lua new file mode 100644 index 00000000..08f41df6 --- /dev/null +++ b/mods/nc_api_craft/item_place_node.lua @@ -0,0 +1,25 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local old_place = minetest.item_place_node +function minetest.item_place_node(itemstack, placer, pointed_thing, ...) + local old_add = minetest.add_node + minetest.add_node = function(pos, node, ...) + local function helper2(...) + nodecore.craft_check(pos, node, { + action = "place", + crafter = placer, + pointed = pointed_thing + }) + return ... + end + return helper2(old_add(pos, node, ...)) + end + local function helper(...) + minetest.add_node = old_add + return ... + end + return helper(old_place(itemstack, placer, pointed_thing, ...)) +end diff --git a/mods/nc_api_craft/on_punchnode.lua b/mods/nc_api_craft/on_punchnode.lua new file mode 100644 index 00000000..ee45b89c --- /dev/null +++ b/mods/nc_api_craft/on_punchnode.lua @@ -0,0 +1,88 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, vector + = minetest, nodecore, vector +-- LUALOCALS > --------------------------------------------------------- + +local function pummelparticles(data) + local pointed = data.pointed + local nodedef = data.nodedef + local pname = data.pname + + local stack = nodecore.stack_get(data.node) + if stack and not stack:is_empty() then + nodedef = minetest.registered_items[stack:get_name()] or nodedef + end + + local a = pointed.above + local b = pointed.under + local vel = vector.subtract(a, b) + local mid = vector.multiply(vector.add(a, b), 0.5) + local p1 = {x = vel.y, y = vel.z, z = vel.x} + local p2 = {x = vel.z, y = vel.x, z = vel.y} + local s1 = vector.add(vector.add(mid, vector.multiply(p1, 0.5)), vector.multiply(p2, 0.5)) + local s2 = vector.add(vector.add(mid, vector.multiply(p1, -0.5)), vector.multiply(p2, -0.5)) + vel = vector.multiply(vel, 0.5) + + data.clearfx = nodecore.digparticles(nodedef, { + amount = 8, + time = 1.5, + minpos = s1, + maxpos = s2, + minvel = vel, + maxvel = vel, + minexptime = 0.4, + maxexptime = 0.9, + minsize = 1, + maxsize = 5, + playername = pname + }) +end + +local pummeling = {} + +minetest.register_on_punchnode(function(pos, node, puncher, pointed) + if not puncher:is_player() then return end + local pname = puncher:get_player_name() + if not nodecore.interact(pname) then return end + + node = node or minetest.get_node(pos) + local def = minetest.registered_items[node.name] or {} + + local now = minetest.get_us_time() / 1000000 + local pum = { + action = "pummel", + crafter = puncher, + pname = pname, + pos = pos, + pointed = pointed, + node = node, + nodedef = def, + start = now, + wield = puncher:get_wielded_item():to_string(), + count = 0, + inprogress = pummelparticles + } + + local old = pummeling[pname] + if old and old.clearfx then old.clearfx() end + + local hash = minetest.hash_node_position + if old and hash(old.pos) == hash(pum.pos) + and hash(old.pointed.above) == hash(pum.pointed.above) + and hash(old.pointed.under) == hash(pum.pointed.under) + and pum.wield == old.wield + and old.last >= (now - 2) + then pum = old end + + pum.count = pum.count + 1 + pum.last = now + pum.duration = now - pum.start - 1 + pummeling[pname] = pum + + if pum.count < 2 then return end + + if nodecore.craft_check(pos, node, nodecore.underride({}, pum)) then + pummeling[pname] = nil + return + end + end) diff --git a/mods/nc_api_craft/register_craft.lua b/mods/nc_api_craft/register_craft.lua new file mode 100644 index 00000000..9edd1afa --- /dev/null +++ b/mods/nc_api_craft/register_craft.lua @@ -0,0 +1,66 @@ +-- LUALOCALS < --------------------------------------------------------- +local error, math, nodecore, pairs, table, type + = error, math, nodecore, pairs, table, type +local math_floor, table_insert + = math.floor, table.insert +-- LUALOCALS > --------------------------------------------------------- + +local craft_recipes = {} +nodecore.craft_recipes = craft_recipes + +local id = 0 + +function nodecore.register_craft(recipe) + recipe.action = recipe.action or "place" + local canrot + recipe.nodes = recipe.nodes or {} + for _, v in pairs(recipe.nodes) do + v.x = v.x or 0 + v.y = v.y or 0 + v.z = v.z or 0 + canrot = canrot or v.x ~= 0 or v.z ~= 0 + if v.x == 0 and v.y == 0 and v.z == 0 then + recipe.root = v + end + end + if not recipe.root or not recipe.root.match then + error "recipe.nodes must have a match for 0,0,0" + end + if not recipe.label then + id = id + 1 + recipe.label = "unnamed " .. recipe.action .. " " .. id + end + if recipe.toolgroups and recipe.toolwear ~= false then + recipe.toolwear = 1 + end + if not canrot then recipe.norotate = true end + if recipe.normal then + recipe.normal.x = recipe.normal.x or 0 + recipe.normal.y = recipe.normal.y or 0 + recipe.normal.z = recipe.normal.z or 0 + end + if recipe.items then + for k, v in pairs(recipe.items) do + if type(v) == "string" then + recipe.items[k] = {name = v} + end + end + end + if recipe.wield and type(recipe.wield) == "table" then + recipe.wield.count = recipe.wield.count or false + end + local newp = recipe.priority or 0 + + local min = 1 + local max = #craft_recipes + 1 + while max > min do + local try = math_floor((min + max) / 2) + local oldp = craft_recipes[try].priority or 0 + if newp < oldp then + min = try + 1 + else + max = try + end + end + table_insert(craft_recipes, min, recipe) +end diff --git a/mods/nc_fire/abm.lua b/mods/nc_fire/abm.lua new file mode 100644 index 00000000..593158cb --- /dev/null +++ b/mods/nc_fire/abm.lua @@ -0,0 +1,88 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore + = math, minetest, nodecore +local math_pow, math_random + = math.pow, math.random +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local fueltest = {groups = {ember = true}} +nodecore.register_limited_abm({ + label = "Fire Requires Embers", + interval = 1, + chance = 1, + nodenames = {modname .. ":fire"}, + action = function(pos) + if not nodecore.match({x = pos.x, y = pos.y - 1, z = pos.z }, fueltest) + and not nodecore.match({x = pos.x + 1, y = pos.y, z = pos.z }, fueltest) + and not nodecore.match({x = pos.x - 1, y = pos.y, z = pos.z }, fueltest) + and not nodecore.match({x = pos.x, y = pos.y, z = pos.z + 1 }, fueltest) + and not nodecore.match({x = pos.x, y = pos.y, z = pos.z - 1 }, fueltest) + then return minetest.remove_node(pos) end + end + }) + +local function mkfire(pos, dx, dy, dz, testonly) + pos = {x = pos.x + dx, y = pos.y + dy, z = pos.z + dz} + if nodecore.match(pos, modname .. ":fire") then return true end + if nodecore.match(pos, "air") then + return testonly or minetest.set_node(pos, {name = modname .. ":fire"}) + end + if nodecore.match(pos, "ignore") then return true end + if nodecore.match(pos, {groups = {flammable = true, fire_fuel = false}}) then + return testonly or minetest.set_node(pos, {name = modname .. ":fire"}) + end +end + +nodecore.register_limited_abm({ + label = "Flammables Ignite", + interval = 5, + chance = 1, + nodenames = {"group:flammable"}, + neighbors = {"group:igniter"}, + action = function(pos, node) + -- Cannot be wet. + if nodecore.quenched(pos) then return end + + -- Must have oxygen supply. + if not mkfire(pos, 0, 1, 0, true) + and not mkfire(pos, 1, 0, 0, true) + and not mkfire(pos, -1, 0, 0, true) + and not mkfire(pos, 0, 0, 1, true) + and not mkfire(pos, 0, 0, -1, true) + then return end + + -- Get flammability level. + node = node or minetest.get_node(pos) + local def = minetest.registered_nodes[node.name] + local flam = def and def.groups and def.groups.flammable + if not flam then return end + + -- Ignite randomly. + if math_random(1, flam) ~= 1 then return end + nodecore.ignite(pos, node) + end + }) + +nodecore.register_limited_abm({ + label = "Fuel Burning/Snuffing", + interval = 1, + chance = 1, + nodenames = {"group:ember"}, + action = function(pos, node) + if nodecore.quenched(pos) then + return nodecore.snuff(pos, node) + end + local f = mkfire(pos, 0, 1, 0) + f = mkfire(pos, 1, 0, 0) or f + f = mkfire(pos, -1, 0, 0) or f + f = mkfire(pos, 0, 0, 1) or f + f = mkfire(pos, 0, 0, -1) or f + if math_random(1, math_pow(2, + nodecore.node_group("ember", pos, node) + + (f and 4 or 0))) == 1 then + return nodecore.snuff(pos, node) + end + end + }) diff --git a/mods/nc_fire/api.lua b/mods/nc_fire/api.lua new file mode 100644 index 00000000..84f2ed37 --- /dev/null +++ b/mods/nc_fire/api.lua @@ -0,0 +1,35 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore + = math, minetest, nodecore +local math_floor + = math.floor +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +function nodecore.ignite(pos, node) + local fuel = nodecore.node_group("fire_fuel", pos, node) or 0 + if fuel < 0 then fuel = 0 end + if fuel > 6 then fuel = 6 end + fuel = math_floor(fuel) + local stack + if nodecore.node_group("eject_inv_on_burn", pos, node) then + stack = nodecore.stack_get(pos) + if stack and (not stack:is_empty()) then + local p = nodecore.scan_flood(pos, 2, nodecore.buildable_to) + nodecore.item_eject(p or pos, stack, 1) + end + end + minetest.set_node(pos, {name = modname .. ((fuel > 0) + and (":ember" .. fuel) or ":fire")}) + minetest.after(0, function() minetest.check_for_falling(pos) end) +end + +function nodecore.snuff(pos, node, groups) + local ember = nodecore.node_group("ember", pos, node) + if not ember then return end + ember = ember - 1 + minetest.set_node(pos, {name = modname .. ((ember > 0) + and (":ember" .. ember) or ":ash")}) + minetest.after(0, function() minetest.check_for_falling(pos) end) +end diff --git a/mods/nc_fire/depends.txt b/mods/nc_fire/depends.txt new file mode 100644 index 00000000..b47dd05f --- /dev/null +++ b/mods/nc_fire/depends.txt @@ -0,0 +1,2 @@ +nc_api +nc_api_craft \ No newline at end of file diff --git a/mods/nc_fire/firestarting.lua b/mods/nc_fire/firestarting.lua new file mode 100644 index 00000000..10ae100c --- /dev/null +++ b/mods/nc_fire/firestarting.lua @@ -0,0 +1,41 @@ +-- LUALOCALS < --------------------------------------------------------- +local ItemStack, math, minetest, nodecore + = ItemStack, math, minetest, nodecore +local math_random + = math.random +-- LUALOCALS > --------------------------------------------------------- + +nodecore.register_craft({ + label = "stick fire starting", + action = "pummel", + wield = { + groups = {firestick = true} + }, + nodes = { + {match = {groups = {firestick = true}}} + }, + consumewield = 1, + duration = 5, + before = function(pos, rel, data) + local w = data.wield and ItemStack(data.wield):get_name() or "" + local wd = minetest.registered_items[w] or {} + local wg = wd.groups or {} + local fs = wg.firestick or 1 + local nd = minetest.registered_items[data.node.name] or {} + local ng = nd.groups or {} + fs = fs * (ng.firestick or 1) + if math_random(1, 4) > fs then return end + minetest.set_node(pos, {name = "nc_fire:fire"}) + if math_random(1, 4) > fs then return end + local dir = nodecore.pickrand(nodecore.dirs()) + local below = { + x = pos.x + dir.x, + y = pos.y + dir.y, + z = pos.z + dir.z + } + if nodecore.match(below, + {groups = {flammable = 1}}) then + nodecore.ignite(below) + end + end + }) diff --git a/mods/nc_fire/init.lua b/mods/nc_fire/init.lua new file mode 100644 index 00000000..c5625523 --- /dev/null +++ b/mods/nc_fire/init.lua @@ -0,0 +1,12 @@ +-- LUALOCALS < --------------------------------------------------------- +local include, minetest + = include, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local path = minetest.get_modpath(modname) + +include('api') +include('node') +include('abm') +include('firestarting') diff --git a/mods/nc_fire/node.lua b/mods/nc_fire/node.lua new file mode 100644 index 00000000..5161e1e0 --- /dev/null +++ b/mods/nc_fire/node.lua @@ -0,0 +1,64 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest + = minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_node(modname .. ":fire", { + description = "Fire", + drawtype = "firelike", + visual_scale = 1.5, + tiles = {modname .. "_fire.png"}, + paramtype = "light", + light_source = 12, + groups = { + igniter = 1, + flame = 1 + }, + damage_per_second = 2, + sunlight_propagates = true, + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + }) + +local function ember(n, t) + minetest.register_node(modname .. ":ember" .. n, { + description = "Burning Embers", + tiles = {t}, + paramtype = "light", + light_source = 6, + groups = { + igniter = 1, + ember = n, + falling_node = 1 + }, + drop = "", + diggable = false, + on_punch = function(pos, node, puncher, ...) + puncher:set_hp(puncher:get_hp() - 1) + return minetest.node_punch(pos, node, puncher, ...) + end, + crush_damage = 1 + }) +end +ember(1, modname .. "_ash.png^(" .. modname .. "_ember1.png^[opacity:128)") +ember(2, modname .. "_ash.png^" .. modname .. "_ember1.png") +ember(3, modname .. "_ash.png^" .. modname .. "_ember1.png^(" .. modname .. "_ember2.png^[opacity:128)") +ember(4, modname .. "_ash.png^" .. modname .. "_ember2.png") +ember(5, modname .. "_ash.png^" .. modname .. "_ember2.png^(" .. modname .. "_ember3.png^[opacity:128)") +ember(6, modname .. "_ember3.png") +minetest.register_alias(modname .. ":fuel", modname .. ":ember2") + +minetest.register_node(modname .. ":ash", { + description = "Ash", + tiles = {modname .. "_ash.png"}, + groups = { + falling_node = 1, + falling_repose = 1, + crumbly = 1 + }, + crush_damage = 0.25 + }) diff --git a/mods/nc_fire/textures/nc_fire_ash.png b/mods/nc_fire/textures/nc_fire_ash.png new file mode 100644 index 00000000..25a4f425 Binary files /dev/null and b/mods/nc_fire/textures/nc_fire_ash.png differ diff --git a/mods/nc_fire/textures/nc_fire_ember1.png b/mods/nc_fire/textures/nc_fire_ember1.png new file mode 100644 index 00000000..5431d14b Binary files /dev/null and b/mods/nc_fire/textures/nc_fire_ember1.png differ diff --git a/mods/nc_fire/textures/nc_fire_ember2.png b/mods/nc_fire/textures/nc_fire_ember2.png new file mode 100644 index 00000000..d4c25a98 Binary files /dev/null and b/mods/nc_fire/textures/nc_fire_ember2.png differ diff --git a/mods/nc_fire/textures/nc_fire_ember3.png b/mods/nc_fire/textures/nc_fire_ember3.png new file mode 100644 index 00000000..f4244c72 Binary files /dev/null and b/mods/nc_fire/textures/nc_fire_ember3.png differ diff --git a/mods/nc_fire/textures/nc_fire_fire.png b/mods/nc_fire/textures/nc_fire_fire.png new file mode 100644 index 00000000..d29fb756 Binary files /dev/null and b/mods/nc_fire/textures/nc_fire_fire.png differ diff --git a/mods/nc_guide/depends.txt b/mods/nc_guide/depends.txt new file mode 100644 index 00000000..f587141f --- /dev/null +++ b/mods/nc_guide/depends.txt @@ -0,0 +1,2 @@ +nc_stats +nc_player \ No newline at end of file diff --git a/mods/nc_guide/init.lua b/mods/nc_guide/init.lua new file mode 100644 index 00000000..d90d561f --- /dev/null +++ b/mods/nc_guide/init.lua @@ -0,0 +1,263 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, minetest, nodecore, pairs, type + = ipairs, minetest, nodecore, pairs, type +-- LUALOCALS > --------------------------------------------------------- + +local function conv(spec) + if not spec then + return function() return true end + end + if type(spec) == "function" then return spec end + if type(spec) == "table" then + local f = spec[1] + if f == true then + return function(db) + for i = 2, #spec do + if db[spec[i]] then return true end + end + end + end + return function(db) + for i = 2, #spec do + if not db[spec[i]] then return end + end + return true + end + end + return function(db) return db[spec] end +end + +local function addhint(text, goal, reqs) + local hints = nodecore.hints + local h = { + text = text, + goal = conv(goal), + reqs = conv(reqs) + } + hints[#hints + 1] = h + return h +end +nodecore.hints = {} +nodecore.addhint = addhint + +addhint("dug up dirt", + "nc_terrain:dirt_loose") + +addhint("dug up sand", + "nc_terrain:sand_loose") + +addhint("found dry leaves", + "nc_tree:leaves_loose") + +addhint("found eggcorns", + "nc_tree:eggcorn") + +addhint("planted an eggcorn", + "nc_tree:eggcorn_planted", + "nc_tree:eggcorn") + +addhint("found sticks", + "nc_tree:stick") + +addhint("crafted a staff from sticks", + "nc_woodwork:staff", + "nc_tree:stick") + +addhint("crafted an adze out of sticks", + "nc_woodwork:adze", + {true, "nc_tree:stick", "nc_woodwork:staff"}) + +addhint("constructed a wooden ladder", + "nc_woodwork:ladder", + {true, "nc_tree:stick", "nc_woodwork:staff"}) + +addhint("constructed a wooden frame", + "nc_woodwork:frame", + {true, "nc_tree:stick", "nc_woodwork:staff"}) + +addhint("split a tree trunk into planks", + "nc_woodwork:plank", + {true, "nc_woodwork:adze", "nc_woodwork:tool_hatchet"}) + +local woodhead = addhint("carved wooden tool heads from planks", + {true, + "nc_woodwork:toolhead_mallet", + "nc_woodwork:toolhead_spade", + "nc_woodwork:toolhead_hatchet", + "nc_woodwork:toolhead_pick", + }, + "nc_woodwork:plank") + +addhint("assembled a wooden tool", + {true, + "nc_woodwork:tool_mallet", + "nc_woodwork:tool_spade", + "nc_woodwork:tool_hatchet", + "nc_woodwork:tool_pick", + }, + woodhead.goal) + +addhint("made each kinds of wooden tool head", + { + "nc_woodwork:toolhead_mallet", + "nc_woodwork:toolhead_spade", + "nc_woodwork:toolhead_hatchet", + "nc_woodwork:toolhead_pick", + }, + woodhead.goal) + +addhint("cut down a tree", + "nc_tree:tree", + "nc_woodwork:tool_hatchet") + +addhint("dug up stone", + "nc_terrain:cobble_loose", + "nc_woodwork:tool_pick") + +addhint("broken cobble into chips", + "nc_stonework:chip", + "nc_terrain:cobble_loose") + +addhint("put a stone tip onto a tool", + {true, + "nc_stonework:tool_mallet", + "nc_stonework:tool_spade", + "nc_stonework:tool_hatchet", + "nc_stonework:tool_pick" + }, + "nc_stonework:chip") + +local embers = addhint("made fire by rubbing sticks together", + {true, + "nc_fire:ember1", + "nc_fire:ember2", + "nc_fire:ember3", + "nc_fire:ember4", + "nc_fire:ember5", + "nc_fire:ember6", + "nc_fire:ash", + }, + "nc_tree:stick") + +addhint("set fire to long-lasting fuels", + {true, + "nc_fire:ember5", + "nc_fire:ember6", + "nc_fire:ash", + }, + embers.goal) + +addhint("found ash", + "nc_fire:ash", + embers.goal) + +local lodefind = addhint("found a lode deposit", + {true, + "nc_lode:stone", + "nc_lode:ore" + }) + +local lodeore = addhint("found lode ore", + "nc_lode:ore", + lodefind.goal) + +addhint("dug out lode ore", + "nc_lode:cobble_loose", + lodeore.goal) + +local lodesmelt = addhint("melted down lode metal", + {true, + "nc_lode:prill_hot", + "nc_lode:prill_annealed", + "nc_lode:prill_tempered" + }, + "nc_lode:cobble_loose") + +local anvil = addhint("constructed a lode anvil", + "nc_lode:block_tempered", + lodesmelt.goal) + +local annealhead = addhint("cold-forged a lode tool head", + {true, + "nc_lode:toolhead_mallet_annealed", + "nc_lode:toolhead_spade_annealed", + "nc_lode:toolhead_hatchet_annealed", + "nc_lode:toolhead_pick_annealed" + }, + anvil.goal) + +addhint("tempered a lode tool head", + {true, + "nc_lode:toolhead_mallet_tempered", + "nc_lode:toolhead_spade_tempered", + "nc_lode:toolhead_hatchet_tempered", + "nc_lode:toolhead_pick_tempered" + }, + annealhead.goal) + +addhint("constructed a shelf", + "nc_woodwork:shelf", + { "nc_woodwork:frame", "nc_woodwork:plank" }) + +addhint("found sponges", + { "nc_sponge:sponge", "nc_sponge:sponge_wet", "nc_sponge:sponge_living" }) + +local function sethint(player) + local pname = player:get_player_name() + + local rawdb = nodecore.statsdb[pname] or {} + local db = {} + for _, r in ipairs({"inv", "punch", "dig", "place"}) do + for k, v in pairs(rawdb[r] or {}) do + db[k] = v + end + end + + local done = 0 + local found = {} + for _, hint in ipairs(nodecore.hints) do + if hint.goal(db) then + done = done + 1 + elseif hint.reqs(db) then + found[#found + 1] = hint.text + end + end + local hint = nodecore.pickrand(found) + hint = hint and (hint .. " yet") or "checked for a new version recently" + + local prog = #found + local left = #(nodecore.hints) - prog - done + local stats = left .. ":" .. prog .. ":" .. done + + return player:set_inventory_formspec(nodecore.inventory_formspec + .. "label[0,3.1;" + .. minetest.formspec_escape("...have you " .. hint .. "...?") + .. "]label[7.1,3.1;" + .. minetest.formspec_escape(stats) + .. "]" + ) +end + +minetest.register_on_joinplayer(function(player) + minetest.after(0, function() sethint(player) end) + end) + +local seen + +minetest.register_globalstep(function() + if not seen then return end + for _, player in pairs(minetest.get_connected_players()) do + local n = player:get_player_name() + if not seen[n] then + seen[n] = true + return sethint(player) + end + end + seen = nil + end) + +local function pump() + minetest.after(20, pump) + seen = seen or {} +end +pump() diff --git a/mods/nc_hand/assetsrc/crack_anylength.svg b/mods/nc_hand/assetsrc/crack_anylength.svg new file mode 100644 index 00000000..7f253354 --- /dev/null +++ b/mods/nc_hand/assetsrc/crack_anylength.svg @@ -0,0 +1,789 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/nc_hand/assetsrc/nc_hand.svg b/mods/nc_hand/assetsrc/nc_hand.svg new file mode 100644 index 00000000..1bd8bdbf --- /dev/null +++ b/mods/nc_hand/assetsrc/nc_hand.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/mods/nc_hand/depends.txt b/mods/nc_hand/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_hand/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_hand/init.lua b/mods/nc_hand/init.lua new file mode 100644 index 00000000..03d9c424 --- /dev/null +++ b/mods/nc_hand/init.lua @@ -0,0 +1,16 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, type + = minetest, nodecore, type +-- LUALOCALS > --------------------------------------------------------- + +minetest.register_item(":", { + type = "none", + wield_image = "nc_hand.png", + wield_scale = {x=1, y=1, z=2.5}, + tool_capabilities = nodecore.toolcaps({ + uses = 0, + crumbly = 1, + snappy = 1, + thumpy = 1 + }) + }) diff --git a/mods/nc_hand/textures/crack_anylength.png b/mods/nc_hand/textures/crack_anylength.png new file mode 100644 index 00000000..ac48f1db Binary files /dev/null and b/mods/nc_hand/textures/crack_anylength.png differ diff --git a/mods/nc_hand/textures/nc_hand.png b/mods/nc_hand/textures/nc_hand.png new file mode 100644 index 00000000..e00292bd Binary files /dev/null and b/mods/nc_hand/textures/nc_hand.png differ diff --git a/mods/nc_health/depends.txt b/mods/nc_health/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_health/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_health/init.lua b/mods/nc_health/init.lua new file mode 100644 index 00000000..3c57383e --- /dev/null +++ b/mods/nc_health/init.lua @@ -0,0 +1,120 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore, pairs, vector + = math, minetest, nodecore, pairs, vector +local math_pow, math_random, math_sqrt + = math.pow, math.random, math.sqrt +-- LUALOCALS > --------------------------------------------------------- + +local cache = {} + +local function envcheck(player) + local stats = {} + + local pos = player:getpos() + pos.y = pos.y + 1.6 + + stats.light = minetest.get_node_light(pos) + + local target = vector.add(pos, { + x = math_random() * 128 - 64, + y = math_random() * 128 - 64, + z = math_random() * 128 - 64 + }) + local _, hit = minetest.line_of_sight(pos, target) + hit = hit or target + + stats.space = vector.distance(pos, hit) + + local node = minetest.get_node(hit) + local def = minetest.registered_items[node.name] + local groups = def.groups or {} + + stats.green = groups.green or 0 + stats.water = groups.water or 0 + stats.green / 5 + + local pname = player:get_player_name() + local agg = cache[pname] + if not agg then + agg = {} + local raw = player:get_attribute("healthenv") + if raw and raw ~= "" then + agg = minetest.deserialize(raw) + end + cache[pname] = agg + end + for k, v in pairs(stats) do + agg[k] = ((agg[k] or 0) * 99 + v) / 100 + end + agg.dirty = (agg.dirty or 0) + 1 + if agg.dirty >= 5 then + agg.dirty = nil + player:set_attribute("healthenv", + minetest.serialize(agg)) + nodecore.addphealth(player, 0.005 + (agg.green + 0.02) + * (agg.water + 0.1) + * (agg.space + 5) + * (agg.light + 8) / 500) + end + +end + +local function setspeed(player, speed) + if speed > 1 then speed = 1 end + if speed < 0 then speed = 0 end + local phys = player:get_physics_override() + if phys.speed == speed then return end + phys.speed = speed + return player:set_physics_override(phys) +end + +local function mobility(player) + local health = nodecore.getphealth(player) / 20 + if health >= 1 then return setspeed(player, 1) end + + local inv = player:get_inventory() + local encumb = 0 + local invsize = inv:get_size("main") + for i = 1, invsize do + if not inv:get_stack("main", i):is_empty() then + encumb = encumb + 1 + end + end + encumb = encumb / invsize + if encumb <= health then return setspeed(player, 1) end + + return setspeed(player, math_pow(1 - math_sqrt(encumb - health) * 0.8, + math_random() * 2 + 1)) +end + +local t = 0 +minetest.register_globalstep(function(dt) + t = t + dt + while t > 0.5 do + t = t - 0.5 + for _, player in pairs(minetest.get_connected_players()) do + if player:get_hp() > 0 then + envcheck(player) + mobility(player) + end + end + end + end) + +minetest.register_on_dieplayer(function(player) + local inv = player:get_inventory() + local pos = player:getpos() + for i = 1, inv:get_size("main") do + nodecore.item_eject(pos, inv:get_stack("main", i), 10) + end + inv:set_list("main", {}) + + -- flush attributes + player:set_attribute("healthenv", "") + cache[player:get_player_name()] = nil + player:set_attribute("dhp", "0") + end) + +minetest.register_on_respawnplayer(function(player) + nodecore.setphealth(player, 0.0001) + mobility(player) + end) diff --git a/mods/nc_hud/assetsrc/huds.svg b/mods/nc_hud/assetsrc/huds.svg new file mode 100644 index 00000000..18a12907 --- /dev/null +++ b/mods/nc_hud/assetsrc/huds.svg @@ -0,0 +1,435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/nc_hud/depends.txt b/mods/nc_hud/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_hud/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_hud/init.lua b/mods/nc_hud/init.lua new file mode 100644 index 00000000..ffbcffaf --- /dev/null +++ b/mods/nc_hud/init.lua @@ -0,0 +1,47 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest + = minetest +-- LUALOCALS > --------------------------------------------------------- + +local health_bar_definition = +{ + hud_elem_type = "statbar", + position = {x = 0.5, y = 1}, + text = "heart_bg.png", + number = 20, + direction = 0, + size = {x = 24, y = 24}, + offset = { x = (-10 * 24) - 25, y = -(48 + 24 + 16)}, +} + +local breath_bar_definition = +{ + hud_elem_type = "statbar", + position = {x = 0.5, y = 1}, + text = "bubble_bg.png", + number = 20, + direction = 0, + size = {x = 24, y = 24}, + offset = {x = 25, y = -(48 + 24 + 16)}, +} + +local reg_bubbles = {} + +local function checkbubbles(player) + local name = player:get_player_name() + if player:get_breath() < 11 then + reg_bubbles[name] = reg_bubbles[name] or player:hud_add(breath_bar_definition) + elseif reg_bubbles[name] then + player:hud_remove(reg_bubbles[name]) + reg_bubbles[name] = nil + end +end + +minetest.register_playerevent(checkbubbles) + +minetest.register_on_joinplayer(function(player) + minetest.after(0, function() + player:hud_add(health_bar_definition) + checkbubbles(player) + end) + end) diff --git a/mods/nc_hud/textures/bubble.png b/mods/nc_hud/textures/bubble.png new file mode 100644 index 00000000..bdcdf921 Binary files /dev/null and b/mods/nc_hud/textures/bubble.png differ diff --git a/mods/nc_hud/textures/bubble_bg.png b/mods/nc_hud/textures/bubble_bg.png new file mode 100644 index 00000000..8eb6495d Binary files /dev/null and b/mods/nc_hud/textures/bubble_bg.png differ diff --git a/mods/nc_hud/textures/heart.png b/mods/nc_hud/textures/heart.png new file mode 100644 index 00000000..eb1c25f0 Binary files /dev/null and b/mods/nc_hud/textures/heart.png differ diff --git a/mods/nc_hud/textures/heart_bg.png b/mods/nc_hud/textures/heart_bg.png new file mode 100644 index 00000000..2d5fd621 Binary files /dev/null and b/mods/nc_hud/textures/heart_bg.png differ diff --git a/mods/nc_hud/textures/nc_hud_bg.png b/mods/nc_hud/textures/nc_hud_bg.png new file mode 100644 index 00000000..905a7869 Binary files /dev/null and b/mods/nc_hud/textures/nc_hud_bg.png differ diff --git a/mods/nc_hud/textures/nc_hud_sel.png b/mods/nc_hud/textures/nc_hud_sel.png new file mode 100644 index 00000000..58849e69 Binary files /dev/null and b/mods/nc_hud/textures/nc_hud_sel.png differ diff --git a/mods/nc_items/depends.txt b/mods/nc_items/depends.txt new file mode 100644 index 00000000..0c9f0fb2 --- /dev/null +++ b/mods/nc_items/depends.txt @@ -0,0 +1,3 @@ +nc_api +nc_api_craft +nc_fire? \ No newline at end of file diff --git a/mods/nc_items/init.lua b/mods/nc_items/init.lua new file mode 100644 index 00000000..acc3f014 --- /dev/null +++ b/mods/nc_items/init.lua @@ -0,0 +1,179 @@ +-- LUALOCALS < --------------------------------------------------------- +local ItemStack, math, minetest, nodecore, setmetatable, type, vector + = ItemStack, math, minetest, nodecore, setmetatable, type, vector +local math_random + = math.random +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local stackbox = nodecore.fixedbox(-0.4, -0.5, -0.4, 0.4, 0.3, 0.4) + +local function invdef(pos) + local stack = nodecore.stack_get(pos) + if not stack or stack:is_empty() then return end + local def = minetest.registered_items[stack:get_name()] + return stack:get_count() == (def.pummel_stack or 1) and def or nil +end + +minetest.register_node(modname .. ":stack", { + drawtype = "nodebox", + node_box = nodecore.fixedbox( + {-0.5, -0.5, -0.5, 0.5, -7/16, 0.5} + ), + use_texture_alpha = true, + tiles = { + "nc_items_shadow.png", + "nc_items_blank.png", + }, + walkable = true, + selection_box = stackbox, + collision_box = stackbox, + drop = {}, + groups = { + snappy = 1, + falling_repose = 1, + visinv = 1, + is_stack_only = 1 + }, + paramtype = "light", + sunlight_propagates = true, + repose_drop = function(posfrom, posto, node) + local stack = nodecore.stack_get(posfrom) + if stack and not stack:is_empty() then + nodecore.item_eject(posto, stack) + end + return minetest.remove_node(posfrom) + end, + on_rightclick = function(pos, node, whom, stack, pointed, ...) + if not nodecore.interact(whom) then return stack end + return nodecore.stack_add(pos, stack) + end + }) + +function nodecore.place_stack(pos, stack, placer, pointed_thing) + stack = ItemStack(stack) + local name = stack:get_name() + + local below = {x = pos.x, y = pos.y - 1, z = pos.z} + if nodecore.match(below, {name = name, count = false}) then + stack = nodecore.stack_add(below, stack) + if stack:is_empty() then return end + end + + minetest.set_node(pos, {name = modname .. ":stack"}) + nodecore.stack_set(pos, stack) + if placer and pointed_thing then + nodecore.craft_check(pos, {name = stack:get_name()}, { + action = "place", + crafter = placer, + pointed = pointed_thing + }) + end + + return minetest.check_for_falling(pos) +end + +local bii = minetest.registered_entities["__builtin:item"] +local item = { + on_step = function(self, dtime, ...) + bii.on_step(self, dtime, ...) + + local pos = self.object:getpos() + if not self.oldpos or not vector.equals(pos, self.oldpos) then + self.oldpos = pos + self.sitting = 0 + return + end + self.sitting = (self.sitting or 0) + dtime + if self.sitting < 0.25 then return end + + pos = vector.round(pos) + local i = ItemStack(self.itemstring) + pos = nodecore.scan_flood(pos, 5, + function(p) + if p.y > pos.y then return end + i = nodecore.stack_add(p, i) + if i:is_empty() then return p end + if nodecore.buildable_to(p) then return p end + end) + if not pos then return end + if not i:is_empty() then nodecore.place_stack(pos, i) end + self.itemstring = "" + self.object:remove() + end, + on_punch = function(self, whom, ...) + if not nodecore.interact(whom) then return end + local r = bii.on_punch(self, whom, ...) + if self.itemstring ~= "" then + local v = self.object:get_velocity() + v.x = v.x + math_random() * 5 - 2.5 + v.y = v.y + math_random() * 5 - 2.5 + v.z = v.z + math_random() * 5 - 2.5 + self.object:set_velocity(v) + end + end +} +setmetatable(item, bii) +minetest.register_entity(":__builtin:item", item) + +local bifn = minetest.registered_entities["__builtin:falling_node"] +local falling = { + set_node = function(self, node, meta, ...) + if node and node.name == modname .. ":stack" + and meta and meta.inventory and meta.inventory.solo then + local stack = ItemStack(meta.inventory.solo[1] or "") + if not stack:is_empty() then + nodecore.item_eject(self.object:getpos(), stack, + nil, nil, {x = 0, y = 0.01, z = 0}) + return self.object:remove() + end + end + return bifn.set_node(self, node, meta, ...) + end +} +setmetatable(falling, bifn) +minetest.register_entity(":__builtin:falling_node", falling) + +function minetest.item_place(itemstack, placer, pointed_thing, param2) + if not nodecore.interact(placer) then return end + if pointed_thing.type == "node" and placer and + not placer:get_player_control().sneak then + local n = minetest.get_node(pointed_thing.under) + local nn = n.name + if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then + return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, n, + placer, itemstack, pointed_thing) or itemstack, false + end + end + if itemstack:get_definition().type == "node" then + return minetest.item_place_node(itemstack, placer, pointed_thing, param2) + end + if not itemstack:is_empty() then + local above = minetest.get_pointed_thing_position(pointed_thing, true) + if above and nodecore.buildable_to(above) then + nodecore.place_stack(above, itemstack:take_item(), placer, pointed_thing) + end + end + return itemstack +end + +if nodecore.loaded_mods().nc_fire then + nodecore.register_limited_abm({ + label = "Flammable ItemStacks Ignite", + interval = 5, + chance = 1, + nodenames = {modname .. ":stack"}, + neighbors = {"group:igniter"}, + action = function(pos, node) + if nodecore.quenched(pos) then return end + + local def = invdef(pos) + local flam = def and def.groups and def.groups.flammable + if not flam then return end + + if math_random(1, flam) ~= 1 then return end + nodecore.ignite(pos, node) + end + }) +end diff --git a/mods/nc_items/textures/nc_items_blank.png b/mods/nc_items/textures/nc_items_blank.png new file mode 100644 index 00000000..ada6be59 Binary files /dev/null and b/mods/nc_items/textures/nc_items_blank.png differ diff --git a/mods/nc_items/textures/nc_items_shadow.png b/mods/nc_items/textures/nc_items_shadow.png new file mode 100644 index 00000000..ce0898b9 Binary files /dev/null and b/mods/nc_items/textures/nc_items_shadow.png differ diff --git a/mods/nc_lode/depends.txt b/mods/nc_lode/depends.txt new file mode 100644 index 00000000..932c9f25 --- /dev/null +++ b/mods/nc_lode/depends.txt @@ -0,0 +1,3 @@ +nc_api +nc_items +nc_terrain \ No newline at end of file diff --git a/mods/nc_lode/init.lua b/mods/nc_lode/init.lua new file mode 100644 index 00000000..b5360a42 --- /dev/null +++ b/mods/nc_lode/init.lua @@ -0,0 +1,11 @@ +-- LUALOCALS < --------------------------------------------------------- +local include, minetest + = include, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local path = minetest.get_modpath(modname) + +include("ore") +include("metallurgy") +include("tools") diff --git a/mods/nc_lode/metallurgy.lua b/mods/nc_lode/metallurgy.lua new file mode 100644 index 00000000..8f90c382 --- /dev/null +++ b/mods/nc_lode/metallurgy.lua @@ -0,0 +1,165 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, pairs, type + = minetest, nodecore, pairs, type +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +function nodecore.register_lode(shape, rawdef) + for _, temper in pairs({"Hot", "Annealed", "Tempered"}) do + local def = nodecore.underride({}, rawdef) + def = nodecore.underride(def, { + description = temper .. " Lode " .. shape, + name = (shape .. "_" .. temper):lower():gsub(" ", "_"), + groups = { cracky = 3 }, + ["metal_temper_" .. temper:lower()] = true, + metal_alt_hot = modname .. ":" .. shape:lower() .. "_hot", + metal_alt_annealed = modname .. ":" .. shape:lower() .. "_annealed", + metal_alt_tempered = modname .. ":" .. shape:lower() .. "_tempered", + }) + if temper ~= "Hot" then + def.light_source = nil + else + def.groups = def.groups or {} + def.groups.falling_node = 1 + def.damage_per_second = 2 + end + + if def.tiles then + local t = {} + for k, v in pairs(def.tiles) do + t[k] = v:gsub("#", temper:lower()) + end + def.tiles = t + end + for k, v in pairs(def) do + if type(v) == "string" then + def[k] = v:gsub("##", temper):gsub("#", temper:lower()) + end + end + + if def.bytemper then def.bytemper(temper, def) end + + minetest.register_item(modname .. ":" .. def.name, def) + end +end + +nodecore.register_lode("Block", { + type = "node", + tiles = { modname .. "_#.png" }, + light_source = 8, + crush_damage = 4 + }) + +nodecore.register_lode("Prill", { + type = "craft", + inventory_image = modname .. "_#.png^[mask:" .. modname .. "_mask_prill.png", + }) + +local flame = {groups = {flame = true}} +local function heated(pos) + if nodecore.quenched(pos) then return end + local f = 0 + if nodecore.match({x = pos.x, y = pos.y - 1, z = pos.z}, flame) + then f = f + 1 end + if nodecore.match({x = pos.x + 1, y = pos.y, z = pos.z}, flame) + then f = f + 1 end + if nodecore.match({x = pos.x - 1, y = pos.y, z = pos.z}, flame) + then f = f + 1 end + if f >= 3 then return true end + if nodecore.match({x = pos.x, y = pos.y, z = pos.z + 1}, flame) + then f = f + 1 end + if f >= 3 then return true end + if nodecore.match({x = pos.x, y = pos.y, z = pos.z - 1}, flame) + then f = f + 1 end + if f >= 3 then return true end +end + +local function timecounter(meta, max, check) + if not check then + local t = meta:to_table() + t.fields.time = nil + meta:from_table(t) + return + end + local t = (meta:get_int("time") or 0) + 1 + if t >= max then return true end + meta:set_int("time", t) +end + +nodecore.register_limited_abm({ + label = "Lode Cobble to Prills", + interval = 1, + chance = 1, + nodenames = {modname .. ":cobble"}, + neighbors = {"group:flame"}, + action = function(pos, node) + local below = {x = pos.x, y = pos.y - 1, z = pos.z} + if timecounter(minetest:get_meta(pos), 30, + not nodecore.match(below, {walkable = true}) and heated(pos)) then + nodecore.item_eject(below, modname .. ":prill_hot 2") + minetest:get_meta(pos):from_table({}) + return nodecore.set_node(pos, {name = "nc_terrain:cobble"}) + end + end}) + +local function replacestack(pos, name, stack) + nodecore.remove_node(pos) + return nodecore.item_eject(pos, name .. " " .. stack:get_count()) +end + +nodecore.register_limited_abm({ + label = "Lode Stack Heating/Cooling", + interval = 1, + chance = 1, + nodenames = {"group:visinv"}, + action = function(pos, node) + local stack = nodecore.stack_get(pos) + if stack:is_empty() then return end + local def = minetest.registered_items[stack:get_name()] + if not def then return end + if def.metal_temper_hot then + if nodecore.quenched(pos) then + return replacestack(pos, def.metal_alt_tempered, stack) + end + if timecounter(stack:get_meta(), 120, not heated(pos)) then + return replacestack(pos, def.metal_alt_annealed, stack) + end + elseif (def.metal_temper_annealed or def.metal_temper_tempered) + and timecounter(stack:get_meta(), 30, heated(pos)) then + return replacestack(pos, def.metal_alt_hot, stack) + end + return nodecore.stack_set(pos, stack) + end}) + +-- Because of how massive they are, forging a block is a hot-working process. +nodecore.register_craft({ + label = "forge lode block", + action = "pummel", + toolgroups = {thumpy = 3}, + nodes = { + { + match = {name = modname .. ":prill_hot", count = 8}, + replace = "air" + } + }, + items = { + modname .. ":block_hot" + } + }) + +-- Blocks can be chopped back into prills using only hardened tools. +nodecore.register_craft({ + label = "break apart lode block", + action = "pummel", + toolgroups = {choppy = 5}, + nodes = { + { + match = modname .. ":block_annealed", + replace = "air" + } + }, + items = { + {name = modname .. ":prill_annealed", count = 8, scatter = 5} + } + }) diff --git a/mods/nc_lode/ore.lua b/mods/nc_lode/ore.lua new file mode 100644 index 00000000..d42de0d7 --- /dev/null +++ b/mods/nc_lode/ore.lua @@ -0,0 +1,133 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local function reg(suff, def) + def = nodecore.underride(def, { + description = "Lode " .. suff, + name = suff:lower(), + is_ground_content = true, + groups = { cracky = 2 } + }) + def.fullname = modname .. ":" .. def.name + def.oldnames = {"nc_iron:" .. def.name} + + minetest.register_node(def.fullname, def) + + return def.fullname +end + +local stratstone = {} +local stratore = {} +local stone = reg("Stone", { + tiles = { "nc_terrain_stone.png^(" .. modname .. "_ore.png^[mask:" + .. modname .. "_mask_ore.png^[opacity:48)" }, + drop_in_place = "nc_terrain:cobble", + strata = stratstone + }) +stratstone[1] = stone +local ore = reg("Ore", { + tiles = { "nc_terrain_stone.png^(" .. modname .. "_ore.png^[mask:" + .. modname .. "_mask_ore.png)" }, + drop_in_place = modname .. ":cobble", + strata = stratore + }) +stratore[1] = ore +for i = 1, nodecore.hard_stone_strata do + local hst = nodecore.hard_stone_tile(i) + stratstone[i + 1] = reg("Stone_" .. i, { + tiles = { hst .. "^(" .. modname .. "_ore.png^[mask:" + .. modname .. "_mask_ore.png^[opacity:48)" }, + drop_in_place = "nc_terrain:cobble", + strata = stratstone, + groups = {cracky = i + 2} + }) + + stratore[i + 1] = reg("Ore_" .. i, { + tiles = { hst .. "^(" .. modname .. "_ore.png^[mask:" + .. modname .. "_mask_ore.png)" }, + drop_in_place = modname .. ":cobble", + strata = stratore, + groups = {cracky = i + 2} + }) +end + +reg("Cobble", { + tiles = { modname .. "_ore.png^nc_terrain_cobble.png" }, + alternate_loose = { + repack_level = 2, + groups = { + cracky = 0, + crumbly = 2, + falling_repose = 3 + } + } + }) + +local function regore(name, def) + return minetest.register_ore(nodecore.underride(def, { + name = name, + ore_type = "scatter", + ore = name, + wherein = "nc_terrain:stone", + random_factor = 0, + noise_params = { + offset = 0, + scale = 4, + spread = {x=40, y=5, z=40}, + seed = 12497, + octaves = 3, + persist = 0.5, + flags = "eased", + }, + noise_threshold = 1.3 + }, def)) +end +regore(ore, { + clust_num_ores = 16, + clust_size = 3, + clust_scarcity = 8 * 8 * 8, + }) +regore(stone, { + clust_num_ores = 4, + clust_size = 3, + clust_scarcity = 2 * 2 * 2, + }) + +local c_ore = minetest.get_content_id(ore) +local c_istone = minetest.get_content_id(stone) +local c_stone = minetest.get_content_id("nc_terrain:stone") + +nodecore.register_mapgen_shared(function(minp, maxp, area, data, vm, emin, emax) + local function bad(x, y, z) + local c = data[area:index(x, y, z)] + return c ~= c_stone and c ~= c_istone + end + + for z = minp.z, maxp.z do + for y = minp.y, maxp.y do + for x = minp.x, maxp.x do + local i = area:index(x, y, z) + if data[i] == c_ore then + if x == minp.x + or x == maxp.x + or y == minp.y + or y == maxp.y + or z == minp.z + or z == maxp.z + or bad(x + 1, y, z) + or bad(x - 1, y, z) + or bad(x, y + 1, z) + or bad(x, y - 1, z) + or bad(x, y, z + 1) + or bad(x, y, z - 1) + then data[i] = c_istone + end + end + end + end + end + end) diff --git a/mods/nc_lode/textures/nc_lode_annealed.png b/mods/nc_lode/textures/nc_lode_annealed.png new file mode 100644 index 00000000..20c2f1be Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_annealed.png differ diff --git a/mods/nc_lode/textures/nc_lode_hot.png b/mods/nc_lode/textures/nc_lode_hot.png new file mode 100644 index 00000000..a66260da Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_hot.png differ diff --git a/mods/nc_lode/textures/nc_lode_mask_ore.png b/mods/nc_lode/textures/nc_lode_mask_ore.png new file mode 100644 index 00000000..dcb08905 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_mask_ore.png differ diff --git a/mods/nc_lode/textures/nc_lode_mask_prill.png b/mods/nc_lode/textures/nc_lode_mask_prill.png new file mode 100644 index 00000000..0a9f551b Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_mask_prill.png differ diff --git a/mods/nc_lode/textures/nc_lode_ore.png b/mods/nc_lode/textures/nc_lode_ore.png new file mode 100644 index 00000000..4a608df6 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_ore.png differ diff --git a/mods/nc_lode/textures/nc_lode_tempered.png b/mods/nc_lode/textures/nc_lode_tempered.png new file mode 100644 index 00000000..2cd1292a Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_tempered.png differ diff --git a/mods/nc_lode/textures/nc_lode_tool_handle.png b/mods/nc_lode/textures/nc_lode_tool_handle.png new file mode 100644 index 00000000..75eb8495 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_tool_handle.png differ diff --git a/mods/nc_lode/textures/nc_lode_tool_hatchet.png b/mods/nc_lode/textures/nc_lode_tool_hatchet.png new file mode 100644 index 00000000..2eecd1e8 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_tool_hatchet.png differ diff --git a/mods/nc_lode/textures/nc_lode_tool_mallet.png b/mods/nc_lode/textures/nc_lode_tool_mallet.png new file mode 100644 index 00000000..adb3d300 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_tool_mallet.png differ diff --git a/mods/nc_lode/textures/nc_lode_tool_pick.png b/mods/nc_lode/textures/nc_lode_tool_pick.png new file mode 100644 index 00000000..d1c5aaf8 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_tool_pick.png differ diff --git a/mods/nc_lode/textures/nc_lode_tool_spade.png b/mods/nc_lode/textures/nc_lode_tool_spade.png new file mode 100644 index 00000000..376d53f8 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_tool_spade.png differ diff --git a/mods/nc_lode/textures/nc_lode_toolhead_hatchet.png b/mods/nc_lode/textures/nc_lode_toolhead_hatchet.png new file mode 100644 index 00000000..1dc6cf24 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_toolhead_hatchet.png differ diff --git a/mods/nc_lode/textures/nc_lode_toolhead_mallet.png b/mods/nc_lode/textures/nc_lode_toolhead_mallet.png new file mode 100644 index 00000000..9e815005 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_toolhead_mallet.png differ diff --git a/mods/nc_lode/textures/nc_lode_toolhead_pick.png b/mods/nc_lode/textures/nc_lode_toolhead_pick.png new file mode 100644 index 00000000..c5dde193 Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_toolhead_pick.png differ diff --git a/mods/nc_lode/textures/nc_lode_toolhead_spade.png b/mods/nc_lode/textures/nc_lode_toolhead_spade.png new file mode 100644 index 00000000..9baa940e Binary files /dev/null and b/mods/nc_lode/textures/nc_lode_toolhead_spade.png differ diff --git a/mods/nc_lode/tools.lua b/mods/nc_lode/tools.lua new file mode 100644 index 00000000..2f951ac3 --- /dev/null +++ b/mods/nc_lode/tools.lua @@ -0,0 +1,89 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, pairs, type + = minetest, nodecore, pairs, type +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local function toolhead(name, group) + local n = name:lower() + + nodecore.register_lode("toolhead_" .. n, { + type = "craft", + description = "## Lode " .. name .. " Head", + inventory_image = modname .. "_#.png^[mask:" .. + modname .. "_toolhead_" .. n .. ".png", + stack_max = 1 + }) + + nodecore.register_lode("tool_" .. n, { + type = "tool", + description = "## Lode " .. name, + inventory_image = modname .. "_tool_handle.png^(" .. + modname .. "_#.png^[mask:" .. + modname .. "_tool_" .. n .. ".png)", + stack_max = 1, + tool_capabilities = nodecore.toolcaps({ + [group] = 4 + }), + bytemper = function(t, d) + if t == "Tempered" then + d.tool_capabilities = nodecore.toolcaps({ + [group] = 5 + }) + end + end, + metal_alt_hot = modname .. ":toolhead_" .. n .. "_hot", + tool_wears_to = modname .. ":prill_# 3", + + }) + + for _, t in pairs({"annealed", "tempered"}) do + nodecore.register_craft({ + label = "assemble lode " .. n, + normal = {y = 1}, + nodes = { + {match = modname .. ":toolhead_" .. n .. "_" .. t, + replace = "air"}, + {y = -1, match = "nc_woodwork:staff", replace = "air"}, + }, + items = { + {y = -1, name = modname .. ":tool_" .. n .. "_" .. t}, + } + }) + end +end + +toolhead("Mallet", "thumpy") +toolhead("Spade", "crumbly") +toolhead("Hatchet", "choppy") +toolhead("Pick", "cracky") + +local function forge(from, fromqty, to, prills) + return nodecore.register_craft({ + label = "anvil making lode " .. (to or "prills"), + action = "pummel", + toolgroups = {thumpy = 3}, + nodes = { + { + match = {name = modname .. ":" .. from .. "_annealed", + count = fromqty}, + replace = "air" + }, + { + y = -1, + match = modname .. ":block_tempered" + } + }, + items = { + to and (modname .. ":" .. to .. "_annealed") or nil, + prills and {name = modname .. ":prill_annealed", count = prills, + scatter = 5} or nil + } + }) +end +forge("prill", 3, "toolhead_mallet") +forge("toolhead_mallet", nil, "toolhead_spade", 1) +forge("toolhead_spade", nil, "toolhead_hatchet") +forge("toolhead_hatchet", nil, "toolhead_pick", 1) +forge("toolhead_pick", nil, nil, 1) diff --git a/mods/nc_namehuds/depends.txt b/mods/nc_namehuds/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_namehuds/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_namehuds/init.lua b/mods/nc_namehuds/init.lua new file mode 100644 index 00000000..1efe738a --- /dev/null +++ b/mods/nc_namehuds/init.lua @@ -0,0 +1,225 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore, pairs, string, tonumber + = math, minetest, nodecore, pairs, string, tonumber +local math_sqrt, string_format + = math.sqrt, string.format +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +-- Maximum distance at which custom nametags are visible. +local distance = tonumber(minetest.setting_get(modname .. "_distance")) or 8 + +-- Precision (number of steps) for line-of-sight check for displaying nametags +local precision = tonumber(minetest.setting_get(modname .. "_precision")) or 50 + +-- Keep track of active player HUDs. +local huds = {} + +------------------------------------------------------------------------ +-- PLAYER JOIN/LEAVE + +-- On player joining, disable the built-in nametag by setting its +-- text to whitespace and color to transparent. +minetest.register_on_joinplayer(function(player) + player:set_nametag_attributes({ + text = " ", + color = {a = 0, r = 0, g = 0, b = 0} + }) + end) + +-- On player leaving, clean up any associated HUDs. +minetest.register_on_leaveplayer(function(player) + -- Garbage-collect player's own HUDs. + local pn = player:get_player_name() + huds[pn] = nil + + -- Remove HUDs for this player's name + -- from other players + for k, v in pairs(huds) do + local i = v[pn] + if i then + i.o:hud_remove(i.i) + v[pn] = nil + end + end + end) + +------------------------------------------------------------------------ +-- GLOBAL TICK HUD MANAGEMENT + +-- Vague health descriptions +local health = { + "critically injured", + "critically injured", + "heavily injured", + "heavily injured", + "heavily injured", + "heavily injured", + "injured", + "injured", + "injured", + "injured", + "injured", + "injured", + "slightly injured", + "slightly injured", + "slightly injured", + "slightly injured", + "barely scratched", + "barely scratched", + "barely scratched", + "uninjured" +} + +-- Get custom text for a visible HUD. +local function gettext(p2, n2) + -- First line: distance units, player HP. + local t = "m " .. (health[p2:get_hp()] or "?") + + -- Check for a wielded item, and add its description + -- to a line below if available. + local w = p2:get_wielded_item() + if w then + w = w:get_name() + local r = minetest.registered_items[w] + t = t .. "\n" .. (r and r.description or w) + end + + if not nodecore.interact(n2) then + t = t .. "\nSPECTATOR" + end + + return t +end + +-- Determine if two players can see one another, using a +-- shared cache for commutitivity. +local function cansee(p1, n1, p2, n2, los) + -- Sight is communitive: if p1 can see p2, p2 can see p1. + -- Compute a single shared cache key for both players that's + -- independent of order, and check for a cached result for + -- this player pair. + local loskey = (n1 < n2) + and (string_format("%q", n1) .. "|" .. string_format("%q", n2)) + or (string_format("%q", n2) .. "|" .. string_format("%q", n1)) + local l = los[loskey] + if l ~= nil then return l end + + -- Dead players neither see, nor are recognizable. + if p1:get_hp() < 1 or p2:get_hp() < 1 then + los[loskey] = false + return + end + + -- Players must be within max distance of one another. + local o1 = p1:getpos() + local o2 = p2:getpos() + local dx = o1.x - o2.x + local dy = o1.y - o2.y + local dz = o1.z - o2.z + if (dx * dx + dy * dy + dz * dz) > (distance * distance) then + los[loskey] = false + return + end + + -- Check for line of sight from approximage eye level + -- of one player to the other. + o1.y = o1.y + 1.5 + o2.y = o2.y + 1.5 + l = minetest.line_of_sight(o1, o2, distance / precision) or false + + -- Cache result (for checking opposite direction). + los[loskey] = l + return l +end + +-- Determine if player 1 can see player 2's face, including +-- checks for distance, line-of-sight, and facing direction. +local function canseeface(p1, n1, p2, n2, los) + -- Checks for reciprocal line-of-sight. + if not cansee(p1, n1, p2, n2, los) then return end + + -- Players must be facing each other; cannot identify another + -- player's face when their back is turned. Note that + -- minetest models don't show pitch, so ignore the y component. + + -- Compute normalized 2d vector from one player to another. + local o1 = p1:getpos() + local o2 = p2:getpos() + local ll = minetest.get_node_light({x = o2.x, y = o2.y + 1.6, z = o2.z}) + if ll < 5 then return end + local dx = o1.x - o2.x + local dz = o1.z - o2.z + local d = dx * dx + dz * dz + if d == 0 then return end + d = math_sqrt(d) + dx = dx / d + dz = dz / d + + -- Compute normalized 2d facing direction vector for target player. + local l2 = p2:get_look_dir() + d = l2.x * l2.x + l2.z * l2.z + if d == 0 then return end + d = math_sqrt(d) + l2.x = l2.x / d + l2.z = l2.z / d + + -- Compare directions via dot product. + if (dx * l2.x + dz * l2.z) <= 0.5 then return end + + return true +end + +-- On each global step, check all player visibility, and create/remove/update +-- each player's HUDs accordingly. +minetest.register_globalstep(function() + local los = {} + local conn = minetest.get_connected_players() + for _, p1 in pairs(conn) do + local n1 = p1:get_player_name() + local h = huds[n1] + if not h then + h = {} + huds[n1] = h + end + for _, p2 in pairs(conn) do + if p2 ~= p1 then + local n2 = p2:get_player_name() + local i = h[n2] + if canseeface(p1, n1, p2, n2, los) then + local p = p2:getpos() + p.y = p.y + 1.25 + local t = gettext(p2, n2) + + -- Create a new HUD if not present. + if not i then + i = {o = p1, t = t, p = p} + i.i = p1:hud_add({ + hud_elem_type = "waypoint", + world_pos = p, + name = n2, + text = t, + number = 0xffffff + }) + h[n2] = i + end + + -- Update HUD if outdated. + if p.x ~= i.p.x or p.y ~= i.p.y or p.z ~= i.p.z then + p1:hud_change(i.i, "world_pos", p) + i.p = p + end + if i.t ~= t then + p1:hud_change(i.i, "text", t) + i.t = t + end + elseif i then + -- Remove HUD if visibility lost. + p1:hud_remove(i.i) + h[n2] = nil + end + end + end + end + end) diff --git a/mods/nc_nodefall/damage.lua b/mods/nc_nodefall/damage.lua new file mode 100644 index 00000000..0226f618 --- /dev/null +++ b/mods/nc_nodefall/damage.lua @@ -0,0 +1,48 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, pairs, vector + = math, minetest, pairs, vector +local math_floor + = math.floor +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local fallname = "__builtin:falling_node" +local fallnode = minetest.registered_entities[fallname] + +local dmg = {} + +local function dmggc() + dmg = {} + minetest.after(10, dmggc) +end +dmggc() + +local oldtick = fallnode.on_step +fallnode.on_step = function(self, dtime, ...) + if not self.crush_damage then + local def = minetest.registered_items[self.node.name] + self.crush_damage = def and def.crush_damage or 0 + end + if self.crush_damage <= 0 then + return oldtick(self, dtime, ...) + end + + local pos = self.object:getpos() + local vel = self.object:getvelocity() + local q = dmg[self] or 0 + local v = vector.length(vel) + q = q + v * v * dtime * self.crush_damage + if q > 1 then + local n = math_floor(q) + for k, v in pairs(minetest.get_objects_inside_radius(pos, 1)) do + v:set_hp(v:get_hp() - n) + end + q = q - n + end + dmg[self] = q + + return oldtick(self, dtime, ...) +end + +minetest.register_entity(":" .. fallname, fallnode) diff --git a/mods/nc_nodefall/depends.txt b/mods/nc_nodefall/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_nodefall/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_nodefall/disturb.lua b/mods/nc_nodefall/disturb.lua new file mode 100644 index 00000000..1836de1d --- /dev/null +++ b/mods/nc_nodefall/disturb.lua @@ -0,0 +1,76 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, math, minetest, nodecore, print, tostring, vector + = ipairs, math, minetest, nodecore, print, tostring, vector +local math_floor, math_random + = math.floor, math.random +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local falling = {groups = {falling_node = true}} +local radius = {x = 2, y = 2, z = 2} + +local function fallcheck(name, start) + if not nodecore.interact(name) then return end + + local target = vector.add(start, { + x = math_random() * 128 - 64, + y = math_random() * 128 - 64, + z = math_random() * 128 - 64 + }) + local clr, pos = minetest.line_of_sight(start, target) + if clr then return end + + local found = minetest.find_nodes_in_area( + vector.subtract(pos, radius), + vector.subtract(pos, radius), + "group:falling_node") + if #found < 1 then return end + pos = nodecore.pickrand(found) + + local miny = pos.y - 64 + repeat pos.y = pos.y - 1 until pos.y < miny or not nodecore.match(pos, falling) + if pos.y < miny then return end + pos.y = pos.y + 1 + local prev = minetest.get_node(pos).name + nodecore.falling_repose_check(pos) + if minetest.get_node(pos).name ~= prev then + print(modname .. ": " .. name .. " disturbed " + .. prev .. " at " .. tostring(pos)) + end +end + +local function queuechecks(qty, name, pos) + if qty < 1 then return end + minetest.after(0, function() + for i = 1, qty do + fallcheck(name, pos) + end + end) +end + +local oldpos = {} +local qtys = {} +minetest.register_globalstep(function(dtime) + for i, v in ipairs(minetest.get_connected_players()) do + local name = v:get_player_name() + + local pos = v:getpos() + local old = oldpos[name] or pos + oldpos[name] = pos + + if v:get_player_control().sneak then return end + + local q = (qtys[name] or 0) + + vector.distance(pos, old) * 0.25 + + dtime * 0.05 + queuechecks(math_floor(q), name, pos) + qtys[name] = q - math_floor(q) + end + end) + +minetest.register_on_dignode(function(pos, oldnode, digger) + local name = "(unknown)" + if digger and digger.get_player_name then name = digger:get_player_name() end + queuechecks(4, name, pos) + end) diff --git a/mods/nc_nodefall/init.lua b/mods/nc_nodefall/init.lua new file mode 100644 index 00000000..34ca232e --- /dev/null +++ b/mods/nc_nodefall/init.lua @@ -0,0 +1,10 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, minetest + = dofile, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local path = minetest.get_modpath(modname) + +dofile(path .. "/damage.lua") +dofile(path .. "/disturb.lua") diff --git a/mods/nc_player/assetsrc/nc_inv_bg.xcf b/mods/nc_player/assetsrc/nc_inv_bg.xcf new file mode 100644 index 00000000..7c389c91 Binary files /dev/null and b/mods/nc_player/assetsrc/nc_inv_bg.xcf differ diff --git a/mods/nc_player/depends.txt b/mods/nc_player/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_player/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_player/hotpotato.lua b/mods/nc_player/hotpotato.lua new file mode 100644 index 00000000..210b8b3a --- /dev/null +++ b/mods/nc_player/hotpotato.lua @@ -0,0 +1,42 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, pairs + = minetest, pairs +-- LUALOCALS > --------------------------------------------------------- + +local function hotpotatoes(player) + local inv = player:get_inventory() + local hurt = 0 + local throw = {} + for i = 1, inv:get_size("main") do + local s = inv:get_stack("main", i) + local n = not s:is_empty() and s:get_name() + n = n and minetest.registered_items[n] + n = n and n.damage_per_second + if n and n > 0 then + hurt = hurt + n + inv:set_stack("main", i, "") + throw[#throw + 1] = s + end + end + if #throw > 0 then + local pname = player:get_player_name() + local pos = player:get_pos() + pos.y = pos.y + 1.2 + local dir = player:get_look_dir() + dir.x = dir.x * 5 + dir.y = dir.y * 5 + 3 + dir.z = dir.z * 5 + for k, v in pairs(throw) do + local obj = minetest.add_item(pos, v) + obj:set_velocity(dir) + obj:get_luaentity().dropped_by = pname + end + end + if hurt > 0 then player:set_hp(player:get_hp() - hurt) end +end + +minetest.register_globalstep(function() + for k, v in pairs(minetest.get_connected_players()) do + hotpotatoes(v) + end + end) diff --git a/mods/nc_player/init.lua b/mods/nc_player/init.lua new file mode 100644 index 00000000..e1a1ad22 --- /dev/null +++ b/mods/nc_player/init.lua @@ -0,0 +1,11 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, minetest + = dofile, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local path = minetest.get_modpath(modname) + +dofile(path .. "/player.lua") +dofile(path .. "/hotpotato.lua") +dofile(path .. "/suicide.lua") diff --git a/mods/nc_player/player.lua b/mods/nc_player/player.lua new file mode 100644 index 00000000..355a9199 --- /dev/null +++ b/mods/nc_player/player.lua @@ -0,0 +1,39 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local version = nodecore.version +version = version and ("Version " .. version) or "DEVELOPMENT VERSION" + +nodecore.inventory_formspec = "size[8,5]" +.. "bgcolor[#000000C0;true]" +.. "background[0,0;8,5;nc_player_invbg.png;true]" +.. "listcolors[#00000000;#00000000;#00000000;#000000FF;#FFFFFFFF]" +.. "list[current_player;main;0,4;8,5;]" +.. "box[-0.25,-0.25;8.5,3.25;#000000C0]" +.. "label[0,0;NodeCore - " +.. minetest.formspec_escape(version) +.. "\n\n" +.. "(C)2018-2019 by Aaron Suen \n" +.. "MIT License: http://www.opensource.org/licenses/MIT\n\n" +.. "GitLab: https://gitlab.com/sztest/nodecore\n" +.. "Discord: https://discord.gg/SHq2tkb]" + +minetest.register_on_joinplayer(function(player) + player:set_properties({ + visual = "upright_sprite", + visual_size = { x = 1, y = 2 }, + textures = { "nc_player_front.png", "nc_player_back.png" } + }) + + player:get_inventory():set_size("main", 8) + player:hud_set_hotbar_itemcount(8) + + player:hud_set_hotbar_image("nc_hud_bg.png") + player:hud_set_hotbar_selected_image("nc_hud_sel.png") + + player:set_inventory_formspec(nodecore.inventory_formspec) + + player:set_properties({stepheight = 1.2}) + end) diff --git a/mods/nc_player/suicide.lua b/mods/nc_player/suicide.lua new file mode 100644 index 00000000..30af482f --- /dev/null +++ b/mods/nc_player/suicide.lua @@ -0,0 +1,42 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, pairs, table + = minetest, nodecore, pairs, table +local table_concat + = table.concat +-- LUALOCALS > --------------------------------------------------------- + +local suiciding = {} + +local function pstate(player) + local pos = player:getpos() + local dir = player:get_look_dir() + return table_concat({ + pos.x, pos.y, pos.z, + dir.x, dir.y, dir.z + }, "|") +end + +local function suicidecheck() + minetest.after(0.5, suicidecheck) + for pname, st in pairs(suiciding) do + local player = minetest.get_player_by_name(pname) + if player then + local hp = player:get_hp() + if (hp > 0) and (player:get_player_control_bits() == 0) + and pstate(player) == st then + nodecore.addphealth(player, -1) + else + suiciding[pname] = nil + end + end + end +end +suicidecheck() + +minetest.register_chatcommand("suicide", { + description = "Commit suicide (e.g. to respawn when stuck)", + func = function(pname) + local player = minetest.get_player_by_name(pname) + if player then suiciding[pname] = pstate(player) end + end, + }) diff --git a/mods/nc_player/textures/nc_player_back.png b/mods/nc_player/textures/nc_player_back.png new file mode 100644 index 00000000..c87df37b Binary files /dev/null and b/mods/nc_player/textures/nc_player_back.png differ diff --git a/mods/nc_player/textures/nc_player_front.png b/mods/nc_player/textures/nc_player_front.png new file mode 100644 index 00000000..bd7f6941 Binary files /dev/null and b/mods/nc_player/textures/nc_player_front.png differ diff --git a/mods/nc_player/textures/nc_player_invbg.png b/mods/nc_player/textures/nc_player_invbg.png new file mode 100644 index 00000000..3cbcdb6c Binary files /dev/null and b/mods/nc_player/textures/nc_player_invbg.png differ diff --git a/mods/nc_sponge/abm.lua b/mods/nc_sponge/abm.lua new file mode 100644 index 00000000..9a088179 --- /dev/null +++ b/mods/nc_sponge/abm.lua @@ -0,0 +1,63 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, pairs + = minetest, nodecore, pairs +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +nodecore.register_limited_abm({ + label = "Sponge Growth", + interval = 10, + chance = 1000, + limited_max = 100, + nodenames = {"group:water"}, + neighbors = {modname .. ":sponge_living"}, + action = function(pos) + if minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}).name + ~= "nc_terrain:sand" then return end + minetest.set_node(pos, {name = modname .. ":sponge_living"}) + end + }) + +nodecore.register_limited_abm({ + label = "Sponge Wettening", + interval = 1, + chance = 10, + limited_max = 100, + nodenames = {modname .. ":sponge"}, + neighbors = {"group:water"}, + action = function(pos) + minetest.set_node(pos, {name = modname .. ":sponge_wet"}) + for _, pos in pairs(minetest.find_nodes_in_area( + {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1}, + {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, + {"group:water"})) do + minetest.remove_node(pos) + end + end + }) + +nodecore.register_limited_abm({ + label = "Sponge Drying in Sunlight", + interval = 1, + chance = 100, + limited_max = 100, + nodenames = {modname .. ":sponge_wet"}, + action = function(pos) + if minetest.get_node_light({x = pos.x, y = pos.y + 1, z = pos.z}) >= 15 then + return minetest.set_node(pos, {name = modname .. ":sponge"}) + end + end + }) + +nodecore.register_limited_abm({ + label = "Sponge Drying near Fire", + interval = 1, + chance = 20, + limited_max = 100, + nodenames = {modname .. ":sponge_wet"}, + neighbors = {"group:igniter"}, + action = function(pos) + return minetest.set_node(pos, {name = modname .. ":sponge"}) + end + }) diff --git a/mods/nc_sponge/depends.txt b/mods/nc_sponge/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_sponge/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_sponge/gen.lua b/mods/nc_sponge/gen.lua new file mode 100644 index 00000000..9ad22655 --- /dev/null +++ b/mods/nc_sponge/gen.lua @@ -0,0 +1,52 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore + = math, minetest, nodecore +local math_floor, math_random + = math.floor, math.random +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local maxy = -8 +local miny = maxy - 8 + +local c_sand = minetest.get_content_id("nc_terrain:sand") +local c_water = minetest.get_content_id("nc_terrain:water_source") +local c_sponge = minetest.get_content_id(modname .. ":sponge_living") + +nodecore.register_mapgen_shared(function(minp, maxp, area, data, vm, emin, emax) + if minp.y > maxy or maxp.y < miny then return end + + local function spawn(x, y, z) + nodecore.scan_flood({x = x, y = y, z = z}, 5, function(p) + if math_random() < 0.1 then return true end + local idx = area:index(p.x, p.y + 1, p.z) + if data[idx] ~= c_water then return false end + data[idx] = c_sponge + end) + end + + local qty = math_floor(math_random() * (maxp.x - minp.x) + * (maxp.z - minp.z) / (64 * 64)) + for n = 1, qty do + local x = math_floor(math_random() * (maxp.x - minp.x)) + minp.x + local z = math_floor(math_random() * (maxp.z - minp.z)) + minp.z + local starty = maxp.y + if starty > (maxy + 1) then starty = (maxy + 1) end + local endy = minp.y + if endy < miny then endy = miny end + local waterabove = nil + for y = starty, endy, -1 do + local idx = area:index(x, y, z) + local cur = data[idx] + if cur == c_water then + waterabove = true + elseif cur == c_sand and waterabove then + spawn(x, y + 1, z) + break + else + break + end + end + end + end) diff --git a/mods/nc_sponge/init.lua b/mods/nc_sponge/init.lua new file mode 100644 index 00000000..5adb5861 --- /dev/null +++ b/mods/nc_sponge/init.lua @@ -0,0 +1,8 @@ +-- LUALOCALS < --------------------------------------------------------- +local include + = include +-- LUALOCALS > --------------------------------------------------------- + +include('node') +include('abm') +include('gen') diff --git a/mods/nc_sponge/node.lua b/mods/nc_sponge/node.lua new file mode 100644 index 00000000..65b07123 --- /dev/null +++ b/mods/nc_sponge/node.lua @@ -0,0 +1,43 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest + = minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_node(modname .. ":sponge", { + description = "Sponge", + drawtype = "allfaces_optional", + tiles = {modname ..".png"}, + paramtype = "light", + groups = { + crumbly = 2, + flammable = 3, + fire_fuel = 3 + } + }) + +minetest.register_node(modname .. ":sponge_wet", { + description = "Wet Sponge", + drawtype = "allfaces_optional", + tiles = {modname ..".png^(nc_terrain_water.png^[opacity:96)"}, + paramtype = "light", + groups = { + crumbly = 2, + coolant = 1, + falling_node = 1 + } + }) + +minetest.register_node(modname .. ":sponge_living", { + description = "Living Sponge", + drawtype = "allfaces_optional", + tiles = {modname ..".png^(nc_terrain_water.png^[opacity:96)"}, + paramtype = "light", + groups = { + crumbly = 2, + coolant = 1, + falling_node = 1 + }, + drop = modname .. ":sponge_wet" + }) diff --git a/mods/nc_sponge/textures/nc_sponge.png b/mods/nc_sponge/textures/nc_sponge.png new file mode 100644 index 00000000..fd73104a Binary files /dev/null and b/mods/nc_sponge/textures/nc_sponge.png differ diff --git a/mods/nc_stats/depends.txt b/mods/nc_stats/depends.txt new file mode 100644 index 00000000..9518e4f6 --- /dev/null +++ b/mods/nc_stats/depends.txt @@ -0,0 +1 @@ +nc_api diff --git a/mods/nc_stats/init.lua b/mods/nc_stats/init.lua new file mode 100644 index 00000000..2d0d7680 --- /dev/null +++ b/mods/nc_stats/init.lua @@ -0,0 +1,235 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore, os, pairs, table, type, vector + = math, minetest, nodecore, os, pairs, table, type, vector +local math_random, os_date, table_remove + = math.random, os.date, table.remove +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local modstore = minetest.get_mod_storage() + +------------------------------------------------------------------------ +-- DATABASE SETUP + +local db = {} +nodecore.statsdb = db + +do + local s = modstore:get_string(modname) + s = s and s ~= "" and minetest.deserialize(s) or {} + db[false] = s + s.firstseen = s.firstseen or os_date("!*t") + s.startup = (s.startup or 0) + 1 +end + +local function dbadd_nav(qty, dirty, db, root, key, ...) + local v = db[root] + if key then + if not v or type(v) ~= "table" then + v = {} + db[root] = v + end + if dirty then v.dirty = true end + return dbadd_nav(qty, nil, v, key, ...) + else + v = v and type(v) == "number" and v or 0 + db[root] = v + qty + end +end +local function dbadd(qty, root, ...) + if qty == 0 then return end + return dbadd_nav(qty, true, db, root, ...) +end + +local function playeradd(qty, player, ...) + if not player then return end + local pname = (type(player) == "string") and player or player:get_player_name() + if not pname then return end + local data = db[pname] + if not data then + data = player:get_attribute(modname) + data = data and minetest.deserialize(data) + data = data or { } + db[pname] = data + end + dbadd(qty, pname, ...) + if not db[pname].firstseen then + db[pname].firstseen = os_date("!*t") + end + dbadd(qty, false, "players", ...) +end +nodecore.player_stat_add = playeradd + +------------------------------------------------------------------------ +-- PLAYER EVENTS + +local function reghook(func, stat, pwhom, npos) + return func(function(...) + local t = {...} + local whom = t[pwhom] + local n = npos and t[npos].name or nil + return playeradd(1, whom, stat, n) + end) +end +reghook(minetest.register_on_punchnode, "punch", 3, 2) +reghook(minetest.register_on_dignode, "dig", 3, 2) +reghook(minetest.register_on_placenode, "place", 3, 2) +reghook(minetest.register_on_dieplayer, "die", 1) +reghook(minetest.register_on_respawnplayer, "spawn", 1) +reghook(minetest.register_on_joinplayer, "join", 1) +reghook(minetest.register_on_leaveplayer, "leave", 1) + +minetest.register_on_player_hpchange(function(whom, change, reason) + if change < 0 then + return playeradd(-change, whom, "hurt", reason or "?") + else + return playeradd(change, whom, "heal", reason or "?") + end + end) + +minetest.register_on_cheat(function(player, name) + playeradd(1, player, "cheat", type(name) == "table" + and name.type or name or "?") + end) + +minetest.register_on_chat_message(function(name, msg) + dbadd(1, name, "chat", (msg:sub(1, 1) == "/") and "command" or "message") + end) + +minetest.register_on_shutdown(function() + for _, player in pairs(minetest.get_connected_players()) do + playeradd(1, player, "shutdown") + end + end) + +------------------------------------------------------------------------ +-- PLAYER INVENTORY SCAN + +local function invscan(dt, player) + local inv = player:get_inventory() + local t = {} + for i = 1, inv:get_size("main") do + local stack = inv:get_stack("main", i) + if not stack:is_empty() then + t[stack:get_name()] = true + end + end + for k, v in pairs(t) do + playeradd(dt, player, "inv", k) + end +end + +------------------------------------------------------------------------ +-- PLAYER MOVEMENT/IDLE HOOKS + +local playdb = { } +local idlemin = 5 +local function movement(dt, player) + if not player or not player:is_player() then return end + local pn = player:get_player_name() + + local pd = playdb[pn] + if not pd then + pd = {} + playdb[pn] = pd + end + + local pos = player:getpos() + local dir = player:get_look_dir() + local cur = { pos.x, pos.y, pos.z, dir.x, dir.y, dir.z } + local moved + if pd.last then + for i = 1, 6 do + moved = moved or pd.last[i] ~= cur[i] + end + if moved then + playeradd(vector.distance(pos, + {x = pd.last[1], y = pd.last[2], z = pd.last[3]}), + player, "distance") + end + end + pd.last = cur + + local t = pd.t or 0 + if moved then + pd.t = 0 + if t >= idlemin then + playeradd(t, player, "idle") + return playeradd(dt, player, "move") + else + return playeradd(t + dt, player, "move") + end + else + if t >= idlemin then + return playeradd(dt, player, "idle") + else + pd.t = t + dt + if (t + dt) >= idlemin then + return playeradd(t + dt, player, "idle") + end + end + end +end +minetest.register_globalstep(function(dt) + for _, player in pairs(minetest.get_connected_players()) do + invscan(dt, player) + movement(dt, player) + end + end) + +------------------------------------------------------------------------ +-- DATABASE FLUSH CYCLE + +local opq = {} + +local function flushkey(k) + local v = db[k] + if not v or not v.dirty then return end + v.dirty = nil + + if k == false then + return modstore:set_string(modname, minetest.serialize(v)) + end + + local player = minetest.get_player_by_name(k) + if player then + return player:set_attribute(modname, minetest.serialize(v)) + end +end + +local function flushop() + local k = table_remove(opq) + if k == nil then return end + minetest.after(0, flushop) + return flushkey(k) +end + +local function flushenq() + minetest.after(20, flushenq) + if #opq > 0 then return end + for k, v in pairs(db) do + opq[#opq + 1] = k + end + for i = 1, #opq do + local j = math_random(1, #opq) + opq[i], opq[j] = opq[j], opq[i] + end + minetest.after(0, flushop) +end +flushenq() + +minetest.register_globalstep(function(dt) + dbadd(dt, false, "elapsed") + dbadd(1, false, "tick") + end) + +minetest.register_on_leaveplayer(function(player) + return flushkey(player:get_player_name()) + end) +minetest.register_on_shutdown(function() + dbadd(1, false, "shutdown") + flushkey(false) + for _, player in pairs(minetest.get_connected_players()) do + flushkey(player:get_player_name()) + end + end) diff --git a/mods/nc_stonework/chip.lua b/mods/nc_stonework/chip.lua new file mode 100644 index 00000000..54f3ef95 --- /dev/null +++ b/mods/nc_stonework/chip.lua @@ -0,0 +1,36 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_craftitem(modname .. ":chip", { + description = "Stone Chip", + inventory_image = modname .. "_stone.png" + }) + +nodecore.register_craft({ + label = "break cobble to chips", + action = "pummel", + nodes = { + {match = "nc_terrain:cobble_loose", replace = "air"} + }, + items = { + {name = modname .. ":chip", count = 8, scatter = 5} + }, + toolgroups = {cracky = 2}, + itemscatter = 5 + }) + +nodecore.register_craft({ + label = "repack chips to cobble", + action = "pummel", + nodes = { + { + match = {name = modname .. ":chip", count = 8}, + replace = "nc_terrain:cobble_loose" + } + }, + toolgroups = {thumpy = 2} + }) diff --git a/mods/nc_stonework/depends.txt b/mods/nc_stonework/depends.txt new file mode 100644 index 00000000..6c8c3646 --- /dev/null +++ b/mods/nc_stonework/depends.txt @@ -0,0 +1,2 @@ +nc_terrain +nc_woodwork \ No newline at end of file diff --git a/mods/nc_stonework/init.lua b/mods/nc_stonework/init.lua new file mode 100644 index 00000000..c38a7bd5 --- /dev/null +++ b/mods/nc_stonework/init.lua @@ -0,0 +1,10 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, minetest + = dofile, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local path = minetest.get_modpath(modname) + +dofile(path .. "/chip.lua") +dofile(path .. "/tools.lua") diff --git a/mods/nc_stonework/textures/nc_stonework_stone.png b/mods/nc_stonework/textures/nc_stonework_stone.png new file mode 100644 index 00000000..c335050f Binary files /dev/null and b/mods/nc_stonework/textures/nc_stonework_stone.png differ diff --git a/mods/nc_stonework/textures/nc_stonework_tip_hatchet.png b/mods/nc_stonework/textures/nc_stonework_tip_hatchet.png new file mode 100644 index 00000000..5e132b7c Binary files /dev/null and b/mods/nc_stonework/textures/nc_stonework_tip_hatchet.png differ diff --git a/mods/nc_stonework/textures/nc_stonework_tip_mallet.png b/mods/nc_stonework/textures/nc_stonework_tip_mallet.png new file mode 100644 index 00000000..19a3f1e3 Binary files /dev/null and b/mods/nc_stonework/textures/nc_stonework_tip_mallet.png differ diff --git a/mods/nc_stonework/textures/nc_stonework_tip_pick.png b/mods/nc_stonework/textures/nc_stonework_tip_pick.png new file mode 100644 index 00000000..526173b1 Binary files /dev/null and b/mods/nc_stonework/textures/nc_stonework_tip_pick.png differ diff --git a/mods/nc_stonework/textures/nc_stonework_tip_spade.png b/mods/nc_stonework/textures/nc_stonework_tip_spade.png new file mode 100644 index 00000000..6ef6910c Binary files /dev/null and b/mods/nc_stonework/textures/nc_stonework_tip_spade.png differ diff --git a/mods/nc_stonework/tools.lua b/mods/nc_stonework/tools.lua new file mode 100644 index 00000000..b7afc7b8 --- /dev/null +++ b/mods/nc_stonework/tools.lua @@ -0,0 +1,56 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, minetest, nodecore, type + = ipairs, minetest, nodecore, type +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +nodecore.register_stone_tip_tool, +nodecore.registered_stone_tip_tools += nodecore.mkreg() + +local chip = modname .. ":chip" +nodecore.extend_item(chip, function(copy, orig) + copy.on_place = function(itemstack, placer, pointed_thing, ...) + if not nodecore.interact(placer) then return end + if itemstack:get_name() == chip and pointed_thing.type == "node" then + local pos = pointed_thing.under + for i, v in ipairs(nodecore.registered_stone_tip_tools) do + if nodecore.match(pos, { + name = v.from, + wear = 0.02 + }) then + minetest.remove_node(pos) + nodecore.item_eject(pos, v.to) + itemstack:set_count(itemstack:get_count() - 1) + return itemstack + end + end + end + return orig.on_place(itemstack, placer, pointed_thing, ...) + end + end) + +local function tooltip(name, group) + local tool = modname .. ":tool_" .. name:lower() + local wood = "nc_woodwork:tool_" .. name:lower() + minetest.register_tool(tool, { + description = "Stone-Tipped " .. name, + inventory_image = "nc_woodwork_tool_" .. name:lower() .. ".png^" + .. modname .. "_tip_" .. name:lower() .. ".png", + tool_wears_to = wood, + groups = { + flammable = 2 + }, + tool_capabilities = nodecore.toolcaps({ + uses = 0.25, + [group] = 3 + }) + }) + nodecore.register_stone_tip_tool({from = wood, to = tool}) +end + +tooltip("Mallet", "thumpy") +tooltip("Spade", "crumbly") +tooltip("Hatchet", "choppy") +tooltip("Pick", "cracky") diff --git a/mods/nc_terrain/api.lua b/mods/nc_terrain/api.lua new file mode 100644 index 00000000..f6af9c20 --- /dev/null +++ b/mods/nc_terrain/api.lua @@ -0,0 +1,24 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore + = math, minetest, nodecore +local math_floor, math_sqrt + = math.floor, math.sqrt +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +nodecore.hard_stone_strata = 7 + +function nodecore.hard_stone_tile(n) + local o = math_floor(math_sqrt(n or 0) * 96) + if o <= 0 then + return modname .. "_stone.png" + end + if o >= 255 then + return modname .. "_stone.png^" + .. modname .. "_stone_hard.png" + end + return modname .. "_stone.png^(" + .. modname .. "_stone_hard.png^[opacity:" + .. o .. ")" +end diff --git a/mods/nc_terrain/biome.lua b/mods/nc_terrain/biome.lua new file mode 100644 index 00000000..0cb0d0ed --- /dev/null +++ b/mods/nc_terrain/biome.lua @@ -0,0 +1,34 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest + = minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_biome({ + name = "unknown", + node_top = modname .. ":dirt_with_grass", + depth_top = 1, + node_filler = modname .. ":dirt", + depth_filler = 1, + node_riverbed = modname .. ":sand", + depth_riverbed = 2, + y_min = 6, + y_max = 31000, + heat_point = 0, + humidity_point = 0, + }) + +minetest.register_biome({ + name = "seabed", + node_top = modname .. ":sand", + depth_top = 1, + node_filler = modname .. ":sand", + depth_filler = 1, + node_riverbed = modname .. ":sand", + depth_riverbed = 2, + y_min = -31000, + y_max = 5, + heat_point = 0, + humidity_point = 0, + }) diff --git a/mods/nc_terrain/depends.txt b/mods/nc_terrain/depends.txt new file mode 100644 index 00000000..296c012e --- /dev/null +++ b/mods/nc_terrain/depends.txt @@ -0,0 +1 @@ +nc_api \ No newline at end of file diff --git a/mods/nc_terrain/grasslife.lua b/mods/nc_terrain/grasslife.lua new file mode 100644 index 00000000..d398dfeb --- /dev/null +++ b/mods/nc_terrain/grasslife.lua @@ -0,0 +1,40 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local dirt = "nc_terrain:dirt" +local grass = "nc_terrain:dirt_with_grass" + +nodecore.register_limited_abm({ + label = "Grass Spread", + nodenames = {dirt, "nc_terrain:dirt_loose"}, + neighbors = {grass}, + interval = 6, + chance = 50, + action = function(pos, node) + local above = {x = pos.x, y = pos.y + 1, z = pos.z} + if (minetest.get_node_light(above) or 0) < 13 then return end + local nodedef = minetest.registered_nodes[node.name] + if nodedef and nodedef.liquidtype ~= "none" + and nodedef.drawtype ~= "airlike" then return end + return minetest.set_node(pos, {name = grass}) + end + }) + +nodecore.register_limited_abm({ + label = "Grass Decay", + nodenames = {grass}, + interval = 8, + chance = 50, + action = function(pos, node) + local above = {x = pos.x, y = pos.y + 1, z = pos.z} + local name = minetest.get_node(above).name + local nodedef = minetest.registered_nodes[name] + if name ~= "ignore" and nodedef and not ((nodedef.sunlight_propagates or + nodedef.paramtype == "light") and + nodedef.liquidtype == "none") then + minetest.set_node(pos, {name = dirt}) + end + end + }) diff --git a/mods/nc_terrain/init.lua b/mods/nc_terrain/init.lua new file mode 100644 index 00000000..8c64e303 --- /dev/null +++ b/mods/nc_terrain/init.lua @@ -0,0 +1,11 @@ +-- LUALOCALS < --------------------------------------------------------- +local include + = include +-- LUALOCALS > --------------------------------------------------------- + +include('api') +include('node') +include('biome') +include('strata') +include('ore') +include('grasslife') diff --git a/mods/nc_terrain/node.lua b/mods/nc_terrain/node.lua new file mode 100644 index 00000000..f19724a2 --- /dev/null +++ b/mods/nc_terrain/node.lua @@ -0,0 +1,218 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, minetest, nodecore, pairs + = ipairs, minetest, nodecore, pairs +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local function regterrain(def) + def.name = def.name or def.description:gsub("%W", "_"):lower() + def.fullname = modname .. ":" .. def.name + + def.tiles = def.tiles or { def.fullname:gsub("%W", "_") .. ".png" } + def.is_ground_content = true + + if def.liquidtype then + def.liquid_alternative_flowing = def.fullname .. "_flowing" + def.liquid_alternative_source = def.fullname .. "_source" + def.fullname = def.fullname .. "_" .. def.liquidtype + def.special_tiles = def.special_tiles or { def.tiles[1], def.tiles[1] } + end + + def.mapgen = def.mapgen or { def.name } + + minetest.register_node(def.fullname, def) + + for k, v in pairs(def.mapgen) do + minetest.register_alias("mapgen_" .. v, def.fullname) + end +end + +local function clone(t) return minetest.deserialize(minetest.serialize(t)) end + +local function regliquid(def) + local t = clone(def) + t.drawtype = "liquid" + t.liquidtype = "source" + regterrain(t) + + t = clone(def) + t.mapgen = { } + t.drawtype = "flowingliquid" + t.liquidtype = "flowing" + t.paramtype2 = "flowingliquid" + regterrain(t) +end + +local strata = {} +regterrain({ + description = "Stone", + mapgen = { + "stone", + "stone_with_coal", + "stone_with_iron", + "desert_stone", + "sandstone", + "mese", + }, + groups = { + cracky = 2 + }, + drop_in_place = modname .. ":cobble", + strata = strata + }) +strata[1] = modname .. ":stone" +for i = 1, nodecore.hard_stone_strata do + regterrain({ + description = "Hard Stone " .. i, + tiles = { nodecore.hard_stone_tile(i) }, + groups = { + cracky = i + 2 + }, + drop_in_place = modname .. ":cobble" + }) + strata[i + 1] = modname .. ":hard_stone_" .. i +end + +regterrain({ + description = "Cobble", + tiles = { modname .. "_gravel.png^" .. modname .. "_cobble.png" }, + mapgen = { + "sandstonebrick", + "stair_sandstone_block", + "cobble", + "stair_cobble", + "stair_desert_stone", + "mossycobble" + }, + groups = { + cracky = 1 + }, + alternate_loose = { + repack_level = 2, + groups = { + cracky = 0, + crumbly = 2, + falling_repose = 3 + } + }, + crush_damage = 2 + }) + +for _, v in ipairs({ + "snow", + "snowblock", + "junglegrass", + "tree", + "jungletree", + "pine_tree", + "leaves", + "apple", + "jungleleaves", + "pine_needles" + }) do + minetest.register_alias("mapgen_" .. v, "air") +end + +regterrain({ + description = "Dirt", + alternate_loose = { + groups = { + falling_repose = 2, + soil = 2 + } + }, + mapgen = { + "dirt", + "ice", + }, + groups = { + crumbly = 1, + soil = 1 + }, + crush_damage = 1 + }) +regterrain({ + description = "Dirt with Grass", + tiles = { + modname .. "_grass_top.png", + modname .. "_dirt.png", + modname .. "_dirt.png^" .. modname .. "_grass_side.png" + }, + mapgen = { + "dirt_with_grass", + "dirt_with_snow" + }, + groups = { + crumbly = 2, + soil = 1, + green = 1 + }, + drop_in_place = modname .. ":dirt" + }) +regterrain({ + description = "Gravel", + alternate_loose = { + groups = { + crumbly = 2, + falling_repose = 2 + } + }, + groups = { + crumbly = 1, + falling_node = 1 + }, + crush_damage = 1 + }) +regterrain({ + description = "Sand", + alternate_loose = { + groups = { + falling_repose = 1 + } + }, + groups = { + crumbly = 1, + falling_node = 1 + }, + mapgen = { + "sand", + "clay", + "desert_sand" + }, + crush_damage = 0.5 + }) + +regliquid({ + description = "Water", + mapgen = { "river_water_source", "water_source" }, + paramtype = "light", + liquid_viscosity = 1, + liquid_renewable = true, + alpha = 160, + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + drowning = 1, + drop = "", + groups = { coolant = 1, water = 2 }, + post_effect_color = {a = 103, r = 30, g = 76, b = 90} + }) +regliquid({ + description = "Lava", + mapgen = { "lava_source" }, + paramtype = "light", + liquid_viscosity = 7, + liquid_renewable = false, + light_source = 13, + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + drowning = 1, + damage_per_second = 8, + drop = "", + groups = { igniter = 1 }, + post_effect_color = {a = 191, r = 255, g = 64, b = 0} + }) diff --git a/mods/nc_terrain/ore.lua b/mods/nc_terrain/ore.lua new file mode 100644 index 00000000..01cd4c4f --- /dev/null +++ b/mods/nc_terrain/ore.lua @@ -0,0 +1,26 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest + = minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_ore({ + name = "gravel", + ore_type = "blob", + ore = modname .. ":gravel", + wherein = modname .. ":stone", + clust_size = 5, + clust_scarcity = 8 * 8 * 8, + random_factor = 0, + noise_params = { + offset = 0, + scale = 3, + spread = {x=10, y=25, z=10}, + seed = 34654, + octaves = 3, + persist = 0.5, + flags = "eased", + }, + noise_threshold = 1.2 + }) diff --git a/mods/nc_terrain/strata.lua b/mods/nc_terrain/strata.lua new file mode 100644 index 00000000..306f7567 --- /dev/null +++ b/mods/nc_terrain/strata.lua @@ -0,0 +1,69 @@ +-- LUALOCALS < --------------------------------------------------------- +local error, ipairs, math, minetest, nodecore, pairs + = error, ipairs, math, minetest, nodecore, pairs +local math_floor, math_random + = math.floor, math.random +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local thickness = 64 + +nodecore.stratadata = nodecore.memoize(function() + local data = {} + data.stratbyid = {} + data.altsbyid = {} + for k, v in pairs(minetest.registered_nodes) do + if v.strata then + local sn + for s, n in ipairs(v.strata) do + if n == k then sn = s end + end + if not sn then error(k .. " not found in own strata") end + local cid = minetest.get_content_id(k) + data.stratbyid[cid] = sn + data.altsbyid[cid] = {} + for s, n in ipairs(v.strata) do + data.altsbyid[cid][s] = minetest.get_content_id(n) + end + end + end + return data + end) + +nodecore.register_mapgen_shared(function(minp, maxp, area, data, vm, emin, emax) + if minp.y > -64 then return end + + local ai = area.index + local t = nodecore.hard_stone_strata + local sd = nodecore.stratadata() + local byid = sd.stratbyid + local alts = sd.altsbyid + + for z = minp.z, maxp.z do + for y = minp.y, maxp.y do + local raw = y / -thickness + local strat = math_floor(raw) + local dither = raw - strat + if strat > t then + strat = t + dither = nil + elseif dither > (4 / thickness) then + dither = nil + else + dither = (dither * thickness + 1)/5 + end + for x = minp.x, maxp.x do + local i = ai(area, x, y, z) + if byid[data[i]] then + if dither and math_random() >= dither then + data[i] = alts[data[i]][strat] + else + data[i] = alts[data[i]][strat + 1] + end + end + end + end + end + end, + -100) diff --git a/mods/nc_terrain/textures/nc_terrain_cobble.png b/mods/nc_terrain/textures/nc_terrain_cobble.png new file mode 100644 index 00000000..205258a1 Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_cobble.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_dark.png b/mods/nc_terrain/textures/nc_terrain_dark.png new file mode 100644 index 00000000..e9596688 Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_dark.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_dirt.png b/mods/nc_terrain/textures/nc_terrain_dirt.png new file mode 100644 index 00000000..7cd1b9c6 Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_dirt.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_grass_side.png b/mods/nc_terrain/textures/nc_terrain_grass_side.png new file mode 100644 index 00000000..adbd1f1f Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_grass_side.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_grass_top.png b/mods/nc_terrain/textures/nc_terrain_grass_top.png new file mode 100644 index 00000000..d7b1dfec Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_grass_top.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_gravel.png b/mods/nc_terrain/textures/nc_terrain_gravel.png new file mode 100644 index 00000000..55d44ba2 Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_gravel.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_lava.png b/mods/nc_terrain/textures/nc_terrain_lava.png new file mode 100644 index 00000000..1a730ffa Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_lava.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_sand.png b/mods/nc_terrain/textures/nc_terrain_sand.png new file mode 100644 index 00000000..7bca609e Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_sand.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_stone.png b/mods/nc_terrain/textures/nc_terrain_stone.png new file mode 100644 index 00000000..a395fb71 Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_stone.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_stone_hard.png b/mods/nc_terrain/textures/nc_terrain_stone_hard.png new file mode 100644 index 00000000..d8c1f88b Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_stone_hard.png differ diff --git a/mods/nc_terrain/textures/nc_terrain_water.png b/mods/nc_terrain/textures/nc_terrain_water.png new file mode 100644 index 00000000..b5985cf1 Binary files /dev/null and b/mods/nc_terrain/textures/nc_terrain_water.png differ diff --git a/mods/nc_tote/depends.txt b/mods/nc_tote/depends.txt new file mode 100644 index 00000000..b47dd05f --- /dev/null +++ b/mods/nc_tote/depends.txt @@ -0,0 +1,2 @@ +nc_api +nc_api_craft \ No newline at end of file diff --git a/mods/nc_tote/init.lua b/mods/nc_tote/init.lua new file mode 100644 index 00000000..1806288e --- /dev/null +++ b/mods/nc_tote/init.lua @@ -0,0 +1,132 @@ +-- LUALOCALS < --------------------------------------------------------- +local ItemStack, ipairs, minetest, nodecore, pairs, type + = ItemStack, ipairs, minetest, nodecore, pairs, type +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local function meta(pos) + local node = minetest.get_node(pos) + + return node, meta +end + +local function totedug(pos, node, meta, digger) + local dump + for dx = -1, 1 do + for dz = -1, 1 do + local p = {x = pos.x + dx, y = pos.y, z = pos.z + dz} + local n = minetest.get_node(p) + local d = minetest.registered_items[n.name] or {} + if d and d.groups and d.groups.totable then + local m = minetest.get_meta(p):to_table() + for k1, v1 in pairs(m.inventory or {}) do + for k2, v2 in pairs(v1) do + if type(v2) == "userdata" then + v1[k2] = v2:to_string() + end + end + end + dump = dump or {} + dump[#dump + 1] = { + x = dx, + z = dz, + n = n, + m = m + } + minetest.remove_node(p) + end + end + end + local drop = ItemStack(modname .. ":handle") + if dump then + local meta = drop:get_meta() + meta:set_string("carrying", minetest.serialize(dump)) + if #dump == 1 then + meta:set_string("description", "Tote (1 Slot)") + else + meta:set_string("description", "Tote (" .. #dump .. " Slots)") + end + end + minetest.handle_node_drops(pos, {drop}, digger) +end + +local function toteplace(stack, placer, pointed) + local pos = nodecore.buildable_to(pointed.under) and pointed.under + or nodecore.buildable_to(pointed.above) and pointed.above + if not pos then return stack end + + stack = ItemStack(stack) + local inv = stack:get_meta():get_string("carrying") + inv = inv and (inv ~= "") and minetest.deserialize(inv) + if not inv then + minetest.set_node(pos, {name = stack:get_name()}) + stack:set_count(stack:get_count() - 1) + return stack + end + + local commit = {{pos, {name = stack:get_name()}, {}}} + for _, v in ipairs(inv) do + if commit then + local p = {x = pos.x + v.x, y = pos.y, z = pos.z + v.z} + if not nodecore.buildable_to(p) then + commit = nil + else + commit[#commit + 1] = {p, v.n, v.m} + end + end + end + if commit then + for _, v in ipairs(commit) do + minetest.set_node(v[1], v[2]) + minetest.get_meta(v[1]):from_table(v[3]) + end + stack:set_count(stack:get_count() - 1) + end + return stack +end + +minetest.register_node(modname .. ":handle", { + description = "Tote Handle", + drawtype = "nodebox", + node_box = nodecore.fixedbox( + {-0.5, -0.5, -0.5, 0.5, -3/8, 0.5}, + {-0.5, -3/8, -0.5, -3/8, 3/8, -3/8}, + {-0.5, -3/8, 3/8, -3/8, 3/8, 0.5}, + {3/8, -3/8, -0.5, 0.5, 3/8, -3/8}, + {3/8, -3/8, 3/8, 0.5, 3/8, 0.5}, + {-0.5, 1/4, -0.5, -3/8, 3/8, 0.5}, + {3/8, 1/4, -0.5, 0.5, 3/8, 0.5}, + {-0.5, 3/8, -1/8, 0.5, 0.5, 1/8} + ), + selection_box = nodecore.fixedbox( + {-0.5, -0.5, -0.5, 0.5, 3/8, 0.5} + ), + paramtype = "light", + tiles = { + "nc_lode_annealed.png", + "nc_lode_annealed.png", + "nc_lode_annealed.png^[lowpart:75:nc_tree_tree_side.png" + .. "^[lowpart:12.5:nc_lode_annealed.png" + }, + groups = { + snappy = 1, + fire_fuel = 5 + }, + after_dig_node = totedug, + on_place = toteplace, + drop = "" + }) + +nodecore.register_craft({ + label = "craft tote handle", + norotate = true, + nodes = { + {match = "nc_woodwork:frame", replace = "air"}, + {y = -1, match = "nc_lode:block_annealed", replace = modname .. ":handle"}, + {y = -1, x = 1, match = "nc_woodwork:shelf"}, + {y = -1, x = -1, match = "nc_woodwork:shelf"}, + {y = -1, z = 1, match = "nc_woodwork:shelf"}, + {y = -1, z = -1, match = "nc_woodwork:shelf"}, + } + }) diff --git a/mods/nc_tree/api.lua b/mods/nc_tree/api.lua new file mode 100644 index 00000000..bf04d476 --- /dev/null +++ b/mods/nc_tree/api.lua @@ -0,0 +1,20 @@ +-- LUALOCALS < --------------------------------------------------------- +local ipairs, minetest, nodecore + = ipairs, minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +nodecore.register_leaf_drops, nodecore.registered_leaf_drops += nodecore.mkreg() + +function nodecore.leaf_decay(pos, node) + node = node or minetest.get_node(pos) + local t = {} + for i, v in ipairs(nodecore.registered_leaf_drops) do + t = v(pos, node, t) or t + end + local p = nodecore.pickrand(t, function(x) return x.prob end) + if not p then return end + minetest.set_node(pos, p) + if p.item then nodecore.item_eject(pos, p.item) end + return minetest.check_for_falling(pos) +end diff --git a/mods/nc_tree/cultivation.lua b/mods/nc_tree/cultivation.lua new file mode 100644 index 00000000..366fa005 --- /dev/null +++ b/mods/nc_tree/cultivation.lua @@ -0,0 +1,86 @@ +-- LUALOCALS < --------------------------------------------------------- +local math, minetest, nodecore + = math, minetest, nodecore +local math_random, math_sqrt + = math.random, math.sqrt +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_node(modname .. ":eggcorn", { + description = "EggCorn", + drawtype = "plantlike", + paramtype = "light", + visual_scale = 0.5, + collision_box = nodecore.fixedbox(-3/16, -0.5, -3/16, 3/16, 0, 3/16), + selection_box = nodecore.fixedbox(-3/16, -0.5, -3/16, 3/16, 0, 3/16), + inventory_image = "[combine:24x24:4,4=" .. modname .. "_eggcorn.png", + tiles = { modname .. "_eggcorn.png" }, + groups = { + snappy = 1, + flammable = 3, + attached_node = 1 + } + }) + +nodecore.register_leaf_drops(function(pos, node, list) + list[#list + 1] = { + name = "air", + item = modname .. ":eggcorn", + prob = 0.05 * (node.param2 + 1)} + end) + +local ldname = "nc_terrain:dirt_loose" +local epname = modname .. ":eggcorn_planted" +minetest.register_node(epname, nodecore.underride({drop = ldname}, + minetest.registered_nodes[ldname])) + +nodecore.register_craft({ + label = "eggcorn planting", + normal = {y = 1}, + nodes = { + {match = "nc_terrain:dirt_loose", replace = "air"}, + { + y = -1, + match = modname .. ":eggcorn", + replace = modname .. ":eggcorn_planted" + }, + } + }) + +nodecore.register_limited_abm({ + label = "EggCorn Growing", + nodenames = {epname}, + interval = 10, + chance = 1, + action = function(pos, node) + local meta = minetest.get_meta(pos) + local d = 0 + local w = 1 + nodecore.scan_flood(pos, 3, function(p) + local nn = minetest.get_node(p).name + local def = minetest.registered_nodes[nn] + if not def or not def.groups then + return false + end + if def.groups.soil then + d = d + def.groups.soil + w = w + 0.2 + elseif def.groups.water then + w = w + def.groups.water + return false + else + return false + end + end) + local g = (meta:get_float("growth") or 0) + + math_sqrt(d * w) * math_random() + if g >= 5000 then + meta:from_table({}) + local place = {x = pos.x - 2, y = pos.y, z = pos.z - 2} + return minetest.place_schematic(place, nodecore.tree_schematic, + "random", {}, false) + end + meta:set_float("growth", g) + end + }) diff --git a/mods/nc_tree/decor.lua b/mods/nc_tree/decor.lua new file mode 100644 index 00000000..bb62bb96 --- /dev/null +++ b/mods/nc_tree/decor.lua @@ -0,0 +1,25 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"nc_terrain:dirt_with_grass"}, + sidelen = 16, + noise_params = { + offset = -0.008, + scale = 0.016, + spread = {x = 120, y = 120, z = 120}, + seed = 2, + octaves = 3, + persist = 0.66 + }, + biomes = {"unknown"}, + y_min = 1, + y_max = 31000, + schematic = nodecore.tree_schematic, + flags = "place_center_x, place_center_z", + rotation = "random", + replacements = { } + }) diff --git a/mods/nc_tree/depends.txt b/mods/nc_tree/depends.txt new file mode 100644 index 00000000..57f3c39d --- /dev/null +++ b/mods/nc_tree/depends.txt @@ -0,0 +1,4 @@ +nc_api +nc_api_craft +nc_terrain +nc_fire? \ No newline at end of file diff --git a/mods/nc_tree/init.lua b/mods/nc_tree/init.lua new file mode 100644 index 00000000..365e2eaa --- /dev/null +++ b/mods/nc_tree/init.lua @@ -0,0 +1,17 @@ +-- LUALOCALS < --------------------------------------------------------- +local dofile, minetest + = dofile, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local path = minetest.get_modpath(modname) + +dofile(path .. "/api.lua") +dofile(path .. "/node.lua") +dofile(path .. "/leafdecay.lua") + +dofile(path .. "/stick.lua") + +dofile(path .. "/schematic.lua") +dofile(path .. "/decor.lua") +dofile(path .. "/cultivation.lua") diff --git a/mods/nc_tree/leafdecay.lua b/mods/nc_tree/leafdecay.lua new file mode 100644 index 00000000..8f7f0634 --- /dev/null +++ b/mods/nc_tree/leafdecay.lua @@ -0,0 +1,30 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +nodecore.register_limited_abm({ + label = "Leaf Decay", + interval = 1, + chance = 10, + limited_max = 100, + limited_alert = 1000, + nodenames = {modname .. ":leaves"}, + action = function(pos) + if not nodecore.scan_flood(pos, 5, function(p) + local n = minetest.get_node(p).name + if n == modname .. ":tree" + or n == "ignore" then + return true + end + if n == modname .. ":leaves" then + return + end + return false + end) then + nodecore.leaf_decay(pos) + end + end + }) diff --git a/mods/nc_tree/node.lua b/mods/nc_tree/node.lua new file mode 100644 index 00000000..87226a0e --- /dev/null +++ b/mods/nc_tree/node.lua @@ -0,0 +1,68 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore, type + = minetest, nodecore, type +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_node(modname .. ":root", { + description = "Root", + tiles = { + modname .. "_tree_top.png", + "nc_terrain_dirt.png", + "nc_terrain_dirt.png^" .. modname .. "_roots.png" + }, + groups = { + flammable = 50, + fire_fuel = 4, + choppy = 4 + }, + drop_in_place = "nc_terrain:dirt_loose" + }) + +minetest.register_node(modname .. ":tree", { + description = "Tree", + tiles = { + modname .. "_tree_top.png", + modname .. "_tree_top.png", + modname .. "_tree_side.png" + }, + groups = { + choppy = 2, + flammable = 5, + fire_fuel = 6 + } + }) + +minetest.register_node(modname .. ":leaves", { + description = "Leaves", + drawtype = "allfaces_optional", + paramtype = "light", + tiles = { modname .. "_leaves.png" }, + groups = { + snappy = 1, + flammable = 3, + fire_fuel = 2, + green = 3 + }, + alternate_loose = { + tiles = { modname .. "_leaves_dry.png" }, + walkable = false, + groups = { + flammable = 1, + falling_repose = 1, + green = 1 + } + }, + alternate_solid = { + after_dig_node = function(...) + return nodecore.leaf_decay(...) + end + } + }) +nodecore.register_leaf_drops(function(pos, node, list) + list[#list + 1] = {name = modname .. ":leaves_loose", prob = 0.5} + list[#list + 1] = {name = "air"} + end) + +local function fixed(t) return {type = "fixed", fixed = t} end diff --git a/mods/nc_tree/schematic.lua b/mods/nc_tree/schematic.lua new file mode 100644 index 00000000..553598e7 --- /dev/null +++ b/mods/nc_tree/schematic.lua @@ -0,0 +1,105 @@ +-- LUALOCALS < --------------------------------------------------------- +local error, ipairs, minetest, nodecore + = error, ipairs, minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local function ezschem(key, yslices, init) + local size = {} + local data = {} + size.y = #yslices + for y, ys in ipairs(yslices) do + if size.z and size.z ~= #ys then error("inconsistent z size") end + size.z = #ys + for z, zs in ipairs(ys) do + if size.x and size.x ~= #zs then error("inconsistent x size") end + size.x = #zs + for x = 1, zs:len() do + data[(z - 1) * size.x * size.y + (y - 1) * size.x + x] + = key[zs:sub(x, x)] + end + end + end + init = init or {} + init.size = size + init.data = data + return minetest.register_schematic(init) +end + +local root = { + " ", + " ", + " r ", + " ", + " ", +} +local trunk = { + " ", + " ", + " t ", + " ", + " ", +} +local bot = { + " ", + " ebe ", + " btb ", + " ebe ", + " ", +} +local low = { + " lll ", + "lebel", + "lbtbl", + "lebel", + " lll ", +} +local hi = { + " lll ", + "llell", + "lebel", + "llell", + " lll ", +} +local top = { + " ", + " lll ", + " lll ", + " lll ", + " ", +} + +nodecore.tree_schematic = ezschem( + { + [" "] = {name = "air", prob = 0}, + r = {name = modname .. ":root", prob = 255, force_place = true}, + t = {name = modname .. ":tree", prob = 255}, + b = {name = modname .. ":leaves", param2 = 2, prob = 255}, + e = {name = modname .. ":leaves", param2 = 1,prob = 255}, + l = {name = modname .. ":leaves", prob = 240}, + }, + { + root, + trunk, + trunk, + trunk, + bot, + low, + low, + hi, + top + }, + { + yslice_prob = { + {ypos = 1, prob = 255}, + {ypos = 2, prob = 160}, + {ypos = 3, prob = 160}, + {ypos = 4, prob = 160}, + {ypos = 5, prob = 255}, + {ypos = 6, prob = 160}, + {ypos = 7, prob = 160}, + {ypos = 8, prob = 255}, + } + } +) diff --git a/mods/nc_tree/stick.lua b/mods/nc_tree/stick.lua new file mode 100644 index 00000000..af9890f4 --- /dev/null +++ b/mods/nc_tree/stick.lua @@ -0,0 +1,30 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_node(modname .. ":stick", { + description = "Stick", + drawtype = "nodebox", + node_box = nodecore.fixedbox(-1/16, -0.5, -1/16, 1/16, 0, 1/16), + tiles = { + modname .. "_tree_top.png", + modname .. "_tree_top.png", + modname .. "_tree_side.png" + }, + paramtype = "light", + groups = { + firestick = 1, + snappy = 1, + flammable = 2, + falling_repose = 1 + } + }) + +nodecore.register_leaf_drops(function(pos, node, list) + list[#list + 1] = { + name = modname .. ":stick", + prob = 0.2 * (node.param2 * node.param2)} + end) diff --git a/mods/nc_tree/textures/nc_tree_eggcorn.png b/mods/nc_tree/textures/nc_tree_eggcorn.png new file mode 100644 index 00000000..479b515e Binary files /dev/null and b/mods/nc_tree/textures/nc_tree_eggcorn.png differ diff --git a/mods/nc_tree/textures/nc_tree_leaves.png b/mods/nc_tree/textures/nc_tree_leaves.png new file mode 100644 index 00000000..b9a07c53 Binary files /dev/null and b/mods/nc_tree/textures/nc_tree_leaves.png differ diff --git a/mods/nc_tree/textures/nc_tree_leaves_dry.png b/mods/nc_tree/textures/nc_tree_leaves_dry.png new file mode 100644 index 00000000..ab91dff2 Binary files /dev/null and b/mods/nc_tree/textures/nc_tree_leaves_dry.png differ diff --git a/mods/nc_tree/textures/nc_tree_roots.png b/mods/nc_tree/textures/nc_tree_roots.png new file mode 100644 index 00000000..b3f0e1eb Binary files /dev/null and b/mods/nc_tree/textures/nc_tree_roots.png differ diff --git a/mods/nc_tree/textures/nc_tree_tree_side.png b/mods/nc_tree/textures/nc_tree_tree_side.png new file mode 100644 index 00000000..a874efab Binary files /dev/null and b/mods/nc_tree/textures/nc_tree_tree_side.png differ diff --git a/mods/nc_tree/textures/nc_tree_tree_top.png b/mods/nc_tree/textures/nc_tree_tree_top.png new file mode 100644 index 00000000..a92907a7 Binary files /dev/null and b/mods/nc_tree/textures/nc_tree_tree_top.png differ diff --git a/mods/nc_woodwork/adze.lua b/mods/nc_woodwork/adze.lua new file mode 100644 index 00000000..c8c4ef84 --- /dev/null +++ b/mods/nc_woodwork/adze.lua @@ -0,0 +1,29 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_tool(modname .. ":adze", { + description = "Wooden Adze", + inventory_image = modname .. "_adze.png", + groups = { + flammable = 2 + }, + tool_capabilities = nodecore.toolcaps({ + choppy = 1 + }) + }) + +nodecore.register_craft({ + label = "assemble wood adze", + normal = {y = 1}, + nodes = { + {match = "nc_tree:stick", replace = "air"}, + {y = -1, match = modname .. ":staff", replace = "air"}, + }, + items = { + {y = -1, name = modname .. ":adze"} + } + }) diff --git a/mods/nc_woodwork/depends.txt b/mods/nc_woodwork/depends.txt new file mode 100644 index 00000000..a4266bdd --- /dev/null +++ b/mods/nc_woodwork/depends.txt @@ -0,0 +1,3 @@ +nc_api +nc_api_craft +nc_tree \ No newline at end of file diff --git a/mods/nc_woodwork/init.lua b/mods/nc_woodwork/init.lua new file mode 100644 index 00000000..d7e907db --- /dev/null +++ b/mods/nc_woodwork/init.lua @@ -0,0 +1,14 @@ +-- LUALOCALS < --------------------------------------------------------- +local include, minetest + = include, minetest +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() +local path = minetest.get_modpath(modname) + +include("adze") +include("plank") +include("staff") +include("tools") +include("ladder") +include("shelf") diff --git a/mods/nc_woodwork/ladder.lua b/mods/nc_woodwork/ladder.lua new file mode 100644 index 00000000..9393332b --- /dev/null +++ b/mods/nc_woodwork/ladder.lua @@ -0,0 +1,70 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local lt = 1/16 +local lw = 3/16 +local ll = 1/2 + +local tt = "nc_tree_tree_side.png^(nc_tree_tree_top.png^[mask:nc_woodwork_ladder_mask.png)" + +minetest.register_node(modname .. ":ladder", { + description = "Wooden Ladder", + drawtype = "nodebox", + node_box = nodecore.fixedbox( + {-lt, -ll, -lt, lt, ll, lt}, + {-lw, -lt, -lt, lw, lt, lt}, + {-lt, -lt, -lw, lt, lt, lw} + ), + tiles = {tt}, + groups = { + snappy = 1, + flammable = 2, + fire_fuel = 1, + falling_node = 1 + }, + crush_damage = 0.25, + paramtype = "light", + sunlight_propagates = true, + climbable = true + }) + +nodecore.register_craft({ + label = "assemble wood ladder", + normal = {x = 1}, + nodes = { + {match = "nc_tree:stick", replace = "air"}, + {x = -1, match = modname .. ":staff", replace = modname .. ":ladder"}, + } + }) + +minetest.register_node(modname .. ":frame", { + description = "Wooden Frame", + drawtype = "nodebox", + node_box = nodecore.fixedbox( + {-lt, -ll, -lt, lt, ll, lt}, + {-ll, -lt, -lt, ll, lt, lt}, + {-lt, -lt, -ll, lt, lt, ll} + ), + tiles = {tt}, + groups = { + snappy = 1, + flammable = 2, + fire_fuel = 1 + }, + paramtype = "light", + climbable = true, + sunlight_propagates = true, + }) + +nodecore.register_craft({ + label = "assemble wood frame", + normal = {x = 1}, + nodes = { + {match = modname .. ":staff", replace = "air"}, + {x = -1, match = modname .. ":staff", replace = modname .. ":frame"}, + } + }) diff --git a/mods/nc_woodwork/plank.lua b/mods/nc_woodwork/plank.lua new file mode 100644 index 00000000..531c320e --- /dev/null +++ b/mods/nc_woodwork/plank.lua @@ -0,0 +1,43 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local plank = modname .. ":plank" +minetest.register_node(plank, { + description = "Wooden Plank", + tiles = { modname .. "_plank.png" }, + groups = { + choppy = 1, + flammable = 2, + fire_fuel = 5 + } + }) + +nodecore.register_craft({ + label = "split tree to planks", + action = "pummel", + toolgroups = {choppy = 1}, + normal = {y = 1}, + nodes = { + {match = "nc_tree:tree", replace = "air"} + }, + items = { + {name = plank, count = 4, scatter = 5} + } + }) + +nodecore.register_craft({ + label = "bash planks to sticks", + action = "pummel", + toolgroups = {thumpy = 3}, + normal = {y = 1}, + nodes = { + {match = plank, replace = "air"} + }, + items = { + {name = "nc_tree:stick", count = 4, scatter = 5} + } + }) diff --git a/mods/nc_woodwork/shelf.lua b/mods/nc_woodwork/shelf.lua new file mode 100644 index 00000000..c113e9be --- /dev/null +++ b/mods/nc_woodwork/shelf.lua @@ -0,0 +1,85 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local side = "nc_tree_tree_side.png" +local top = side .. "^(" .. modname .. "_plank.png^[mask:" +.. modname .. "_shelf.png)" + +minetest.register_node(modname .. ":shelf", { + description = "Wooden Shelf", + drawtype = "nodebox", + node_box = nodecore.fixedbox( + {-0.5, -0.5, -0.5, 0.5, -7/16, 0.5}, + {-0.5, 7/16, -0.5, 0.5, 0.5, 0.5}, + {-0.5, -7/16, -0.5, -7/16, 7/16, -7/16}, + {-0.5, -7/16, 7/16, -7/16, 7/16, 0.5}, + {7/16, -7/16, -0.5, 0.5, 7/16, -7/16}, + {7/16, -7/16, 7/16, 0.5, 7/16, 0.5} + ), + selection_box = nodecore.fixedbox( + {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5} + ), + tiles = { top, top, side }, + groups = { + choppy = 1, + visinv = 1, + flammable = 2, + fire_fuel = 3, + eject_inv_on_burn = 1, + totable = 1 + }, + paramtype = "light", + on_construct = function(pos) + local inv = minetest.get_meta(pos):get_inventory() + inv:set_size("solo", 1) + nodecore.visinv_update_ents(pos) + end, + on_rightclick = function(pos, node, clicker, stack, pointed_thing) + if not nodecore.interact(clicker) then return end + if pointed_thing.above.y ~= pointed_thing.under.y then return end + if not stack or stack:is_empty() then return end + local def = minetest.registered_items[stack:get_name()] + if (not def) or (def.groups and def.groups.visinv) then + return minetest.item_place_node(stack, clicker, pointed_thing) + end + return nodecore.stack_add(pos, stack) + end, + on_punch = function(pos, node, puncher, pointed_thing) + if not nodecore.interact(puncher) then return end + if pointed_thing.above.y ~= pointed_thing.under.y then return end + return nodecore.stack_giveto(pos, puncher) + end, + on_dig = function(pos, node, digger, ...) + if nodecore.stack_giveto(pos, digger) then + return minetest.node_dig(pos, node, digger, ...) + end + end + }) + +nodecore.register_craft({ + label = "assemble wood shelf", + norotate = true, + nodes = { + {match = modname .. ":plank", replace = "air"}, + {x = -1, z = -1, match = modname .. ":frame", replace = modname .. ":shelf"}, + {x = 1, z = -1, match = modname .. ":frame", replace = modname .. ":shelf"}, + {x = -1, z = 1, match = modname .. ":frame", replace = modname .. ":shelf"}, + {x = 1, z = 1, match = modname .. ":frame", replace = modname .. ":shelf"}, + } + }) + +nodecore.register_craft({ + label = "assemble wood shelf", + norotate = true, + nodes = { + {match = modname .. ":plank", replace = "air"}, + {x = 0, z = -1, match = modname .. ":frame", replace = modname .. ":shelf"}, + {x = 0, z = 1, match = modname .. ":frame", replace = modname .. ":shelf"}, + {x = -1, z = 0, match = modname .. ":frame", replace = modname .. ":shelf"}, + {x = 1, z = 0, match = modname .. ":frame", replace = modname .. ":shelf"}, + } + }) diff --git a/mods/nc_woodwork/staff.lua b/mods/nc_woodwork/staff.lua new file mode 100644 index 00000000..346b9c46 --- /dev/null +++ b/mods/nc_woodwork/staff.lua @@ -0,0 +1,34 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +minetest.register_node(modname .. ":staff", { + description = "Staff", + drawtype = "nodebox", + node_box = nodecore.fixedbox(-1/16, -0.5, -1/16, 1/16, 0.5, 1/16), + oldnames = {"nc_tree:staff"}, + tiles = { + "nc_tree_tree_top.png", + "nc_tree_tree_top.png", + "nc_tree_tree_side.png" + }, + paramtype = "light", + groups = { + firestick = 2, + snappy = 1, + flammable = 2, + falling_repose = 2 + } + }) + +nodecore.register_craft({ + label = "assemble staff", + normal = {y = 1}, + nodes = { + {match = "nc_tree:stick", replace = "air"}, + {y = -1, match = "nc_tree:stick", replace = modname .. ":staff"} + } + }) diff --git a/mods/nc_woodwork/textures/nc_woodwork_adze.png b/mods/nc_woodwork/textures/nc_woodwork_adze.png new file mode 100644 index 00000000..0a3efcb0 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_adze.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_ladder_mask.png b/mods/nc_woodwork/textures/nc_woodwork_ladder_mask.png new file mode 100644 index 00000000..5b9e5011 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_ladder_mask.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_plank.png b/mods/nc_woodwork/textures/nc_woodwork_plank.png new file mode 100644 index 00000000..2062d7b4 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_plank.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_shelf.png b/mods/nc_woodwork/textures/nc_woodwork_shelf.png new file mode 100644 index 00000000..f8540285 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_shelf.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_tool_hatchet.png b/mods/nc_woodwork/textures/nc_woodwork_tool_hatchet.png new file mode 100644 index 00000000..2a2c8e80 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_tool_hatchet.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_tool_mallet.png b/mods/nc_woodwork/textures/nc_woodwork_tool_mallet.png new file mode 100644 index 00000000..7a44e577 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_tool_mallet.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_tool_pick.png b/mods/nc_woodwork/textures/nc_woodwork_tool_pick.png new file mode 100644 index 00000000..b4a8baf3 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_tool_pick.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_tool_spade.png b/mods/nc_woodwork/textures/nc_woodwork_tool_spade.png new file mode 100644 index 00000000..c1f9896e Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_tool_spade.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_toolhead_hatchet.png b/mods/nc_woodwork/textures/nc_woodwork_toolhead_hatchet.png new file mode 100644 index 00000000..1bfb48ea Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_toolhead_hatchet.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_toolhead_mallet.png b/mods/nc_woodwork/textures/nc_woodwork_toolhead_mallet.png new file mode 100644 index 00000000..63500645 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_toolhead_mallet.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_toolhead_pick.png b/mods/nc_woodwork/textures/nc_woodwork_toolhead_pick.png new file mode 100644 index 00000000..af2b04e1 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_toolhead_pick.png differ diff --git a/mods/nc_woodwork/textures/nc_woodwork_toolhead_spade.png b/mods/nc_woodwork/textures/nc_woodwork_toolhead_spade.png new file mode 100644 index 00000000..11613699 Binary files /dev/null and b/mods/nc_woodwork/textures/nc_woodwork_toolhead_spade.png differ diff --git a/mods/nc_woodwork/tools.lua b/mods/nc_woodwork/tools.lua new file mode 100644 index 00000000..e44d8be8 --- /dev/null +++ b/mods/nc_woodwork/tools.lua @@ -0,0 +1,71 @@ +-- LUALOCALS < --------------------------------------------------------- +local minetest, nodecore + = minetest, nodecore +-- LUALOCALS > --------------------------------------------------------- + +local modname = minetest.get_current_modname() + +local function toolhead(name, from, group, sticks) + local n + if name then + n = modname .. ":toolhead_" .. name:lower() + local t = n:gsub(":", "_") .. ".png" + minetest.register_craftitem(n, { + description = "Wooden " .. name .. " Head", + inventory_image = t, + stack_max = 1, + groups = { + choppy = 1, + flammable = 2 + } + }) + local m = modname .. ":tool_" .. name:lower() + local u = m:gsub(":", "_") .. ".png" + minetest.register_tool(m, { + description = "Wooden " .. name, + inventory_image = u, + groups = { + flammable = 2 + }, + tool_capabilities = nodecore.toolcaps({ + [group] = 2 + }) + }) + nodecore.register_craft({ + label = "assemble wood " .. name:lower(), + normal = {y = 1}, + nodes = { + {match = n, replace = "air"}, + {y = -1, match = modname .. ":staff", replace = "air"}, + }, + items = { + {y = -1, name = m}, + } + }) + end + + nodecore.register_craft({ + label = "carve " .. from, + action = "pummel", + toolgroups = {choppy = 1}, + nodes = { + {match = from, replace = "air"} + }, + items = { + n and {name = n} or nil, + sticks and {name = "nc_tree:stick", + count = sticks, scatter = 5} or nil + } + }) +end + +toolhead("Mallet", modname .. ":plank", + "thumpy", 2) +toolhead("Spade", modname .. ":toolhead_mallet", + "crumbly", 1) +toolhead("Hatchet", modname .. ":toolhead_spade", + "choppy", 1) +toolhead("Pick", modname .. ":toolhead_hatchet", + "cracky", 2) +toolhead(nil, modname.. ":toolhead_pick", + nil, 2)