From aa3739528b638bb8e6544119714a8a620bd9e889 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 11 Feb 2019 21:27:17 +0100 Subject: [PATCH 001/798] Fix weird arguments given to is_protected --- mods/ITEMS/mcl_core/nodes_base.lua | 6 +++++- mods/ITEMS/mcl_fire/fire_charge.lua | 4 ++-- mods/ITEMS/mcl_fire/flint_and_steel.lua | 4 ++-- mods/ITEMS/mcl_fire/init.lua | 18 +++++++++++++++--- mods/ITEMS/mcl_nether/init.lua | 7 ++++++- mods/ITEMS/mcl_tnt/init.lua | 2 +- 6 files changed, 31 insertions(+), 10 deletions(-) diff --git a/mods/ITEMS/mcl_core/nodes_base.lua b/mods/ITEMS/mcl_core/nodes_base.lua index f2299e07..237eff39 100644 --- a/mods/ITEMS/mcl_core/nodes_base.lua +++ b/mods/ITEMS/mcl_core/nodes_base.lua @@ -652,7 +652,11 @@ minetest.register_node("mcl_core:bedrock", { local dim = mcl_worlds.pos_to_dimension(pos) local flame_pos = {x = pos.x, y = pos.y + 1, z = pos.z} local fn = minetest.get_node(flame_pos) - if dim == "end" and fn.name == "air" and not minetest.is_protected(flame_pos, "fire") and pointed_thing.under.y < pointed_thing.above.y then + local pname = player:get_player_name() + if minetest.is_protected(flame_pos, pname) then + return minetest.record_protection_violation(flame_pos, pname) + end + if dim == "end" and fn.name == "air" and pointed_thing.under.y < pointed_thing.above.y then minetest.set_node(flame_pos, {name = "mcl_fire:eternal_fire"}) return true else diff --git a/mods/ITEMS/mcl_fire/fire_charge.lua b/mods/ITEMS/mcl_fire/fire_charge.lua index 26259e04..d35ffae9 100644 --- a/mods/ITEMS/mcl_fire/fire_charge.lua +++ b/mods/ITEMS/mcl_fire/fire_charge.lua @@ -28,10 +28,10 @@ minetest.register_craftitem("mcl_fire:fire_charge", { if nodedef and nodedef._on_ignite then local overwrite = nodedef._on_ignite(user, pointed_thing) if not overwrite then - mcl_fire.set_fire(pointed_thing) + mcl_fire.set_fire(pointed_thing, user) end else - mcl_fire.set_fire(pointed_thing) + mcl_fire.set_fire(pointed_thing, user) end if not minetest.settings:get_bool("creative_mode") then itemstack:take_item() diff --git a/mods/ITEMS/mcl_fire/flint_and_steel.lua b/mods/ITEMS/mcl_fire/flint_and_steel.lua index 0ab4043f..7cb114ad 100644 --- a/mods/ITEMS/mcl_fire/flint_and_steel.lua +++ b/mods/ITEMS/mcl_fire/flint_and_steel.lua @@ -33,10 +33,10 @@ minetest.register_tool("mcl_fire:flint_and_steel", { if nodedef and nodedef._on_ignite then local overwrite = nodedef._on_ignite(user, pointed_thing) if not overwrite then - mcl_fire.set_fire(pointed_thing) + mcl_fire.set_fire(pointed_thing, user) end else - mcl_fire.set_fire(pointed_thing) + mcl_fire.set_fire(pointed_thing, user) end used = true end diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 665b13de..7743d30d 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -393,10 +393,22 @@ else -- Fire enabled end --- Set pointed_thing on (normal) fire -mcl_fire.set_fire = function(pointed_thing) +-- Set pointed_thing on (normal) fire. +-- * pointed_thing: Pointed thing to ignite +-- * player: Player who sets fire or nil if nobody +mcl_fire.set_fire = function(pointed_thing, player) + local pname + if player == nil then + pname = "" + else + pname = player:get_player_name() + end local n = minetest.get_node(pointed_thing.above) - if n.name == "air" and not minetest.is_protected(pointed_thing.above, "fire") then + if minetest.is_protected(pointed_thing.above, pname) then + minetest.record_protection_violation(pointed_thing.above, pname) + return + end + if n.name == "air" then minetest.add_node(pointed_thing.above, {name="mcl_fire:fire"}) end end diff --git a/mods/ITEMS/mcl_nether/init.lua b/mods/ITEMS/mcl_nether/init.lua index b0bb771a..4fc36a8b 100644 --- a/mods/ITEMS/mcl_nether/init.lua +++ b/mods/ITEMS/mcl_nether/init.lua @@ -46,7 +46,12 @@ local eternal_on_ignite = function(player, pointed_thing) local pos = pointed_thing.under local flame_pos = {x = pos.x, y = pos.y + 1, z = pos.z} local fn = minetest.get_node(flame_pos) - if fn.name == "air" and not minetest.is_protected(flame_pos, "fire") and pointed_thing.under.y < pointed_thing.above.y then + local pname = player:get_player_name() + if minetest.is_protected(flame_pos, pname) then + minetest.record_protection_violation(flame_pos, pname) + return + end + if fn.name == "air" and pointed_thing.under.y < pointed_thing.above.y then minetest.set_node(flame_pos, {name = "mcl_fire:eternal_fire"}) return true else diff --git a/mods/ITEMS/mcl_tnt/init.lua b/mods/ITEMS/mcl_tnt/init.lua index 39df3848..dd6ebc66 100644 --- a/mods/ITEMS/mcl_tnt/init.lua +++ b/mods/ITEMS/mcl_tnt/init.lua @@ -204,7 +204,7 @@ tnt.boom = function(pos, info) end minetest.sound_play(sound, {pos = pos,gain = 1.0,max_hear_distance = 16,}) local node = minetest.get_node(pos) - if minetest.get_item_group("water") == 1 or minetest.get_item_group("lava") == 1 or minetest.is_protected(pos, "tnt") then + if minetest.get_item_group("water") == 1 or minetest.get_item_group("lava") == 1 then -- Cancel the Explosion return end From 46a6d9bb8e616ddeae42c6ccc0dc30fb1ed1df11 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 18 Feb 2019 20:14:30 +0100 Subject: [PATCH 002/798] Signs: Remove dead code --- mods/ITEMS/mcl_signs/init.lua | 6 ------ 1 file changed, 6 deletions(-) diff --git a/mods/ITEMS/mcl_signs/init.lua b/mods/ITEMS/mcl_signs/init.lua index 29c23f52..541e8114 100644 --- a/mods/ITEMS/mcl_signs/init.lua +++ b/mods/ITEMS/mcl_signs/init.lua @@ -361,9 +361,6 @@ minetest.register_node("mcl_signs:wall_sign", { return itemstack end, on_destruct = destruct_sign, - on_receive_fields = function(pos, formname, fields, sender) - update_sign(pos, fields, sender) - end, on_punch = function(pos, node, puncher) update_sign(pos) end, @@ -398,9 +395,6 @@ local ssign = { sounds = node_sounds, on_destruct = destruct_sign, - on_receive_fields = function(pos, formname, fields, sender) - update_sign(pos, fields, sender) - end, on_punch = function(pos, node, puncher) update_sign(pos) end, From d0e38623562b56cfa7913ea78eb39b372700bc92 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 18 Feb 2019 20:52:44 +0100 Subject: [PATCH 003/798] Fix bad wall sign text update after punch --- mods/ITEMS/mcl_signs/init.lua | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_signs/init.lua b/mods/ITEMS/mcl_signs/init.lua index 541e8114..608d1922 100644 --- a/mods/ITEMS/mcl_signs/init.lua +++ b/mods/ITEMS/mcl_signs/init.lua @@ -157,6 +157,19 @@ local function get_rotation_level(facedir, nodename) return rl end +local function get_wall_signtext_info(param2, nodename) + local dir = minetest.wallmounted_to_dir(param2) + if dir.x > 0 then + return 2 + elseif dir.z > 0 then + return 1 + elseif dir.x < 0 then + return 4 + else + return 3 + end +end + local sign_groups = {handy=1,axey=1, flammable=1, deco_block=1, material_wood=1, attached_node=1} local destruct_sign = function(pos) @@ -186,7 +199,7 @@ local update_sign = function(pos, fields, sender) for _, v in ipairs(objects) do local ent = v:get_luaentity() if ent and ent.name == "mcl_signs:text" then - v:set_properties({textures={generate_texture(create_lines(text), v:get_luaentity()._signnodename)}}) + v:set_properties({textures={generate_texture(create_lines(text), ent._signnodename)}}) return end end @@ -198,7 +211,7 @@ local update_sign = function(pos, fields, sender) if nn == "mcl_signs:standing_sign" or nn == "mcl_signs:standing_sign22_5" or nn == "mcl_signs:standing_sign45" or nn == "mcl_signs:standing_sign67_5" then sign_info = signtext_info_standing[get_rotation_level(n.param2, nn) + 1] elseif nn == "mcl_signs:wall_sign" then - sign_info = signtext_info_wall[n.param2 + 1] + sign_info = signtext_info_wall[get_wall_signtext_info(n.param2)] end if sign_info == nil then return @@ -215,6 +228,7 @@ local update_sign = function(pos, fields, sender) sign_info.yaw = sign_info.yaw + 3 * (math.pi / 8) end text_entity:get_luaentity()._signnodename = nn + text_entity:set_properties({textures={generate_texture(create_lines(text), nn)}}) text_entity:setyaw(sign_info.yaw) end From 37b9a8f6bf70ab3fad990b02e468530726c7262b Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 18 Feb 2019 20:59:25 +0100 Subject: [PATCH 004/798] Fix incorrect text rotation when sign text updates --- mods/ITEMS/mcl_signs/init.lua | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mods/ITEMS/mcl_signs/init.lua b/mods/ITEMS/mcl_signs/init.lua index 608d1922..1b1ede29 100644 --- a/mods/ITEMS/mcl_signs/init.lua +++ b/mods/ITEMS/mcl_signs/init.lua @@ -220,13 +220,6 @@ local update_sign = function(pos, fields, sender) x = pos.x + sign_info.delta.x, y = pos.y + sign_info.delta.y, z = pos.z + sign_info.delta.z}, "mcl_signs:text") - if nn == "mcl_signs:standing_sign22_5" then - sign_info.yaw = sign_info.yaw + math.pi / 8 - elseif nn == "mcl_signs:standing_sign45" then - sign_info.yaw = sign_info.yaw + 2 * (math.pi / 8) - elseif nn == "mcl_signs:standing_sign67_5" then - sign_info.yaw = sign_info.yaw + 3 * (math.pi / 8) - end text_entity:get_luaentity()._signnodename = nn text_entity:set_properties({textures={generate_texture(create_lines(text), nn)}}) From 198adcc7404725138d19c7f1f20889392abc8f36 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 18 Feb 2019 21:29:11 +0100 Subject: [PATCH 005/798] Respawn sign text on load --- mods/ITEMS/mcl_signs/init.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_signs/init.lua b/mods/ITEMS/mcl_signs/init.lua index 1b1ede29..c9763379 100644 --- a/mods/ITEMS/mcl_signs/init.lua +++ b/mods/ITEMS/mcl_signs/init.lua @@ -427,7 +427,7 @@ local ssign67 = table.copy(ssign) ssign67.mesh = "mcl_signs_sign67.5.obj" minetest.register_node("mcl_signs:standing_sign67_5", ssign67) - +-- FIXME: Prevent entity destruction by /clearobjects minetest.register_entity("mcl_signs:text", { collisionbox = { 0, 0, 0, 0, 0, 0 }, visual = "upright_sprite", @@ -484,6 +484,15 @@ end minetest.register_alias("signs:sign_wall", "mcl_signs:wall_sign") minetest.register_alias("signs:sign_yard", "mcl_signs:standing_sign") +minetest.register_lbm({ + name = "mcl_signs:respawn_entities", + label = "Respawn sign text entities", + run_at_every_load = true, + nodenames = { "mcl_signs:wall_sign", "mcl_signs:standing_sign", "mcl_signs:standing_sign22_5", "mcl_signs:standing_sign45", "mcl_signs:standing_sign67_5" }, + action = function(pos, node) + update_sign(pos) + end, +}) if minetest.settings:get_bool("log_mods") then minetest.log("action", "[mcl_signs] loaded") From eb7c8371ac07c8c3de3bb4abea1f1910e8f873e5 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 18 Feb 2019 21:58:31 +0100 Subject: [PATCH 006/798] Respawn itemframe entities on load --- mods/ITEMS/mcl_itemframes/init.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mods/ITEMS/mcl_itemframes/init.lua b/mods/ITEMS/mcl_itemframes/init.lua index 14170e3b..d4d39ab8 100644 --- a/mods/ITEMS/mcl_itemframes/init.lua +++ b/mods/ITEMS/mcl_itemframes/init.lua @@ -221,4 +221,14 @@ minetest.register_lbm({ end, }) +minetest.register_lbm({ + label = "Respawn item frame item entities", + name = "mcl_itemframes:respawn_entities", + nodenames = {"mcl_itemframes:item_frame"}, + run_at_every_load = true, + action = function(pos, node) + update_item_entity(pos, node) + end, +}) + minetest.register_alias("itemframes:frame", "mcl_itemframes:item_frame") From 267a697fab032188d2ff7f9947a1da5e95b117da Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 18 Feb 2019 23:32:18 +0100 Subject: [PATCH 007/798] Banners: Respawn entity if it got lost Entity is respawned on load (in an LBM) or when the banner node is punched. Also, the banner drop is now handled in the node instead of the entity. --- mods/ITEMS/mcl_banners/init.lua | 192 ++++++++++++++++++++++---------- 1 file changed, 134 insertions(+), 58 deletions(-) diff --git a/mods/ITEMS/mcl_banners/init.lua b/mods/ITEMS/mcl_banners/init.lua index 71f0ac02..5bab2ed7 100644 --- a/mods/ITEMS/mcl_banners/init.lua +++ b/mods/ITEMS/mcl_banners/init.lua @@ -32,6 +32,11 @@ mcl_banners.colors = { ["unicolor_light_blue"] = {"light_blue", "Light Blue Banner", "mcl_wool:light_blue", "#4040CF", "mcl_dye:lightblue", "Light Blue" }, } +local colors_reverse = {} +for k,v in pairs(mcl_banners.colors) do + colors_reverse["mcl_banners:banner_item_"..v[1]] = k +end + -- Add pattern/emblazoning crafting recipes dofile(minetest.get_modpath("mcl_banners").."/patterncraft.lua") @@ -42,28 +47,40 @@ local layer_ratio = 255 local standing_banner_entity_offset = { x=0, y=-0.499, z=0 } local hanging_banner_entity_offset = { x=0, y=-1.7, z=0 } -local on_destruct_standing_banner = function(pos) +local on_destruct_banner = function(pos, hanging) + local offset, nodename + if hanging then + offset = hanging_banner_entity_offset + nodename = "mcl_banners:hanging_banner" + else + offset = standing_banner_entity_offset + nodename = "mcl_banners:standing_banner" + end -- Find this node's banner entity and make it drop as an item - local checkpos = vector.add(pos, standing_banner_entity_offset) + local checkpos = vector.add(pos, offset) local objects = minetest.get_objects_inside_radius(checkpos, 0.5) for _, v in ipairs(objects) do local ent = v:get_luaentity() - if ent and ent.name == "mcl_banners:standing_banner" then - v:get_luaentity():_drop() + if ent and ent.name == nodename then + v:remove() end end + -- Drop item + local meta = minetest.get_meta(pos) + local item = meta:get_inventory():get_stack("banner", 1) + if not item:is_empty() then + minetest.add_item(pos, item) + else + minetest.add_item(pos, "mcl_banners:banner_item_white") + end +end + +local on_destruct_standing_banner = function(pos) + return on_destruct_banner(pos, false) end local on_destruct_hanging_banner = function(pos) - -- Find this node's banner entity and make it drop as an item - local checkpos = vector.add(pos, hanging_banner_entity_offset) - local objects = minetest.get_objects_inside_radius(checkpos, 0.5) - for _, v in ipairs(objects) do - local ent = v:get_luaentity() - if ent and ent.name == "mcl_banners:hanging_banner" then - v:get_luaentity():_drop() - end - end + return on_destruct_banner(pos, true) end local make_banner_texture = function(base_color, layers) @@ -96,6 +113,59 @@ local make_banner_texture = function(base_color, layers) end end +local spawn_banner_entity = function(pos, hanging, itemstack) + local banner + if hanging then + banner = minetest.add_entity(pos, "mcl_banners:hanging_banner") + else + banner = minetest.add_entity(pos, "mcl_banners:standing_banner") + end + if banner == nil then + return banner + end + local imeta = itemstack:get_meta() + local layers_raw = imeta:get_string("layers") + local layers = minetest.deserialize(layers_raw) + local colorid = colors_reverse[itemstack:get_name()] + banner:get_luaentity():_set_textures(colorid, layers) + local mname = imeta:get_string("name") + if mname ~= nil and mname ~= "" then + banner:get_luaentity()._item_name = mname + banner:get_luaentity()._item_description = imeta:get_string("description") + end + + return banner +end + +local respawn_banner_entity = function(pos, node) + local hanging = node.name == "mcl_banners:hanging_banner" + local offset + if hanging then + offset = hanging_banner_entity_offset + else + offset = standing_banner_entity_offset + end + -- Check if a banner entity already exists + local bpos = vector.add(pos, offset) + local objects = minetest.get_objects_inside_radius(bpos, 0.5) + for _, v in ipairs(objects) do + local ent = v:get_luaentity() + if ent and (ent.name == "mcl_banners:standing_banner" or ent.name == "mcl_banners:hanging_banner") then + return + end + end + -- Spawn new entity + local meta = minetest.get_meta(pos) + local banner_item = meta:get_inventory():get_stack("banner", 1) + local banner_entity = spawn_banner_entity(bpos, hanging, banner_item) + + -- Set rotation + local final_yaw + local rotation_level = meta:get_int("rotation_level") + final_yaw = (rotation_level * (math.pi/8)) + math.pi + banner_entity:set_yaw(final_yaw) +end + local on_rotate if minetest.get_modpath("screwdriver") then on_rotate = screwdriver.disallow @@ -138,6 +208,9 @@ minetest.register_node("mcl_banners:standing_banner", { drop = "", -- Item drops are handled in entity code on_destruct = on_destruct_standing_banner, + on_punch = function(pos, node) + respawn_banner_entity(pos, node) + end, _mcl_hardness = 1, _mcl_blast_resistance = 5, }) @@ -166,6 +239,9 @@ minetest.register_node("mcl_banners:hanging_banner", { drop = "", -- Item drops are handled in entity code on_destruct = on_destruct_hanging_banner, + on_punch = function(pos, node) + respawn_banner_entity(pos, node) + end, _mcl_hardness = 1, _mcl_blast_resistance = 5, on_rotate = on_rotate, @@ -269,48 +345,61 @@ for colorid, colortab in pairs(mcl_banners.colors) do end hanging = true end - local place_pos if minetest.registered_nodes[node_under.name].buildable_to then place_pos = under else place_pos = above end - if hanging then - place_pos = vector.add(place_pos, hanging_banner_entity_offset) - else - place_pos = vector.add(place_pos, standing_banner_entity_offset) + local bnode = minetest.get_node(place_pos) + if bnode.name ~= "mcl_banners:standing_banner" and bnode.name ~= "mcl_banners:hanging_banner" then + minetest.log("error", "[mcl_banners] The placed banner node is not what the mod expected!") + return itemstack end + local meta = minetest.get_meta(place_pos) + local inv = meta:get_inventory() + inv:set_size("banner", 1) + local store_stack = ItemStack(itemstack) + store_stack:set_count(1) + inv:set_stack("banner", 1, store_stack) - local banner + -- Spawn entity + local entity_place_pos if hanging then - banner = minetest.add_entity(place_pos, "mcl_banners:hanging_banner") + entity_place_pos = vector.add(place_pos, hanging_banner_entity_offset) else - banner = minetest.add_entity(place_pos, "mcl_banners:standing_banner") + entity_place_pos = vector.add(place_pos, standing_banner_entity_offset) end - local imeta = itemstack:get_meta() - local layers_raw = imeta:get_string("layers") - local layers = minetest.deserialize(layers_raw) - banner:get_luaentity():_set_textures(colorid, layers) - local mname = imeta:get_string("name") - if mname ~= nil and mname ~= "" then - banner:get_luaentity()._item_name = mname - banner:get_luaentity()._item_description = imeta:get_string("description") - end - + local banner_entity = spawn_banner_entity(entity_place_pos, hanging, itemstack) -- Set rotation - local final_yaw + local final_yaw, rotation_level if hanging then local pdir = vector.direction(pointed_thing.under, pointed_thing.above) final_yaw = minetest.dir_to_yaw(pdir) + if pdir.x > 0 then + rotation_level = 4 + elseif pdir.z > 0 then + rotation_level = 8 + elseif pdir.x < 0 then + rotation_level = 12 + else + rotation_level = 0 + end else -- Determine the rotation based on player's yaw local yaw = placer:get_look_horizontal() -- Select one of 16 possible rotations (0-15) - local rotation_level = round((yaw / (math.pi*2)) * 16) + rotation_level = round((yaw / (math.pi*2)) * 16) + if rotation_level >= 16 then + rotation_level = 0 + end final_yaw = (rotation_level * (math.pi/8)) + math.pi end - banner:set_yaw(final_yaw) + meta:set_int("rotation_level", rotation_level) + + if banner_entity ~= nil then + banner_entity:set_yaw(final_yaw) + end if not minetest.settings:get_bool("creative_mode") then itemstack:take_item() @@ -392,30 +481,6 @@ local entity_standing = { self.object:set_armor_groups({immortal=1}) end, - -- This is a custom function which causes the banner to be dropped as item and destroys the entity. - _drop = function(self) - local pos = self.object:get_pos() - pos.y = pos.y + 1 - - if not minetest.settings:get_bool("creative_mode") and self._base_color then - -- Spawn item - local banner = ItemStack("mcl_banners:banner_item_"..mcl_banners.colors[self._base_color][1]) - local meta = banner:get_meta() - meta:set_string("layers", minetest.serialize(self._layers)) - if self._item_name ~= nil and self._item_name ~= "" then - meta:set_string("description", self._item_description) - meta:set_string("name", self._item_name) - else - meta:set_string("description", mcl_banners.make_advanced_banner_description(banner:get_definition().description, self._layers)) - end - - minetest.add_item(pos, banner) - end - - -- Destroy entity - self.object:remove() - end, - -- Set the banner textures. This function can be used by external mods. -- Meaning of parameters: -- * self: Lua entity reference to entity. @@ -436,6 +501,17 @@ local entity_hanging = table.copy(entity_standing) entity_hanging.mesh = "amc_banner_hanging.b3d" minetest.register_entity("mcl_banners:hanging_banner", entity_hanging) +-- FIXME: Prevent entity destruction by /clearobjects +minetest.register_lbm({ + label = "Respawn banner entities", + name = "mcl_banners:respawn_entities", + run_at_every_load = true, + nodenames = {"mcl_banners:standing_banner", "mcl_banners:hanging_banner"}, + action = function(pos, node) + respawn_banner_entity(pos, node) + end, +}) + minetest.register_craft({ type = "fuel", recipe = "group:banner", From e332c64d2ada9aa5c09f8bb591c7adf17b1632c6 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 18 Feb 2019 23:55:18 +0100 Subject: [PATCH 008/798] Respawn armor entity of armor stand on load --- .../minetest-3d_armor/3d_armor_stand/init.lua | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/minetest-3d_armor/3d_armor_stand/init.lua b/mods/ITEMS/minetest-3d_armor/3d_armor_stand/init.lua index 76dcec55..02eb0bcb 100644 --- a/mods/ITEMS/minetest-3d_armor/3d_armor_stand/init.lua +++ b/mods/ITEMS/minetest-3d_armor/3d_armor_stand/init.lua @@ -125,6 +125,12 @@ minetest.register_node("3d_armor_stand:armor_stand", { minetest.record_protection_violation(pos, protname) return itemstack end + + local inv = minetest.get_inventory({type = "node", pos = pos}) + if not inv then + return itemstack + end + -- Check if player wields armor local name = itemstack:get_name() local list @@ -135,11 +141,8 @@ minetest.register_node("3d_armor_stand:armor_stand", { break end end - -- If player wields armor, put it on armor stand - local inv = minetest.get_inventory({type = "node", pos = pos}) local wielditem = clicker:get_wielded_item() - if not inv then return itemstack end if list then -- ... but only if the slot is free local single_item = ItemStack(itemstack) @@ -172,11 +175,12 @@ minetest.register_node("3d_armor_stand:armor_stand", { if taken then stand_armor:take_item() inv:set_stack("armor_" .. elements[e], 1, stand_armor) - update_entity(pos) end + update_entity(pos) return clicker:get_wielded_item() end end + update_entity(pos) return itemstack end, after_place_node = function(pos) @@ -264,6 +268,17 @@ minetest.register_entity("3d_armor_stand:armor_entity", { end, }) +-- FIXME: Armor helper entity can get destroyed by /clearobjects +minetest.register_lbm({ + label = "Respawn armor stand entities", + name = "3d_armor_stand:respawn_entities", + nodenames = {"3d_armor_stand:armor_stand"}, + run_at_every_load = true, + action = function(pos, node) + update_entity(pos, node) + end, +}) + if minetest.get_modpath("doc_identifier") ~= nil then doc.sub.identifier.register_object("3d_armor_stand:armor_entity", "nodes", "3d_armor_stand:armor_stand") end From e5c78973b008e7f35323b4039546d36102352f7d Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 19 Feb 2019 00:06:14 +0100 Subject: [PATCH 009/798] Respawn mobspawner doll on load, if gone mobspawner doll on load, if gone mobspawner doll on load, if gone mobspawner doll on load, if gone --- mods/ITEMS/mcl_mobspawners/init.lua | 31 +++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_mobspawners/init.lua b/mods/ITEMS/mcl_mobspawners/init.lua index 46cf74c7..58ec364c 100644 --- a/mods/ITEMS/mcl_mobspawners/init.lua +++ b/mods/ITEMS/mcl_mobspawners/init.lua @@ -66,6 +66,20 @@ local function set_doll_properties(doll, mob) doll:get_luaentity()._mob = mob end +local function respawn_doll(pos) + local meta = minetest.get_meta(pos) + local mob = meta:get_string("Mob") + local doll + if mob and mob ~= "" then + doll = find_doll(pos) + if not doll then + doll = spawn_doll(pos) + set_doll_properties(doll, mob) + end + end + return doll +end + --[[ Public function: Setup the spawner at pos. This function blindly assumes there's actually a spawner at pos. If not, then the results are undefined. @@ -288,6 +302,10 @@ minetest.register_node("mcl_mobspawners:spawner", { end end, + on_punch = function(pos) + respawn_doll(pos) + end, + on_timer = spawn_mobs, sounds = mcl_sounds.node_sound_metal_defaults(), @@ -328,7 +346,7 @@ end doll_def.on_step = function(self, dtime) -- Check if spawner is still present. If not, delete the entity - self.timer = self.timer + 0.01 + self.timer = self.timer + dtime local n = minetest.get_node_or_nil(self.object:get_pos()) if self.timer > 1 then if n and n.name and n.name ~= "mcl_mobspawners:spawner" then @@ -341,5 +359,14 @@ doll_def.on_punch = function(self, hitter) end minetest.register_entity("mcl_mobspawners:doll", doll_def) - +-- FIXME: Doll can get destroyed by /clearobjects +minetest.register_lbm({ + label = "Respawn mob spawner dolls", + name = "mcl_mobspawners:respawn_entities", + nodenames = { "mcl_mobspawners:spawner" }, + run_at_every_load = true, + action = function(pos, node) + respawn_doll(pos) + end, +}) From a1ce94eee5eefec7af84c79b1f6f4bcb7a4833b1 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 19 Feb 2019 00:06:47 +0100 Subject: [PATCH 010/798] Add comment to mcl_itemframes --- mods/ITEMS/mcl_itemframes/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mods/ITEMS/mcl_itemframes/init.lua b/mods/ITEMS/mcl_itemframes/init.lua index d4d39ab8..2d2fffb3 100644 --- a/mods/ITEMS/mcl_itemframes/init.lua +++ b/mods/ITEMS/mcl_itemframes/init.lua @@ -221,6 +221,7 @@ minetest.register_lbm({ end, }) +-- FIXME: Item entities can get destroyed by /clearobjects minetest.register_lbm({ label = "Respawn item frame item entities", name = "mcl_itemframes:respawn_entities", From c1bc7a8fae9ff108021e6b0ca076f7d978459d4d Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 19 Feb 2019 03:42:50 +0100 Subject: [PATCH 011/798] Chorus fruit keeps u safe from damaging blocks --- mods/ITEMS/mcl_end/chorus_plant.lua | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/mods/ITEMS/mcl_end/chorus_plant.lua b/mods/ITEMS/mcl_end/chorus_plant.lua index 58832b42..5c48654d 100644 --- a/mods/ITEMS/mcl_end/chorus_plant.lua +++ b/mods/ITEMS/mcl_end/chorus_plant.lua @@ -17,6 +17,12 @@ local chorus_flower_box = { } } +-- Helper function +local function round(num, idp) + local mult = 10^(idp or 0) + return math.floor(num * mult + 0.5) / mult +end + minetest.register_node("mcl_end:chorus_flower", { description = "Chorus Flower", _doc_items_longdesc = "A chorus flower is the living part of a chorus plant. It can grow into a tall chorus plant, step by step. When it grows, it may die on old age eventually. It also dies when it is unable to grow.", @@ -282,16 +288,16 @@ local random_teleport = function(player) for a=1, 16 do -- Teleportation box local x,y,z - x = math.random(pos.x-8, pos.x+8) + x = math.random(round(pos.x)-8, round(pos.x)+8) y = math.random(math.ceil(pos.y)-8, math.ceil(pos.y)+8) - z = math.random(pos.z-8, pos.z+8) + z = math.random(round(pos.z)-8, round(pos.z)+8) local node_cache = {} local ground_level = false -- Scan nodes from selected position until we hit ground for t=0, 16 do local tpos = {x=x, y=y-t, z=z} local tnode = minetest.get_node(tpos) - if tnode.name == "mcl_core:void" then + if tnode.name == "mcl_core:void" or tnode.name == "ignore" then break end local tdef = minetest.registered_nodes[tnode.name] @@ -304,16 +310,20 @@ local random_teleport = function(player) -- Ground found? Then let's check if the player has enough room if ground_level and #node_cache >= 1 then local streak = 0 + local last_was_walkable = true for c=#node_cache, 1, -1 do local tpos = node_cache[c].pos local tnode = node_cache[c].node local tdef = minetest.registered_nodes[tnode.name] - -- Player needs a space of 2 nodes on top of each other - if not tdef.walkable and tdef.liquidtype == "none" then - streak = streak + 1 + -- Player needs a space of 2 safe non-liquid nodes on top of a walkable node + if not tdef.walkable and tdef.liquidtype == "none" and tdef.damage_per_second <= 0 then + if (streak == 0 and last_was_walkable) or (streak > 0) then + streak = streak + 1 + end else streak = 0 end + last_was_walkable = tdef.walkable if streak >= 2 then -- JACKPOT! Now we can teleport. local goal = {x=tpos.x, y=tpos.y-1.5, z=tpos.z} @@ -347,7 +357,7 @@ end minetest.register_craftitem("mcl_end:chorus_fruit", { description = "Chorus Fruit", - _doc_items_longdesc = "A chorus fruit is an edible fruit from the chorus plant which is home to the End. Eating it teleports you to the top of a random solid block nearby, provided you won't end up inside a liquid or some solid blocks. Teleportation might fail if there are very few or no places to teleport to.", + _doc_items_longdesc = "A chorus fruit is an edible fruit from the chorus plant which is home to the End. Eating it teleports you to the top of a random solid block nearby, provided you won't end up inside a liquid, solid or harmful blocks. Teleportation might fail if there are very few or no places to teleport to.", wield_image = "mcl_end_chorus_fruit.png", inventory_image = "mcl_end_chorus_fruit.png", on_place = eat_chorus_fruit, From 38f6804a799b23706fd525a1dbd9c6e96e3317d2 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 19 Feb 2019 20:16:12 +0100 Subject: [PATCH 012/798] Update tsm_railcorridors to 0.14.0 --- mods/MAPGEN/tsm_railcorridors/README.md | 13 +- mods/MAPGEN/tsm_railcorridors/init.lua | 789 ++++++++++++------ .../MAPGEN/tsm_railcorridors/settingtypes.txt | 19 +- 3 files changed, 557 insertions(+), 264 deletions(-) diff --git a/mods/MAPGEN/tsm_railcorridors/README.md b/mods/MAPGEN/tsm_railcorridors/README.md index 9b2b745b..de9df489 100644 --- a/mods/MAPGEN/tsm_railcorridors/README.md +++ b/mods/MAPGEN/tsm_railcorridors/README.md @@ -1,11 +1,10 @@ # Railway corridors [`tsm_railcorridors`] MineClone 2 adaption. NO TREASURER SUPPORT! -* Current version 0.12.0 +* Current version 0.14.0 -Minetest mod for adding underground corridors with rails and wood constructions with -a few treasure chests now and then. Optional Treasurer support is available for adding -treasures from various mods. +Minetest mod for adding underground corridors with rails and wood constructions with a few treasure chests now and then. +Optional support for the Treasurer mod is available for adding treasures from various mods. Cobwebs are added if the `mobs_monster` mod is found. Use the advanced settings to finetune the railway corridors. @@ -13,6 +12,6 @@ Use the advanced settings to finetune the railway corridors. * Forum thread: https://forum.minetest.net/viewtopic.php?t=10339 * License: MIT License. -## Info for modders -Want to include this mod in a game, but you hate the dependencies? -You can edit the node names in gameconfig.lua to fit your needs. :-) +## Info for game makers +Want to include this mod in a game, but you have problems with the dependencies? +Edit `gameconfig.lua` to fit your needs. :-) diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index 57c18379..d6de8198 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -12,15 +12,13 @@ local P = function (float) return math.floor(32767 * float) end --- Wahrscheinlichkeit für jeden Chunk, solche Gänge mit Schienen zu bekommen --- Probability for every newly generated chunk to get corridors -local probability_railcaves_in_chunk = P(0.3) -setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_railcaves_in_chunk")) +-- Probability for every newly generated mapchunk to get corridors +local probability_railcaves_in_mapchunk = P(0.33333) +setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_railcaves_in_mapchunk")) if setting then - probability_railcaves_in_chunk = P(setting) + probability_railcaves_in_mapchunk = P(setting) end --- Innerhalb welcher Parameter soll sich die Pfadlänge bewegen? (Forks heben den Maximalwert auf) -- Minimal and maximal value of path length (forks don't look up this value) local way_min = 4; local way_max = 7; @@ -33,7 +31,6 @@ if setting then way_max = setting end --- Wahrsch. für jeden geraden Teil eines Korridors, Fackeln zu bekommen -- Probability for every horizontal part of a corridor to be with torches local probability_torches_in_segment = P(0.5) setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_torches_in_segment")) @@ -41,7 +38,6 @@ if setting then probability_torches_in_segment = P(setting) end --- Wahrsch. für jeden Teil eines Korridors, nach oben oder nach unten zu gehen -- Probability for every part of a corridor to go up or down local probability_up_or_down = P(0.2) setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_up_or_down")) @@ -49,7 +45,6 @@ if setting then probability_up_or_down = P(setting) end --- Wahrscheinlichkeit für jeden Teil eines Korridors, sich zu verzweigen – vorsicht, wenn fast jeder Gang sich verzweigt, kann der Algorithums unlösbar werden und MT hängt sich auf -- Probability for every part of a corridor to fork – caution, too high values may cause MT to hang on. local probability_fork = P(0.04) setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_fork")) @@ -57,7 +52,6 @@ if setting then probability_fork = P(setting) end --- Wahrscheinlichkeit für jeden geraden Teil eines Korridors eine Kiste zu enthalten -- Probability for every part of a corridor to contain a chest local probability_chest = P(0.05) setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_chest")) @@ -66,17 +60,11 @@ if setting then end -- Probability for every part of a corridor to contain a cart --- Disabled because cart spawning creates error message spam: --- “m_static_exists=true but static data doesn't actually exist in (x,y,z) --- TODO: Set back to 0.05 when this is fixed. --- TODO: Remove minecarts from loot table when minecarts spawn on rails. -local probability_cart = P(0) ---[[ +local probability_cart = P(0.05) setting = tonumber(minetest.settings:get("tsm_railcorridors_probability_cart")) if setting then probability_cart = P(setting) end -]] -- Probability for a rail corridor system to be damaged local probability_damage = P(1.0) @@ -111,27 +99,50 @@ local height_max = mcl_worlds.layer_to_y(60) -- Chaos Mode: If enabled, rail corridors don't stop generating when hitting obstacles local chaos_mode = minetest.settings:get_bool("tsm_railcorridors_chaos") or false --- Parameter Ende +-- End of parameters --- Random generators -local pr, webperlin_major, webperlin_minor -local pr_initialized = false +if not tsm_railcorridors.nodes.corridor_woods_function then + local accumulated_chance = 0 + for w=1, #tsm_railcorridors.nodes.corridor_woods do + accumulated_chance = accumulated_chance + tsm_railcorridors.nodes.corridor_woods[w].chance + end + assert(accumulated_chance == 1000, "Rail corridor wood chances add up to "..accumulated_chance.." per mille! (should be 1000 per mille)") +end + +-- Random Perlin noise generators +local pr, pr_carts, pr_treasures, pr_deco, webperlin_major, webperlin_minor local function InitRandomizer(seed) -- Mostly used for corridor gen. pr = PseudoRandom(seed) + -- Dirt room decorations + pr_deco = PseudoRandom(seed+25) + -- Separate randomizer for carts because spawning carts is very timing-dependent + pr_carts = PseudoRandom(seed-654) + -- Chest contents randomizer + pr_treasures = PseudoRandom(seed+777) -- Used for cobweb generation, both noises have to reach a high value for cobwebs to appear webperlin_major = PerlinNoise(934, 3, 0.6, 500) webperlin_minor = PerlinNoise(834, 3, 0.6, 50) - pr_initialized = true + pr_inited = true end +local carts_table = {} + +local dirt_room_coords + +-- Returns true if pos is inside the dirt room of the current corridor system +local function IsInDirtRoom(pos) + local min = dirt_room_coords.min + local max = dirt_room_coords.max + return pos.x >= min.x and pos.x <= max.x and pos.y >= min.y and pos.y <= max.y and pos.z >= min.z and pos.z <= max.z +end -- Checks if the mapgen is allowed to carve through this structure and only sets -- the node if it is allowed. Does never build in liquids. -- If check_above is true, don't build if the node above is attached (e.g. rail) -- or a liquid. -local function SetNodeIfCanBuild(pos, node, check_above) +local function SetNodeIfCanBuild(pos, node, check_above, can_replace_rail) if check_above then local abovename = minetest.get_node({x=pos.x,y=pos.y+1,z=pos.z}).name local abovedef = minetest.registered_nodes[abovename] @@ -144,8 +155,13 @@ local function SetNodeIfCanBuild(pos, node, check_above) end local name = minetest.get_node(pos).name local def = minetest.registered_nodes[name] - if name ~= "unknown" and name ~= "ignore" and def.is_ground_content and - (def.liquidtype == "none" or name == tsm_railcorridors.nodes.cobweb) then + if name ~= "unknown" and name ~= "ignore" and + ((def.is_ground_content and def.liquidtype == "none") or + name == tsm_railcorridors.nodes.cobweb or + name == tsm_railcorridors.nodes.torch_wall or + name == tsm_railcorridors.nodes.torch_floor or + (can_replace_rail and name == tsm_railcorridors.nodes.rail) + ) then minetest.set_node(pos, node) return true else @@ -175,8 +191,9 @@ end -- Returns true if rails are allowed to be placed on top of this node local function IsRailSurface(pos) local nodename = minetest.get_node(pos).name + local nodename_above = minetest.get_node({x=pos.x,y=pos.y+2,z=pos.z}).name local nodedef = minetest.registered_nodes[nodename] - return nodename ~= "unknown" and nodename ~= "ignore" and nodedef.walkable and (nodedef.node_box == nil or nodedef.node_box.type == "regular") + return nodename ~= "unknown" and nodename ~= "ignore" and nodedef.walkable and (nodedef.node_box == nil or nodedef.node_box.type == "regular") and nodename_above ~= tsm_railcorridors.nodes.rail end -- Checks if the node is empty space which requires to be filled by a platform @@ -184,36 +201,91 @@ local function NeedsPlatform(pos) local node = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}) local node2 = minetest.get_node({x=pos.x,y=pos.y-2,z=pos.z}) local nodedef = minetest.registered_nodes[node.name] - return node.name ~= "ignore" and node.name ~= "unknown" and nodedef.is_ground_content and ((nodedef.walkable == false and node2.name ~= tsm_railcorridors.nodes.dirt) or (nodedef.groups and nodedef.groups.falling_node)) + local falling = minetest.get_item_group(node.name, "falling_node") == 1 + return + -- Node can be replaced if ground content or rail + (node.name ~= "ignore" and node.name ~= "unknown" and nodedef.is_ground_content) and + -- Node needs platform if node below is not walkable. + -- Unless 2 nodes below there is dirt: This is a special case for the starter cube. + ((nodedef.walkable == false and node2.name ~= tsm_railcorridors.nodes.dirt) or + -- Falling nodes always need to be replaced by a platform, we want a solid and safe ground + falling), + -- second return value + falling end -- Create a cube filled with the specified nodes -- Specialties: --- * Avoids floating rails for non-solid nodes like air +-- * Avoids floating rails +-- * May cut into wood structures of the corridors (alongside with their torches) +-- Arguments: +-- * p: Center position +-- * radius: How many nodes from the center the cube will extend +-- * node: Node to set +-- * replace_air_only: If true, only air can be replaced +-- * wood, post: Wood and post nodes of the railway corridor to cut into (optional) + -- Returns true if all nodes could be set -- Returns false if setting one or more nodes failed -local function Cube(p, radius, node, replace_air_only) +local function Cube(p, radius, node, replace_air_only, wood, post) local y_top = p.y+radius local nodedef = minetest.registered_nodes[node.name] local solid = nodedef.walkable and (nodedef.node_box == nil or nodedef.node_box.type == "regular") and nodedef.liquidtype == "none" -- Check if all the nodes could be set local built_all = true - for zi = p.z-radius, p.z+radius do - for yi = y_top, p.y-radius, -1 do - for xi = p.x-radius, p.x+radius do + + -- If wood has been removed, remod + local cleanup_torches = {} + for xi = p.x-radius, p.x+radius do + for zi = p.z-radius, p.z+radius do + local column_last_attached = nil + for yi = y_top, p.y-radius, -1 do local ok = false - if not solid and yi == y_top then - local topdef = minetest.registered_nodes[minetest.get_node({x=xi,y=yi+1,z=zi}).name] - if not (topdef.groups and topdef.groups.attached_node) and topdef.liquidtype == "none" then + local thisnode = minetest.get_node({x=xi,y=yi,z=zi}) + if not solid then + if yi == y_top then + local topnode = minetest.get_node({x=xi,y=yi+1,z=zi}) + local topdef = minetest.registered_nodes[topnode.name] + if minetest.get_item_group(topnode.name, "attached_node") ~= 1 and topdef.liquidtype == "none" then + ok = true + end + elseif column_last_attached and yi == column_last_attached - 1 then + ok = false + else ok = true end + if minetest.get_item_group(thisnode.name, "attached_node") == 1 then + column_last_attached = yi + end else ok = true end local built = false if ok then if replace_air_only ~= true then - built = SetNodeIfCanBuild({x=xi,y=yi,z=zi}, node) + -- Cut into wood structures (post/wood) + if post and (xi == p.x or zi == p.z) and thisnode.name == post then + minetest.set_node({x=xi,y=yi,z=zi}, node) + built = true + elseif wood and (xi == p.x or zi == p.z) and thisnode.name == wood then + local topnode = minetest.get_node({x=xi,y=yi+1,z=zi}) + local topdef = minetest.registered_nodes[topnode.name] + if topdef.walkable and topnode.name ~= wood then + minetest.set_node({x=xi,y=yi,z=zi}, node) + -- Check for torches around the wood and schedule them + -- for removal + if node.name == "air" then + table.insert(cleanup_torches, {x=xi+1,y=yi,z=zi}) + table.insert(cleanup_torches, {x=xi-1,y=yi,z=zi}) + table.insert(cleanup_torches, {x=xi,y=yi,z=zi+1}) + table.insert(cleanup_torches, {x=xi,y=yi,z=zi-1}) + end + built = true + end + -- Set node normally + else + built = SetNodeIfCanBuild({x=xi,y=yi,z=zi}, node) + end else if minetest.get_node({x=xi,y=yi,z=zi}).name == "air" then built = SetNodeIfCanBuild({x=xi,y=yi,z=zi}, node) @@ -226,15 +298,74 @@ local function Cube(p, radius, node, replace_air_only) end end end + -- Remove torches we have detected before + for c=1, #cleanup_torches do + local check = minetest.get_node(cleanup_torches[c]) + if check.name == tsm_railcorridors.nodes.torch_wall or check.name == tsm_railcorridors.nodes.torch_floor then + minetest.set_node(cleanup_torches[c], node) + end + end return built_all end -local function Platform(p, radius, node) +local function DirtRoom(p, radius, height, dirt_mode, decorations_mode) + local y_bottom = p.y + local y_top = y_bottom + height + 1 + dirt_room_coords = { + min = { x = p.x-radius, y = y_bottom, z = p.z-radius }, + max = { x = p.x+radius, y = y_top, z = p.z+radius }, + } + local built_all = true + for xi = p.x-radius, p.x+radius do + for zi = p.z-radius, p.z+radius do + for yi = y_top, y_bottom, -1 do + local thisnode = minetest.get_node({x=xi,y=yi,z=zi}) + local built = false + if xi == p.x-radius or xi == p.x+radius or zi == p.z-radius or zi == p.z+radius or yi == y_bottom or yi == y_top then + if dirt_mode == 1 or yi == y_bottom then + built = SetNodeIfCanBuild({x=xi,y=yi,z=zi}, {name=tsm_railcorridors.nodes.dirt}) + elseif (dirt_mode == 2 or dirt_mode == 3) and yi == y_top then + if minetest.get_item_group(thisnode.name, "falling_node") == 1 then + built = SetNodeIfCanBuild({x=xi,y=yi,z=zi}, {name=tsm_railcorridors.nodes.dirt}) + end + end + else + if yi == y_bottom + 1 then + -- crazy rails + if decorations_mode == 1 then + local r = pr_deco:next(1,3) + if r == 2 then + built = SetNodeIfCanBuild({x=xi,y=yi,z=zi}, {name=tsm_railcorridors.nodes.rail}) + end + end + end + if not built then + built = SetNodeIfCanBuild({x=xi,y=yi,z=zi}, {name="air"}) + end + end + if not built then + built_all = false + end + end + end + end + return built_all +end + +local function Platform(p, radius, node, node2) + -- node2 is secondary platform material for replacing falling nodes + if not node2 then + node2 = { name = tsm_railcorridors.nodes.dirt } + end for zi = p.z-radius, p.z+radius do for xi = p.x-radius, p.x+radius do - local np = NeedsPlatform({x=xi,y=p.y,z=zi}) + local np, np2 = NeedsPlatform({x=xi,y=p.y,z=zi}) if np then - minetest.set_node({x=xi,y=p.y-1,z=zi}, node) + if np2 then + minetest.set_node({x=xi,y=p.y-1,z=zi}, node2) + else + minetest.set_node({x=xi,y=p.y-1,z=zi}, node) + end end end end @@ -253,36 +384,24 @@ local function PlaceChest(pos, param2) end -- This function checks if a cart has ACTUALLY been spawned. --- If not, it tries to spawn it again, and again, until it succeeded or --- it failed too often. -- To be calld by minetest.after. --- This is a HORRIBLE workaround thanks to the fact that minetest.add_entity is unreliable as fuck +-- This is a workaround thanks to the fact that minetest.add_entity is unreliable as fuck -- See: https://github.com/minetest/minetest/issues/4759 -- FIXME: Kill this horrible hack with fire as soon you can. local function RecheckCartHack(params) local pos = params[1] local cart_id = params[2] - local tries = params[3] - tries = tries - 1 -- Find cart for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do if obj ~= nil and obj:get_luaentity().name == cart_id then -- Cart found! We can now safely call the callback func. -- (calling it earlier has the danger of failing) + minetest.log("info", "[tsm_railcorridors] Cart spawn succeeded: "..minetest.pos_to_string(pos)) tsm_railcorridors.on_construct_cart(pos, obj) return end end - if tries <= 0 then - -- Abort if too many tries to avoid excessive function calls - return - end - -- No cart found! :-( Try again … - if minetest.get_node(pos).name == tsm_railcorridors.nodes.rail then - minetest.add_entity(pos, cart_id) - minetest.after(5, RecheckCartHack, {pos, cart_id, tries}) - end - -- The rail may have been destroyed in the meantime, that's why the node is checked. + minetest.log("info", "[tsm_railcorridors] Cart spawn FAILED: "..minetest.pos_to_string(pos)) end -- Try to place a cobweb. @@ -321,27 +440,108 @@ local function TryPlaceCobweb(pos, needs_check, side_vector) return false end end - -local function WoodBulk(pos, wood) - SetNodeIfCanBuild({x=pos.x+1, y=pos.y, z=pos.z+1}, {name=wood}) - SetNodeIfCanBuild({x=pos.x-1, y=pos.y, z=pos.z+1}, {name=wood}) - SetNodeIfCanBuild({x=pos.x+1, y=pos.y, z=pos.z-1}, {name=wood}) - SetNodeIfCanBuild({x=pos.x-1, y=pos.y, z=pos.z-1}, {name=wood}) + +-- 4 wooden pillars around pos at height +local function WoodBulk(pos, height, wood) + for y=0, height-1 do + SetNodeIfCanBuild({x=pos.x+1, y=pos.y+y, z=pos.z+1}, {name=wood}, false, true) + SetNodeIfCanBuild({x=pos.x-1, y=pos.y+y, z=pos.z+1}, {name=wood}, false, true) + SetNodeIfCanBuild({x=pos.x+1, y=pos.y+y, z=pos.z-1}, {name=wood}, false, true) + SetNodeIfCanBuild({x=pos.x-1, y=pos.y+y, z=pos.z-1}, {name=wood}, false, true) + end end --- Gänge mit Schienen --- Corridors with rails +-- Build a wooden support frame +local function WoodSupport(p, wood, post, torches, dir, torchdir) + local node_wood = {name=wood} + local node_fence = {name=post} + + local calc = { + p.x+dir[1], p.z+dir[2], -- X and Z, added by direction + p.x-dir[1], p.z-dir[2], -- subtracted + p.x+dir[2], p.z+dir[1], -- orthogonal + p.x-dir[2], p.z-dir[1], -- orthogonal, the other way + } + --[[ Shape: + WWW + P.P + PrP + pfp + W = wood + P = post (above floor level) + p = post (in floor level, only placed if no floor) + + From previous generation (for reference): + f = floor + r = rail + . = air + ]] + + -- Don't place those wood structs below open air + if not (minetest.get_node({x=calc[1], y=p.y+2, z=calc[2]}).name == "air" and + minetest.get_node({x=calc[3], y=p.y+2, z=calc[4]}).name == "air" and + minetest.get_node({x=p.x, y=p.y+2, z=p.z}).name == "air") then + + -- Left post and planks + local left_ok + left_ok = SetNodeIfCanBuild({x=calc[1], y=p.y-1, z=calc[2]}, node_fence) + if left_ok then left_ok = SetNodeIfCanBuild({x=calc[1], y=p.y , z=calc[2]}, node_fence) end + if left_ok then left_ok = SetNodeIfCanBuild({x=calc[1], y=p.y+1, z=calc[2]}, node_wood, false, true) end + + -- Right post and planks + local right_ok + right_ok = SetNodeIfCanBuild({x=calc[3], y=p.y-1, z=calc[4]}, node_fence) + if right_ok then right_ok = SetNodeIfCanBuild({x=calc[3], y=p.y , z=calc[4]}, node_fence) end + if right_ok then right_ok = SetNodeIfCanBuild({x=calc[3], y=p.y+1, z=calc[4]}, node_wood, false, true) end + + -- Middle planks + local top_planks_ok = false + if left_ok and right_ok then top_planks_ok = SetNodeIfCanBuild({x=p.x, y=p.y+1, z=p.z}, node_wood) end + + if minetest.get_node({x=p.x,y=p.y-2,z=p.z}).name=="air" then + if left_ok then SetNodeIfCanBuild({x=calc[1], y=p.y-2, z=calc[2]}, node_fence) end + if right_ok then SetNodeIfCanBuild({x=calc[3], y=p.y-2, z=calc[4]}, node_fence) end + end + -- Torches on the middle planks + if torches and top_planks_ok then + -- Place torches at horizontal sides + SetNodeIfCanBuild({x=calc[5], y=p.y+1, z=calc[6]}, {name=tsm_railcorridors.nodes.torch_wall, param2=torchdir[1]}, true) + SetNodeIfCanBuild({x=calc[7], y=p.y+1, z=calc[8]}, {name=tsm_railcorridors.nodes.torch_wall, param2=torchdir[2]}, true) + end + elseif torches then + -- Try to build torches instead of the wood structs + local node = {name=tsm_railcorridors.nodes.torch_floor, param2=minetest.dir_to_wallmounted({x=0,y=-1,z=0})} + + -- Try two different height levels + local pos1 = {x=calc[1], y=p.y-2, z=calc[2]} + local pos2 = {x=calc[3], y=p.y-2, z=calc[4]} + local nodedef1 = minetest.registered_nodes[minetest.get_node(pos1).name] + local nodedef2 = minetest.registered_nodes[minetest.get_node(pos2).name] + + if nodedef1.walkable then + pos1.y = pos1.y + 1 + end + SetNodeIfCanBuild(pos1, node, true) + + if nodedef2.walkable then + pos2.y = pos2.y + 1 + end + SetNodeIfCanBuild(pos2, node, true) + + end +end + +-- Dig out a single corridor section and place wooden structures and torches -- Returns , -- success: true if corridor could be placed entirely -- segments: Number of segments successfully placed -local function corridor_part(start_point, segment_vector, segment_count, wood, post, first_or_final, up_or_down_prev) +local function dig_corridor_section(start_point, segment_vector, segment_count, wood, post, up_or_down_prev) local p = {x=start_point.x, y=start_point.y, z=start_point.z} local torches = pr:next() < probability_torches_in_segment local dir = {0, 0} local torchdir = {1, 1} local node_wood = {name=wood} - local node_fence = {name=post} if segment_vector.x == 0 and segment_vector.z ~= 0 then dir = {1, 0} torchdir = {5, 4} @@ -350,7 +550,12 @@ local function corridor_part(start_point, segment_vector, segment_count, wood, p torchdir = {3, 2} end for segmentindex = 0, segment_count-1 do - local dug = Cube(p, 1, {name="air"}) + local dug + if segment_vector.y == 0 then + dug = Cube(p, 1, {name="air"}, false, wood, post) + else + dug = Cube(p, 1, {name="air"}, false) + end if not chaos_mode and segmentindex > 0 and not dug then return false, segmentindex end -- Add wooden platform, if neccessary. To avoid floating rails if segment_vector.y == 0 then @@ -364,92 +569,27 @@ local function corridor_part(start_point, segment_vector, segment_count, wood, p -- Normal 3×3 platform Platform({x=p.x, y=p.y-1, z=p.z}, 1, node_wood) end + else + -- Sloped bridge + Platform({x=p.x-dir[1], y=p.y-2, z=p.z-dir[2]}, 0, node_wood) + Platform({x=p.x, y=p.y-2, z=p.z}, 0, node_wood) + Platform({x=p.x+dir[1], y=p.y-2, z=p.z+dir[2]}, 0, node_wood) end - -- Diese komischen Holz-Konstruktionen - -- These strange wood structs if segmentindex % 2 == 1 and segment_vector.y == 0 then - local calc = { - p.x+dir[1], p.z+dir[2], -- X and Z, added by direction - p.x-dir[1], p.z-dir[2], -- subtracted - p.x+dir[2], p.z+dir[1], -- orthogonal - p.x-dir[2], p.z-dir[1], -- orthogonal, the other way - } - --[[ Shape: - WWW - P.P - PrP - pfp - W = wood - P = post (above floor level) - p = post (in floor level, only placed if no floor) - - From previous generation (for reference): - f = floor - r = rail - . = air - ]] - - -- Don't place those wood structs below open air - if not (minetest.get_node({x=calc[1], y=p.y+2, z=calc[2]}).name == "air" and - minetest.get_node({x=calc[3], y=p.y+2, z=calc[4]}).name == "air" and - minetest.get_node({x=p.x, y=p.y+2, z=p.z}).name == "air") then - - -- Left post and planks - local left_ok = true - left_ok = SetNodeIfCanBuild({x=calc[1], y=p.y-1, z=calc[2]}, node_fence) - if left_ok then left_ok = SetNodeIfCanBuild({x=calc[1], y=p.y , z=calc[2]}, node_fence) end - if left_ok then left_ok = SetNodeIfCanBuild({x=calc[1], y=p.y+1, z=calc[2]}, node_wood) end - - -- Right post and planks - local right_ok = true - right_ok = SetNodeIfCanBuild({x=calc[3], y=p.y-1, z=calc[4]}, node_fence) - if right_ok then right_ok = SetNodeIfCanBuild({x=calc[3], y=p.y , z=calc[4]}, node_fence) end - if right_ok then right_ok = SetNodeIfCanBuild({x=calc[3], y=p.y+1, z=calc[4]}, node_wood) end - - -- Middle planks - local top_planks_ok = false - if left_ok and right_ok then top_planks_ok = SetNodeIfCanBuild({x=p.x, y=p.y+1, z=p.z}, node_wood) end - - if minetest.get_node({x=p.x,y=p.y-2,z=p.z}).name=="air" then - if left_ok then SetNodeIfCanBuild({x=calc[1], y=p.y-2, z=calc[2]}, node_fence) end - if right_ok then SetNodeIfCanBuild({x=calc[3], y=p.y-2, z=calc[4]}, node_fence) end - end - -- Torches on the middle planks - if torches and top_planks_ok then - -- Place torches at horizontal sides - SetNodeIfCanBuild({x=calc[5], y=p.y+1, z=calc[6]}, {name=tsm_railcorridors.nodes.torch_wall, param2=torchdir[1]}, true) - SetNodeIfCanBuild({x=calc[7], y=p.y+1, z=calc[8]}, {name=tsm_railcorridors.nodes.torch_wall, param2=torchdir[2]}, true) - end - elseif torches then - -- Try to build torches instead of the wood structs - local node = {name=tsm_railcorridors.nodes.torch_floor, param2=minetest.dir_to_wallmounted({x=0,y=-1,z=0})} - - -- Try two different height levels - local pos1 = {x=calc[1], y=p.y-2, z=calc[2]} - local pos2 = {x=calc[3], y=p.y-2, z=calc[4]} - local nodedef1 = minetest.registered_nodes[minetest.get_node(pos1).name] - local nodedef2 = minetest.registered_nodes[minetest.get_node(pos2).name] - - if nodedef1.walkable then - pos1.y = pos1.y + 1 - end - SetNodeIfCanBuild(pos1, node, true) - - if nodedef2.walkable then - pos2.y = pos2.y + 1 - end - SetNodeIfCanBuild(pos2, node, true) - - end + WoodSupport(p, wood, post, torches, dir, torchdir) end - - -- nächster Punkt durch Vektoraddition - -- next way point + + -- Next way point p = vector.add(p, segment_vector) end -- End of the corridor segment; create the final piece - local dug = Cube(p, 1, {name="air"}) + local dug + if segment_vector.y == 0 then + dug = Cube(p, 1, {name="air"}, false, wood, post) + else + dug = Cube(p, 1, {name="air"}, false) + end if not chaos_mode and not dug then return false, segment_count end if segment_vector.y == 0 then Platform({x=p.x, y=p.y-1, z=p.z}, 1, node_wood) @@ -457,7 +597,11 @@ local function corridor_part(start_point, segment_vector, segment_count, wood, p return true, segment_count end -local function corridor_func(waypoint, coord, sign, up_or_down, up_or_down_next, up_or_down_prev, up, wood, post, first_or_final, damage, no_spawner) +-- Generate a corridor section. Corridor sections are part of a corridor line. +-- This is one short part of a corridor line. It can be one straight section or it goes up or down. +-- It digs out the corridor and places wood structs and torches using the helper function dig_corridor_function, +-- then it places rails, chests, and other goodies. +local function create_corridor_section(waypoint, axis, sign, up_or_down, up_or_down_next, up_or_down_prev, up, wood, post, first_or_final, damage, no_spawner) local segamount = 3 if up_or_down then segamount = 1 @@ -467,12 +611,12 @@ local function corridor_func(waypoint, coord, sign, up_or_down, up_or_down_next, end local vek = {x=0,y=0,z=0}; local start = table.copy(waypoint) - if coord == "x" then + if axis == "x" then vek.x=segamount if up_or_down and up == false then start.x=start.x+segamount end - elseif coord == "z" then + elseif axis == "z" then vek.z=segamount if up_or_down and up == false then start.z=start.z+segamount @@ -487,20 +631,19 @@ local function corridor_func(waypoint, coord, sign, up_or_down, up_or_down_next, end local segcount = pr:next(4,6) if up_or_down and up == false then - Cube(waypoint, 1, {name="air"}) + Cube(waypoint, 1, {name="air"}, false) end - local corridor_dug, corridor_segments_dug = corridor_part(start, vek, segcount, wood, post, first_or_final, up_or_down_prev) + local corridor_dug, corridor_segments_dug = dig_corridor_section(start, vek, segcount, wood, post, up_or_down_prev) local corridor_vek = {x=vek.x*segcount, y=vek.y*segcount, z=vek.z*segcount} - -- nachträglich Schienen legen - -- after this: rails + -- After this: rails segamount = 1 if sign then segamount = 0-segamount end - if coord == "x" then + if axis == "x" then vek.x=segamount - elseif coord == "z" then + elseif axis == "z" then vek.z=segamount end if up_or_down then @@ -541,7 +684,7 @@ local function corridor_func(waypoint, coord, sign, up_or_down, up_or_down_next, -- Randomly returns either the left or right side of the main rail. -- Also returns offset as second return value. local left_or_right = function(pos, vek) - local off, facedir + local off if pr:next(1, 2) == 1 then -- left off = {x = -vek.z, y= 0, z = vek.x} @@ -565,32 +708,30 @@ local function corridor_func(waypoint, coord, sign, up_or_down, up_or_down_next, -- Chest if i == chestplace then local cpos, offset = left_or_right(p, vek) - if minetest.get_node(cpos).name == post then + if minetest.get_node(cpos).name == post or IsInDirtRoom(p) then chestplace = chestplace + 1 else PlaceChest(cpos, minetest.dir_to_facedir(offset)) end end - -- Rail and cart - if i == cartplace then + -- A rail at the side of the track to put a cart on + if i == cartplace and #tsm_railcorridors.carts > 0 then local cpos = left_or_right(p, vek) if minetest.get_node(cpos).name == post then cartplace = cartplace + 1 else - local placed = PlaceRail(cpos, damage) + local placed + if IsRailSurface({x=cpos.x, y=cpos.y-1, z=cpos.z}) then + placed = PlaceRail(cpos, damage) + else + placed = false + end if placed then - local cart_type = pr:next(1, #tsm_railcorridors.carts) - -- FIXME: The cart sometimes fails to spawn - -- See - local cart_id = tsm_railcorridors.carts[cart_type] - local cart = minetest.add_entity(cpos, cart_id) - - -- This checks if the cart is actually spawned, it's a giant hack! - -- Note that the callback function is also called there. - -- TODO: Move callback function to this position when the - -- minetest.add_entity bug has been fixed. - minetest.after(2, RecheckCartHack, {cpos, cart_id, 10}) + -- We don't put on a cart yet, we put it in the carts table + -- for later placement + local cart_type = pr_carts:next(1, #tsm_railcorridors.carts) + table.insert(carts_table, {pos = cpos, cart_type = cart_type}) end end end @@ -649,7 +790,7 @@ local function corridor_func(waypoint, coord, sign, up_or_down, up_or_down_next, end end - + local offset = table.copy(corridor_vek) local final_point = vector.add(waypoint, offset) if up_or_down then @@ -657,12 +798,13 @@ local function corridor_func(waypoint, coord, sign, up_or_down, up_or_down_next, offset.y = offset.y - 1 final_point = vector.add(waypoint, offset) else - offset[coord] = offset[coord] + segamount + offset[axis] = offset[axis] + segamount final_point = vector.add(waypoint, offset) - -- After going up or down, 1 missing rail piece must be added - if IsRailSurface({x=final_point.x,y=final_point.y-2,z=final_point.z}) then - PlaceRail({x=final_point.x,y=final_point.y-1,z=final_point.z}, damage) - end + end + -- After going up or down, 1 missing rail piece must be added + Platform({x=final_point.x,y=final_point.y-1,z=final_point.z}, 0, {name=wood}) + if IsRailSurface({x=final_point.x,y=final_point.y-2,z=final_point.z}) then + PlaceRail({x=final_point.x,y=final_point.y-1,z=final_point.z}, damage) end end if not corridor_dug then @@ -672,34 +814,46 @@ local function corridor_func(waypoint, coord, sign, up_or_down, up_or_down_next, end end -local function start_corridor(waypoint, coord, sign, length, psra, wood, post, damage, no_spawner) +-- Generate a line of corridors. +-- The corridor can go up/down, take turns and it can branch off, creating more corridor lines. +local function create_corridor_line(waypoint, axis, sign, length, wood, post, damage, no_spawner) local wp = waypoint - local c = coord + local a = axis local s = sign - local ud = false -- up or down - local udn = false -- up or down is next - local udp = false -- up or down was previous - local up + local ud = false -- Up or down + local udn = false -- Up or down is next + local udp = false -- Up or down was previous + local up = false -- true if going up + local upp = false -- true if was going up previously for i=1,length do - local needs_platform -- Update previous up/down status udp = ud + -- Can't go up/down if a platform is needed at waypoint + local needs_platform = NeedsPlatform({x=wp.x,y=wp.y-2,z=wp.z}) -- Update current up/down status - if udn then - needs_platform = NeedsPlatform(wp) - if needs_platform then - ud = false - end + if udn and not needs_platform then ud = true -- Force direction near the height limits if wp.y >= height_max - 12 then + if udp then + ud = false + end up = false elseif wp.y <= height_min + 12 then + if udp then + ud = false + end up = true else - -- Chose random direction in between - up = pr:next(0, 2) < 1 + -- If previous was up/down, keep the vertical direction + if udp and not chaos_mode then + up = upp + else + -- Chose random direction + up = pr:next(1, 2) == 1 + end end + upp = up else ud = false end @@ -709,71 +863,141 @@ local function start_corridor(waypoint, coord, sign, length, psra, wood, post, d elseif udn and not needs_platform then udn = false end - -- Make corridor / Korridor graben + -- Make corridor local first_or_final if i == length then first_or_final = "final" elseif i == 1 then first_or_final = "first" end - wp, no_spawner = corridor_func(wp,c,s, ud, udn, udp, up, wood, post, first_or_final, damage, no_spawner) + wp, no_spawner = create_corridor_section(wp,a,s, ud, udn, udp, up, wood, post, first_or_final, damage, no_spawner) if wp == false then return end - -- Verzweigung? - -- Fork? + -- Fork in the road? If so, starts 2-3 new corridor lines and terminates the current one. if pr:next() < probability_fork then + -- 75% chance to fork off in 3 directions (making a crossing) + -- 25% chance to fork off in 2 directions (making a t-junction) + local is_crossing = pr:next(0, 3) < 3 + local forks = 2 + if is_crossing then + forks = 3 + end local p = {x=wp.x, y=wp.y, z=wp.z} - start_corridor(wp, c, s, pr:next(way_min,way_max), psra, wood, post, damage, no_spawner) - if c == "x" then c="z" else c="x" end - start_corridor(wp, c, s, pr:next(way_min,way_max), psra, wood, post, damage, no_spawner) - start_corridor(wp, c, not s, pr:next(way_min,way_max), psra, wood, post, damage, no_spawner) - WoodBulk({x=p.x, y=p.y-1, z=p.z}, wood) - WoodBulk({x=p.x, y=p.y, z=p.z}, wood) - WoodBulk({x=p.x, y=p.y+1, z=p.z}, wood) - WoodBulk({x=p.x, y=p.y+2, z=p.z}, wood) + local a2 + if a == "x" then + a2="z" + else + a2="x" + end + local fork_dirs = { + {a2, s}, -- to the side + {a2, not s}, -- to the other side + {a, s}, -- straight ahead + } + for f=1, forks do + local r = pr:next(1, #fork_dirs) + create_corridor_line(wp, fork_dirs[r][1], fork_dirs[r][2], pr:next(way_min,way_max), wood, post, damage, no_spawner) + table.remove(fork_dirs, r) + end + if is_crossing and not IsInDirtRoom(p) then + -- 4 large wooden pillars around the center rail + WoodBulk({x=p.x, y=p.y-1, z=p.z}, 4, wood) + end return end - -- coord und sign verändern - -- randomly change sign and coord - if c=="x" then - c="z" - elseif c=="z" then - c="x" + -- Randomly change sign, toggle axis. + -- In other words, take a turn. + if a=="x" then + a="z" + elseif a=="z" then + a="x" end; - s = pr:next(0, 2) < 1 + s = pr:next(1, 2) == 1 end end -local function place_corridors(main_cave_coords, psra) - --[[ ALWAYS start building in the ground. Prevents corridors starting - in mid-air or in liquids. ]] - if not IsGround(main_cave_coords) then - return +-- Spawns all carts in the carts table and clears the carts table afterwards +local function spawn_carts() + for c=1, #carts_table do + local cpos = carts_table[c].pos + local cart_type = carts_table[c].cart_type + local node = minetest.get_node(cpos) + if node.name == tsm_railcorridors.nodes.rail then + -- FIXME: The cart sometimes fails to spawn + -- See + local cart_id = tsm_railcorridors.carts[cart_type] + minetest.log("info", "[tsm_railcorridors] Cart spawn attempt: "..minetest.pos_to_string(cpos)) + minetest.add_entity(cpos, cart_id) + + -- This checks if the cart is actually spawned, it's a giant hack! + -- Note that the callback function is also called there. + -- TODO: Move callback function to this position when the + -- minetest.add_entity bug has been fixed. + minetest.after(3, RecheckCartHack, {cpos, cart_id}) + end end + carts_table = {} +end + +-- Start generation of a rail corridor system +-- main_cave_coords is the center of the floor of the dirt room, from which +-- all corridors expand. +local function create_corridor_system(main_cave_coords) + + -- Dirt room size + local maxsize = 6 + if chaos_mode then + maxsize = 9 + end + local size = pr:next(3, maxsize) + + --[[ Only build if starter coords are in the ground. + Prevents corridors starting in mid-air or in liquids. ]] + local check_coords = { + -- Center of the room, on the floor + {x=0,y=0,z=0}, + -- Also check near the 4 bottom corners of the dirt room + {x= size-1, y=0, z=size-1}, + {x=-size+1, y=0, z=size-1}, + {x= size-1, y=0, z=-size+1}, + {x=-size+1, y=0, z=-size+1}, + } + for c=1, #check_coords do + if not IsGround(vector.add(main_cave_coords, check_coords[c])) then + return false + end + end + local center_node = minetest.get_node(main_cave_coords) + local height = pr:next(4, 7) + if height > size then + height = size + end + local floor_diff = 1 + if pr:next(0, 100) < 50 then + floor_diff = 0 + end + local dirt_mode = pr:next(1,2) + local rnd = pr:next(1,1000) + -- Small chance to fill dirt room with random rails + local decorations_mode = 0 + if rnd == 1000 then + decorations_mode = 1 + end + + --[[ Starting point: A big hollow dirt cube from which the corridors will extend. + Corridor generation starts here. ]] + DirtRoom(main_cave_coords, size, height, dirt_mode, decorations_mode) + main_cave_coords.y = main_cave_coords.y + 2 + floor_diff + -- Determine if this corridor system is “damaged” (some rails removed) and to which extent local damage = 0 if pr:next() < probability_damage then damage = pr:next(10, 50) end - --[[ Starter cube: A big hollow dirt cube from which the corridors will extend. - Corridor generation starts here. ]] - if pr:next(0, 100) < 50 then - Cube(main_cave_coords, 4, {name=tsm_railcorridors.nodes.dirt}) - Cube(main_cave_coords, 3, {name="air"}) - -- Center rail - PlaceRail({x=main_cave_coords.x, y=main_cave_coords.y-3, z=main_cave_coords.z}, damage) - main_cave_coords.y =main_cave_coords.y - 1 - else - Cube(main_cave_coords, 3, {name=tsm_railcorridors.nodes.dirt}) - Cube(main_cave_coords, 2, {name="air"}) - -- Center rail - PlaceRail({x=main_cave_coords.x, y=main_cave_coords.y-2, z=main_cave_coords.z}, damage) - end - local xs = pr:next(0, 2) < 1 - local zs = pr:next(0, 2) < 1; -- Get wood and fence post types, using gameconfig. + local wood, post if tsm_railcorridors.nodes.corridor_woods_function then -- Get wood type by gameconfig function @@ -787,10 +1011,6 @@ local function place_corridors(main_cave_coords, psra) for w=1, #tsm_railcorridors.nodes.corridor_woods do local woodtable = tsm_railcorridors.nodes.corridor_woods[w] accumulated_chance = accumulated_chance + woodtable.chance - if accumulated_chance > 1000 then - minetest.log("warning", "[tsm_railcorridors] Warning: Wood chances add up to over 100%!") - break - end if rnd <= accumulated_chance then woodtype = w break @@ -800,31 +1020,98 @@ local function place_corridors(main_cave_coords, psra) post = tsm_railcorridors.nodes.corridor_woods[woodtype].post end - start_corridor(main_cave_coords, "x", xs, pr:next(way_min,way_max), psra, wood, post, damage, false) - start_corridor(main_cave_coords, "z", zs, pr:next(way_min,way_max), psra, wood, post, damage, false) - -- Auch mal die andere Richtung? - -- Try the other direction? - if pr:next(0, 100) < 70 then - start_corridor(main_cave_coords, "x", not xs, pr:next(way_min,way_max), psra, wood, post, damage, false) + -- Start 2-4 corridors in each direction + local dirs = { + {axis="x", axis2="z", sign=false}, + {axis="x", axis2="z", sign=true}, + {axis="z", axis2="x", sign=false}, + {axis="z", axis2="x", sign=true}, + } + local first_corridor + local corridors = 2 + for _=1, 2 do + if pr:next(0,100) < 70 then + corridors = corridors + 1 + end end - if pr:next(0, 100) < 70 then - start_corridor(main_cave_coords, "z", not zs, pr:next(way_min,way_max), psra, wood, post, damage, false) + -- Chance for 5th corridor in Chaos Mode + if chaos_mode and size > 4 then + if pr:next(0,100) < 50 then + corridors = corridors + 1 + end end + local centered_crossing = false + if corridors <= 4 and pr:next(1, 20) >= 11 then + centered_crossing = true + end + -- This moves the start of the corridors in the dirt room back and forth + local d_max = 3 + if floor_diff == 1 and height <= 4 then + d_max = d_max + 1 + end + local from_center_base = size - pr:next(1,d_max) + for i=1, math.min(4, corridors) do + local d = pr:next(1, #dirs) + local dir = dirs[d] + local side_offset = 0 + if not centered_crossing and size > 3 then + if i==1 and corridors == 5 then + side_offset = pr:next(2, size-2) + if pr:next(1,2) == 1 then + side_offset = -side_offset + end + else + side_offset = pr:next(-size+2, size-2) + end + end + local from_center = from_center_base + if dir.sign then + from_center = -from_center + end + if i == 1 then + first_corridor = {sign=dir.sign, axis=dir.axis, axis2=dir.axis2, side_offset=side_offset, from_center=from_center} + end + local coords = vector.add(main_cave_coords, {[dir.axis] = from_center, y=0, [dir.axis2] = side_offset}) + create_corridor_line(coords, dir.axis, dir.sign, pr:next(way_min,way_max), wood, post, damage, false) + table.remove(dirs, d) + end + if corridors == 5 then + local special_coords = vector.add(main_cave_coords, {[first_corridor.axis2] = -first_corridor.side_offset, y=0, [first_corridor.axis] = first_corridor.from_center}) + create_corridor_line(special_coords, first_corridor.axis, first_corridor.sign, pr:next(way_min,way_max), wood, post, damage, false) + end + + -- At this point, all corridors were generated and all nodes were set. + -- We spawn the carts now + spawn_carts() + + return true end +-- The rail corridor algorithm starts here minetest.register_on_generated(function(minp, maxp, blockseed) + -- We re-init the randomizer for every mapchunk as we start generating in the middle of each mapchunk. + -- We can't use the mapgen seed as this would make the algorithm depending on the order the mapchunk generate. InitRandomizer(blockseed) - if minp.y < height_max and maxp.y > height_min and pr:next() < probability_railcaves_in_chunk then - -- Get semi-random height in chunk - + if minp.y < height_max and maxp.y > height_min and pr:next() < probability_railcaves_in_mapchunk then + -- Keep some distance from the upper/lower mapchunk limits local buffer = 5 - local y = pr:next(minp.y + buffer, maxp.y - buffer) - y = math.floor(math.max(height_min + buffer, math.min(height_max - buffer, y))) - -- Mid point of the chunk - local p = {x=minp.x+math.floor((maxp.x-minp.x)/2), y=y, z=minp.z+math.floor((maxp.z-minp.z)/2)} - -- Haupthöhle und alle weiteren - -- Corridors; starting with main cave out of dirt - place_corridors(p, pr) + -- Do up to 10 tries to start a corridor system + for t=1,10 do + -- Get semi-random height in mapchunk + local y = pr:next(minp.y + buffer, maxp.y - buffer) + y = math.floor(math.max(height_min + buffer, math.min(height_max - buffer, y))) + + -- Mid point of the mapchunk + local p = {x=minp.x+math.floor((maxp.x-minp.x)/2), y=y, z=minp.z+math.floor((maxp.z-minp.z)/2)} + -- Start corridor system at p. Might fail if p is in open air + minetest.log("verbose", "[tsm_railcorridors] Attempting to start rail corridor system at "..minetest.pos_to_string(p)) + if create_corridor_system(p, pr) then + minetest.log("info", "[tsm_railcorridors] Generated rail corridor system at "..minetest.pos_to_string(p)) + break + else + minetest.log("info", "[tsm_railcorridors] Rail corridor system generation attempt failed at "..minetest.pos_to_string(p).. " (try "..t..")") + end + end end end) diff --git a/mods/MAPGEN/tsm_railcorridors/settingtypes.txt b/mods/MAPGEN/tsm_railcorridors/settingtypes.txt index b21662c6..a2896290 100644 --- a/mods/MAPGEN/tsm_railcorridors/settingtypes.txt +++ b/mods/MAPGEN/tsm_railcorridors/settingtypes.txt @@ -1,5 +1,5 @@ -#Probability (0.0 to 1.0) for every newly generated chunk to get rail corridors. -tsm_railcorridors_probability_railcaves_in_chunk (Rail corridor probability) float 0.3 0.0 1.0 +#Probability (0.0 to 1.0) for every newly generated mapchunk to get rail corridors. +tsm_railcorridors_probability_railcaves_in_mapchunk (Rail corridor probability) float 0.33333 0.0 1.0 #Minimum rail corridor path length (excludes forks). tsm_railcorridors_way_min (Minimum rail corridor length) int 4 1 @@ -20,6 +20,17 @@ tsm_railcorridors_probability_fork (Fork probability) float 0.04 0.0 1.0 #Probability (0.0 to 1.0) for every part of a rail corridor to contain a treasure chest. tsm_railcorridors_probability_chest (Chest probability) float 0.05 0.0 1.0 +#Probability (0.0 to 1.0) for every part of a rail corridor to include a cart. +#Note: The rail may still be subject to rail damage, so the probability +#of finding a cart in rail corridors with high rail damage will be lower. +#NOTE: Due to a bug in Minetest +#carts often fail to spawn even if they should. +tsm_railcorridors_probability_cart (Cart probability) float 0.0 0.0 1.0 + +#If enabled, cobwebs may be placed in some corridors. +#Currently, cobwebs are only supported with the Mobs Redo mod. +tsm_railcorridors_place_cobwebs (Cobwebs) bool true + #Probability (0.0 to 1.0) for a rail corridor system to have damaged/incomplete railways tsm_railcorridors_probability_damage (Damaged railway probability) float 1.0 0.0 1.0 @@ -28,7 +39,3 @@ tsm_railcorridors_probability_damage (Damaged railway probability) float 1.0 0.0 #to pretty chaotic rail corridors, but they are also more free to spread. #If disabled, rail corridors spread in a orderly fashion. tsm_railcorridors_chaos (Chaos Mode) bool false - -#If enabled, cobwebs may be placed in some corridors. -#Currently, cobwebs are only supported with the Mobs Redo mod. -tsm_railcorridors_place_cobwebs (Cobwebs) bool true From 8982e368ecb1e4146a74d75508eea5c593388f60 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 16:09:39 +0100 Subject: [PATCH 013/798] Abort sleep if taking damage --- mods/ITEMS/mcl_beds/functions.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index f763a23c..495927a1 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -194,6 +194,7 @@ function mcl_beds.sleep() end end +-- Throw all players out of bed function mcl_beds.kick_players() for name, _ in pairs(mcl_beds.player) do local player = minetest.get_player_by_name(name) @@ -201,6 +202,14 @@ function mcl_beds.kick_players() end end +-- Throw a player out of bed +function mcl_beds.kick_player(player) + local name = player:get_player_name() + if mcl_beds.player[name] ~= nil then + lay_down(player, nil, nil, false) + end +end + function mcl_beds.skip_night() minetest.set_timeofday(0.25) -- tod = 6000 end @@ -305,3 +314,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) mcl_beds.sleep() end end) + +minetest.register_on_player_hpchange(function(player, hp_change) + if hp_change < 0 then + mcl_beds.kick_player(player) + end +end) From 3b8fe6039e8238b0c5d5a335fbfcfbe7d21c9743 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 16:18:13 +0100 Subject: [PATCH 014/798] Don't skip night if not all players are asleep --- mods/ITEMS/mcl_beds/functions.lua | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 495927a1..e57d5822 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -161,15 +161,18 @@ local function lay_down(player, pos, bed_pos, state, skip) end local function update_formspecs(finished) + if is_sp then + return + end local ges = #minetest.get_connected_players() local form_n local all_in_bed = ges == player_in_bed if finished then - form_n = mcl_beds.formspec .. "label[2.7,11; Good morning.]" + form_n = mcl_beds.formspec .. "label[2.7,11;Good morning.]" else form_n = mcl_beds.formspec .. "label[2.2,11;" .. tostring(player_in_bed) .. - " of " .. tostring(ges) .. " players are in bed]" + " of " .. tostring(ges) .. " players are in bed]" end for name,_ in pairs(mcl_beds.player) do @@ -272,10 +275,10 @@ function mcl_beds.on_rightclick(pos, player) -- skip the night and let all players stand up if check_in_beds() then minetest.after(5, function() - if not is_sp then + if check_in_beds() then update_formspecs(is_night_skip_enabled()) + mcl_beds.sleep() end - mcl_beds.sleep() end) end end @@ -294,8 +297,10 @@ minetest.register_on_leaveplayer(function(player) mcl_beds.player[name] = nil if check_in_beds() then minetest.after(5, function() - update_formspecs(is_night_skip_enabled()) - mcl_beds.sleep() + if check_in_beds() then + update_formspecs(is_night_skip_enabled()) + mcl_beds.sleep() + end end) end end) From cb2978470cea010bc7b96d2eacff7e34432d5308 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 17:04:38 +0100 Subject: [PATCH 015/798] Improve bed formspec --- mods/ITEMS/mcl_beds/functions.lua | 35 ++++++++++++++++++++++--------- mods/ITEMS/mcl_beds/init.lua | 4 ---- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index e57d5822..357d8bd2 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -161,18 +161,32 @@ local function lay_down(player, pos, bed_pos, state, skip) end local function update_formspecs(finished) - if is_sp then - return - end local ges = #minetest.get_connected_players() - local form_n + local form_n = "size[8,15;true]" + local all_in_bed = ges == player_in_bed if finished then - form_n = mcl_beds.formspec .. "label[2.7,11;Good morning.]" + for name,_ in pairs(mcl_beds.player) do + minetest.close_formspec(name, "mcl_beds_form") + end + return + elseif not is_sp then + local text = string.format("%d of %d player(s) are in bed.", player_in_bed, ges) + if all_in_bed then + text = text .. "\n" .. "You're sleeping." + form_n = form_n .. "bgcolor[#000000FF; true]" + form_n = form_n .. "button_exit[2,12;4,0.75;leave;Abort sleep]" + else + text = text .. "\n" .. "Sleep will commence when all players are in bed." + form_n = form_n .. "bgcolor[#808080BB; true]" + form_n = form_n .. "button_exit[2,12;4,0.75;leave;Leave bed]" + end + form_n = form_n .. "label[2.2,7.5;"..minetest.formspec_escape(text).."]" else - form_n = mcl_beds.formspec .. "label[2.2,11;" .. tostring(player_in_bed) .. - " of " .. tostring(ges) .. " players are in bed]" + form_n = form_n .. "label[2.2,7.5;You're sleeping.]" + form_n = form_n .. "button_exit[2,12;4,0.75;leave;Abort sleep]" + form_n = form_n .. "bgcolor[#000000FF; true]" end for name,_ in pairs(mcl_beds.player) do @@ -203,6 +217,7 @@ function mcl_beds.kick_players() local player = minetest.get_player_by_name(name) lay_down(player, nil, nil, false) end + update_formspecs(false) end -- Throw a player out of bed @@ -210,6 +225,8 @@ function mcl_beds.kick_player(player) local name = player:get_player_name() if mcl_beds.player[name] ~= nil then lay_down(player, nil, nil, false) + update_formspecs(false) + minetest.close_formspec(name, "mcl_beds_form") end end @@ -268,9 +285,7 @@ function mcl_beds.on_rightclick(pos, player) lay_down(player, nil, nil, false) end - if not is_sp then - update_formspecs(false) - end + update_formspecs(false) -- skip the night and let all players stand up if check_in_beds() then diff --git a/mods/ITEMS/mcl_beds/init.lua b/mods/ITEMS/mcl_beds/init.lua index 72bfb347..8f60cb54 100644 --- a/mods/ITEMS/mcl_beds/init.lua +++ b/mods/ITEMS/mcl_beds/init.lua @@ -2,10 +2,6 @@ mcl_beds = {} mcl_beds.player = {} mcl_beds.pos = {} -mcl_beds.formspec = "size[8,15;true]" .. - "bgcolor[#080808BB; true]" .. - "button_exit[2,12;4,0.75;leave;Leave Bed]" - local modpath = minetest.get_modpath("mcl_beds") -- Load files From b481eadafd08d3dcc79a0619db36a546edc0fd25 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 17:32:22 +0100 Subject: [PATCH 016/798] Beds: Show message when night skip is disabled --- mods/ITEMS/mcl_beds/functions.lua | 34 +++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 357d8bd2..6b35000b 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -163,8 +163,12 @@ end local function update_formspecs(finished) local ges = #minetest.get_connected_players() local form_n = "size[8,15;true]" - local all_in_bed = ges == player_in_bed + local night_skip = is_night_skip_enabled() + local button_leave = "button_exit[2,12;4,0.75;leave;Leave bed]" + local button_abort = "button_exit[2,12;4,0.75;leave;Abort sleep]" + local bg_presleep = "bgcolor[#00000080;true]" + local bg_sleep = "bgcolor[#000000FF;true]" if finished then for name,_ in pairs(mcl_beds.player) do @@ -173,20 +177,32 @@ local function update_formspecs(finished) return elseif not is_sp then local text = string.format("%d of %d player(s) are in bed.", player_in_bed, ges) - if all_in_bed then + if not night_skip then + text = text .. "\n" .. "You're in bed." .. "\n" .. "Note: Night skip is disabled." + form_n = form_n .. bg_presleep + form_n = form_n .. button_leave + elseif all_in_bed then text = text .. "\n" .. "You're sleeping." - form_n = form_n .. "bgcolor[#000000FF; true]" - form_n = form_n .. "button_exit[2,12;4,0.75;leave;Abort sleep]" + form_n = form_n .. bg_sleep + form_n = form_n .. button_abort else text = text .. "\n" .. "Sleep will commence when all players are in bed." - form_n = form_n .. "bgcolor[#808080BB; true]" - form_n = form_n .. "button_exit[2,12;4,0.75;leave;Leave bed]" + form_n = form_n .. bg_presleep + form_n = form_n .. button_leave end form_n = form_n .. "label[2.2,7.5;"..minetest.formspec_escape(text).."]" else - form_n = form_n .. "label[2.2,7.5;You're sleeping.]" - form_n = form_n .. "button_exit[2,12;4,0.75;leave;Abort sleep]" - form_n = form_n .. "bgcolor[#000000FF; true]" + local text + if night_skip then + text = "You're sleeping." + form_n = form_n .. bg_sleep + form_n = form_n .. button_abort + else + text = "You're in bed." .. "\n" .. "Note: Night skip is disabled." + form_n = form_n .. bg_presleep + form_n = form_n .. button_leave + end + form_n = form_n .. "label[2.2,7.5;"..minetest.formspec_escape(text).."]" end for name,_ in pairs(mcl_beds.player) do From 942b9468ffadb7f3451583b909b096e8a8c8c9c2 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 17:38:19 +0100 Subject: [PATCH 017/798] Simplify bed help a bit --- mods/ITEMS/mcl_beds/api.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/mcl_beds/api.lua b/mods/ITEMS/mcl_beds/api.lua index 074d847d..5d473245 100644 --- a/mods/ITEMS/mcl_beds/api.lua +++ b/mods/ITEMS/mcl_beds/api.lua @@ -23,16 +23,16 @@ local function destruct_bed(pos, n) end local beddesc = "Beds allow you to sleep at night and make the time pass faster." -local beduse = "Right-click on the bed to sleep in it. This only works when the sun sets, at night or during a thunderstorm." +local beduse = "To use a bed, stand close to it and right-click the bed to sleep in it. Sleeping only works when the sun sets, at night or during a thunderstorm. The bed must also be clear of any danger." if minetest.settings:get_bool("enable_bed_respawn") == false then - beddesc = beddesc .. "\n" .. "In local folklore, legends are told of other worlds where setting the start point for your next would be possible. But this world is not one of them." + beddesc = beddesc .. "\n" .. "In local folklore, legends are told of other worlds where setting the start point for your next life would be possible. But this world is not one of them." else - beddesc = beddesc .. "\n" .. "By sleeping in a bed, you set the starting point for your next life." + beddesc = beddesc .. "\n" .. "By sleeping in a bed, you set the starting point for your next life. If you die, you will start your next life at this bed, unless it is obstructed or destroyed." end if minetest.settings:get_bool("enable_bed_night_skip") == false then beddesc = beddesc .. "\n" .. "In this strange world, going to bed won't skip the night, but you can skip thunderstorms." else - beddesc = beddesc .. "\n" .. "Sleeping allows you to skip the night if you're the only player in this world. If you're not alone, the night is skipped when all players in this world went to sleep. Thunderstorms can be skipped in the same manner." + beddesc = beddesc .. "\n" .. "Sleeping allows you to skip the night. The night is skipped when all players in this world went to sleep. The night is skipped after sleeping for a few seconds. Thunderstorms can be skipped in the same manner." end local default_sounds From f18b1bd52ae158f05e096324f33df44d2956b1c4 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 18:01:21 +0100 Subject: [PATCH 018/798] Kick player out of bed when bed destroyed --- mods/ITEMS/mcl_beds/api.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mods/ITEMS/mcl_beds/api.lua b/mods/ITEMS/mcl_beds/api.lua index 5d473245..5675b50a 100644 --- a/mods/ITEMS/mcl_beds/api.lua +++ b/mods/ITEMS/mcl_beds/api.lua @@ -22,6 +22,18 @@ local function destruct_bed(pos, n) end end +local function kick_player_after_destruct(pos) + for name, _ in pairs(mcl_beds.pos) do + if vector.equals(pos, mcl_beds.pos) then + local player = minetest.get_player_by_name(name) + if player and player:is_player() then + mcl_beds.kick_player(player) + break + end + end + end +end + local beddesc = "Beds allow you to sleep at night and make the time pass faster." local beduse = "To use a bed, stand close to it and right-click the bed to sleep in it. Sleeping only works when the sun sets, at night or during a thunderstorm. The bed must also be clear of any danger." if minetest.settings:get_bool("enable_bed_respawn") == false then @@ -125,6 +137,7 @@ function mcl_beds.register_bed(name, def) on_destruct = function(pos) destruct_bed(pos, 1) + kick_player_after_destruct(pos) end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) @@ -199,6 +212,7 @@ function mcl_beds.register_bed(name, def) on_rotate = false, on_destruct = function(pos) destruct_bed(pos, 2) + kick_player_after_destruct(pos) end, }) From 0cc8962258a522ce787fb5b2df0c16e3e412d82e Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 18:17:44 +0100 Subject: [PATCH 019/798] Fix stuck player if server shuts down during sleep --- mods/ITEMS/mcl_beds/functions.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 6b35000b..27faef23 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -318,8 +318,11 @@ end -- Callbacks minetest.register_on_joinplayer(function(player) if player:get_attribute("mcl_beds:sleeping") == "true" then + -- Make player awake on joining server player:set_attribute("mcl_beds:sleeping", "false") end + playerphysics.remove_physics_factor(player, "speed", "mcl_beds:sleeping") + playerphysics.remove_physics_factor(player, "jump", "mcl_beds:sleeping") end) minetest.register_on_leaveplayer(function(player) From ec53db93529de46cfc14692701bfdd9b9554c908 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 19:22:24 +0100 Subject: [PATCH 020/798] Beds can save spawn pos at daytime, too --- mods/ITEMS/mcl_beds/api.lua | 18 +++++----- mods/ITEMS/mcl_beds/functions.lua | 58 ++++++++++++++++++------------- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/mods/ITEMS/mcl_beds/api.lua b/mods/ITEMS/mcl_beds/api.lua index 5675b50a..708f529c 100644 --- a/mods/ITEMS/mcl_beds/api.lua +++ b/mods/ITEMS/mcl_beds/api.lua @@ -1,15 +1,13 @@ local reverse = true -local function destruct_bed(pos, n) +local function destruct_bed(pos, is_top) local node = minetest.get_node(pos) local other - - if n == 2 then - local dir = minetest.facedir_to_dir(node.param2) + local dir = minetest.facedir_to_dir(node.param2) + if is_top then other = vector.subtract(pos, dir) - elseif n == 1 then - local dir = minetest.facedir_to_dir(node.param2) + else other = vector.add(pos, dir) end @@ -136,12 +134,12 @@ function mcl_beds.register_bed(name, def) end, on_destruct = function(pos) - destruct_bed(pos, 1) + destruct_bed(pos, false) kick_player_after_destruct(pos) end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - mcl_beds.on_rightclick(pos, clicker) + mcl_beds.on_rightclick(pos, clicker, false) return itemstack end, @@ -206,12 +204,12 @@ function mcl_beds.register_bed(name, def) selection_box = selection_box_top, collision_box = collision_box_top, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - mcl_beds.on_rightclick(pos, clicker) + mcl_beds.on_rightclick(pos, clicker, true) return itemstack end, on_rotate = false, on_destruct = function(pos) - destruct_bed(pos, 2) + destruct_bed(pos, true) kick_player_after_destruct(pos) end, }) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 27faef23..2232f6da 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -61,14 +61,14 @@ local function lay_down(player, pos, bed_pos, state, skip) local hud_flags = player:hud_get_flags() if not player or not name then - return + return false end if bed_pos then -- No sleeping if too far away if vector.distance(bed_pos, pos) > 2 then minetest.chat_send_player(name, "You can't sleep, the bed's too far away!") - return + return false end -- No sleeping while moving. This is a workaround. @@ -76,7 +76,7 @@ local function lay_down(player, pos, bed_pos, state, skip) -- but this is not possible in Minetest 0.4.17. if vector.length(player:get_player_velocity()) > 0.001 then minetest.chat_send_player(name, "You have to stop moving before going to bed!") - return + return false end -- No sleeping if monsters nearby. @@ -93,7 +93,7 @@ local function lay_down(player, pos, bed_pos, state, skip) if math.abs(bed_pos.y - obj:get_pos().y) <= 5 then minetest.chat_send_player(name, "You can't sleep now, monsters are nearby!") end - return + return false end end end @@ -108,7 +108,7 @@ local function lay_down(player, pos, bed_pos, state, skip) end -- skip here to prevent sending player specific changes (used for leaving players) if skip then - return + return false end if p then player:setpos(p) @@ -116,7 +116,9 @@ local function lay_down(player, pos, bed_pos, state, skip) -- physics, eye_offset, etc player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) - player:set_look_horizontal(math.random(1, 180) / 100) + if player:get_look_vertical() > 0 then + player:set_look_vertical(0) + end mcl_player.player_attached[name] = false playerphysics.remove_physics_factor(player, "speed", "mcl_beds:sleeping") playerphysics.remove_physics_factor(player, "jump", "mcl_beds:sleeping") @@ -135,10 +137,24 @@ local function lay_down(player, pos, bed_pos, state, skip) local def2 = minetest.registered_nodes[n2.name] if def1.walkable or def2.walkable then minetest.chat_send_player(name, "You can't sleep, the bed is obstructed!") - return + return false elseif (def1.damage_per_second ~= nil and def1.damage_per_second > 0) or (def2.damage_per_second ~= nil and def2.damage_per_second > 0) then minetest.chat_send_player(name, "It's too dangerous to sleep here!") - return + return false + end + + if minetest.get_modpath("mcl_spawn") then + local spos = table.copy(bed_pos) + spos.y = spos.y + 0.1 + mcl_spawn.set_spawn_pos(player, spos) -- save respawn position when entering bed + end + + -- Check day of time and weather + local tod = minetest.get_timeofday() * 24000 + -- Values taken from Minecraft Wiki with offset of +6000 + if tod < 18541 and tod > 5458 and (not weather_mod or (mcl_weather.get_weather() ~= "thunder")) then + minetest.chat_send_player(name, "You can only sleep at night or during a thunderstorm.") + return false end mcl_beds.player[name] = 1 @@ -147,6 +163,7 @@ local function lay_down(player, pos, bed_pos, state, skip) -- physics, eye_offset, etc player:set_eye_offset({x = 0, y = -13, z = 0}, {x = 0, y = 0, z = 0}) player:set_look_horizontal(yaw) + player:set_look_vertical(0) player:set_attribute("mcl_beds:sleeping", "true") playerphysics.add_physics_factor(player, "speed", "mcl_beds:sleeping", 0) @@ -158,6 +175,7 @@ local function lay_down(player, pos, bed_pos, state, skip) end player:hud_set_flags(hud_flags) + return true end local function update_formspecs(finished) @@ -260,7 +278,7 @@ function mcl_beds.skip_thunderstorm() return false end -function mcl_beds.on_rightclick(pos, player) +function mcl_beds.on_rightclick(pos, player, is_top) -- Anti-Inception: Don't allow to sleep while you're sleeping if player:get_attribute("mcl_beds:sleeping") == "true" then return @@ -278,24 +296,16 @@ function mcl_beds.on_rightclick(pos, player) end local name = player:get_player_name() local ppos = player:get_pos() - local tod = minetest.get_timeofday() * 24000 - - -- Values taken from Minecraft Wiki with offset of +6000 - if tod < 18541 and tod > 5458 and (not weather_mod or (mcl_weather.get_weather() ~= "thunder")) then - if mcl_beds.player[name] then - lay_down(player, nil, nil, false) - end - minetest.chat_send_player(name, "You can only sleep at night or during a thunderstorm.") - return - end -- move to bed if not mcl_beds.player[name] then - lay_down(player, ppos, pos) - if minetest.get_modpath("mcl_spawn") then - local spos = table.copy(pos) - spos.y = spos.y + 0.1 - mcl_spawn.set_spawn_pos(player, spos) -- save respawn position when entering bed + if is_top then + lay_down(player, ppos, pos) + else + local node = minetest.get_node(pos) + local dir = minetest.facedir_to_dir(node.param2) + local other = vector.add(pos, dir) + lay_down(player, ppos, other) end else lay_down(player, nil, nil, false) From 079b09c80f1a01d8ac0a4041c9c83e3963be4c2e Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 19:39:12 +0100 Subject: [PATCH 021/798] Explicit msgs when respawn pos changed due to bed --- mods/ITEMS/mcl_beds/api.lua | 2 +- mods/ITEMS/mcl_beds/functions.lua | 12 ++++++++++-- mods/PLAYER/mcl_spawn/init.lua | 22 +++++++++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/mcl_beds/api.lua b/mods/ITEMS/mcl_beds/api.lua index 708f529c..b6f18ea4 100644 --- a/mods/ITEMS/mcl_beds/api.lua +++ b/mods/ITEMS/mcl_beds/api.lua @@ -37,7 +37,7 @@ local beduse = "To use a bed, stand close to it and right-click the bed to sleep if minetest.settings:get_bool("enable_bed_respawn") == false then beddesc = beddesc .. "\n" .. "In local folklore, legends are told of other worlds where setting the start point for your next life would be possible. But this world is not one of them." else - beddesc = beddesc .. "\n" .. "By sleeping in a bed, you set the starting point for your next life. If you die, you will start your next life at this bed, unless it is obstructed or destroyed." + beddesc = beddesc .. "\n" .. "By using a bed, you set the starting point for your next life. If you die, you will start your next life at this bed, unless it is obstructed or destroyed." end if minetest.settings:get_bool("enable_bed_night_skip") == false then beddesc = beddesc .. "\n" .. "In this strange world, going to bed won't skip the night, but you can skip thunderstorms." diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 2232f6da..e20bb8c5 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -143,19 +143,27 @@ local function lay_down(player, pos, bed_pos, state, skip) return false end + local spawn_changed = false if minetest.get_modpath("mcl_spawn") then local spos = table.copy(bed_pos) spos.y = spos.y + 0.1 - mcl_spawn.set_spawn_pos(player, spos) -- save respawn position when entering bed + spawn_changed = mcl_spawn.set_spawn_pos(player, spos) -- save respawn position when entering bed end -- Check day of time and weather local tod = minetest.get_timeofday() * 24000 -- Values taken from Minecraft Wiki with offset of +6000 if tod < 18541 and tod > 5458 and (not weather_mod or (mcl_weather.get_weather() ~= "thunder")) then - minetest.chat_send_player(name, "You can only sleep at night or during a thunderstorm.") + if spawn_changed then + minetest.chat_send_player(name, "New respawn position set! But you can only sleep at night or during a thunderstorm.") + else + minetest.chat_send_player(name, "You can only sleep at night or during a thunderstorm.") + end return false end + if spawn_changed then + minetest.chat_send_player(name, "New respawn position set!") + end mcl_beds.player[name] = 1 mcl_beds.pos[name] = pos diff --git a/mods/PLAYER/mcl_spawn/init.lua b/mods/PLAYER/mcl_spawn/init.lua index f97f1700..5c8a797e 100644 --- a/mods/PLAYER/mcl_spawn/init.lua +++ b/mods/PLAYER/mcl_spawn/init.lua @@ -32,12 +32,32 @@ end -- Sets the player's spawn position to pos. -- Set pos to nil to clear the spawn position. -mcl_spawn.set_spawn_pos = function(player, pos, type) +-- If message is set, informs the player with a chat message when the spawn position +-- changed. +mcl_spawn.set_spawn_pos = function(player, pos, message) + local spawn_changed = false if pos == nil then + if player:get_attribute("mcl_beds:spawn") ~= "" then + spawn_changed = true + if message then + minetest.chat_send_player(player:get_player_name(), "Respawn position cleared!") + end + end player:set_attribute("mcl_beds:spawn", "") else + local oldpos = minetest.string_to_pos(player:get_attribute("mcl_beds:spawn")) + if oldpos then + -- We don't bother sending a message if the new spawn pos is basically the same + if vector.distance(pos, oldpos) > 0.1 then + spawn_changed = true + if message then + minetest.chat_send_player(player:get_player_name(), "New respawn position set!") + end + end + end player:set_attribute("mcl_beds:spawn", minetest.pos_to_string(pos)) end + return spawn_changed end -- Respawn player at specified respawn position From 88872c8ce253f49f73222a469f3daa056c377f46 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 20:40:06 +0100 Subject: [PATCH 022/798] Can't sleep in occupied beds --- mods/ITEMS/mcl_beds/functions.lua | 14 ++++++++++++-- mods/ITEMS/mcl_beds/init.lua | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index e20bb8c5..5f24f7ba 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -71,6 +71,13 @@ local function lay_down(player, pos, bed_pos, state, skip) return false end + for _, other_pos in pairs(mcl_beds.bed_pos) do + if vector.distance(bed_pos, other_pos) < 0.1 then + minetest.chat_send_player(name, "This bed is already occupied!") + return false + end + end + -- No sleeping while moving. This is a workaround. -- TODO: Ideally, the player speed should be force-set to 0, -- but this is not possible in Minetest 0.4.17. @@ -111,7 +118,7 @@ local function lay_down(player, pos, bed_pos, state, skip) return false end if p then - player:setpos(p) + player:set_pos(p) end -- physics, eye_offset, etc @@ -125,6 +132,8 @@ local function lay_down(player, pos, bed_pos, state, skip) player:set_attribute("mcl_beds:sleeping", "false") hud_flags.wielditem = true mcl_player.player_set_animation(player, "stand" , 30) + mcl_beds.pos[name] = nil + mcl_beds.bed_pos[name] = nil -- lay down else @@ -167,6 +176,7 @@ local function lay_down(player, pos, bed_pos, state, skip) mcl_beds.player[name] = 1 mcl_beds.pos[name] = pos + mcl_beds.bed_pos[name] = bed_pos player_in_bed = player_in_bed + 1 -- physics, eye_offset, etc player:set_eye_offset({x = 0, y = -13, z = 0}, {x = 0, y = 0, z = 0}) @@ -176,7 +186,7 @@ local function lay_down(player, pos, bed_pos, state, skip) player:set_attribute("mcl_beds:sleeping", "true") playerphysics.add_physics_factor(player, "speed", "mcl_beds:sleeping", 0) playerphysics.add_physics_factor(player, "jump", "mcl_beds:sleeping", 0) - player:setpos(p) + player:set_pos(p) mcl_player.player_attached[name] = true hud_flags.wielditem = false mcl_player.player_set_animation(player, "lay" , 0) diff --git a/mods/ITEMS/mcl_beds/init.lua b/mods/ITEMS/mcl_beds/init.lua index 8f60cb54..4c25b539 100644 --- a/mods/ITEMS/mcl_beds/init.lua +++ b/mods/ITEMS/mcl_beds/init.lua @@ -1,6 +1,7 @@ mcl_beds = {} mcl_beds.player = {} mcl_beds.pos = {} +mcl_beds.bed_pos = {} local modpath = minetest.get_modpath("mcl_beds") From 8c672fa3b22a686e07b6b15a75c1bb3b90e3cb86 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 20:45:55 +0100 Subject: [PATCH 023/798] Tweak messages in mcl_beds --- mods/ITEMS/mcl_beds/functions.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 5f24f7ba..67382dfb 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -78,9 +78,7 @@ local function lay_down(player, pos, bed_pos, state, skip) end end - -- No sleeping while moving. This is a workaround. - -- TODO: Ideally, the player speed should be force-set to 0, - -- but this is not possible in Minetest 0.4.17. + -- No sleeping while moving. Slightly different behaviour than in MC. if vector.length(player:get_player_velocity()) > 0.001 then minetest.chat_send_player(name, "You have to stop moving before going to bed!") return false @@ -214,7 +212,7 @@ local function update_formspecs(finished) elseif not is_sp then local text = string.format("%d of %d player(s) are in bed.", player_in_bed, ges) if not night_skip then - text = text .. "\n" .. "You're in bed." .. "\n" .. "Note: Night skip is disabled." + text = text .. "\n" .. "Note: Night skip is disabled." form_n = form_n .. bg_presleep form_n = form_n .. button_leave elseif all_in_bed then @@ -222,7 +220,7 @@ local function update_formspecs(finished) form_n = form_n .. bg_sleep form_n = form_n .. button_abort else - text = text .. "\n" .. "Sleep will commence when all players are in bed." + text = text .. "\n" .. "You will fall asleep when all players are in bed." form_n = form_n .. bg_presleep form_n = form_n .. button_leave end From 7678a1a95fefdf742f39bd39ff11aa249ac12ad5 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 20:52:06 +0100 Subject: [PATCH 024/798] Beds: Fix kick_players_afeter_destruct --- mods/ITEMS/mcl_beds/api.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mods/ITEMS/mcl_beds/api.lua b/mods/ITEMS/mcl_beds/api.lua index b6f18ea4..4fbbe55e 100644 --- a/mods/ITEMS/mcl_beds/api.lua +++ b/mods/ITEMS/mcl_beds/api.lua @@ -20,9 +20,9 @@ local function destruct_bed(pos, is_top) end end -local function kick_player_after_destruct(pos) - for name, _ in pairs(mcl_beds.pos) do - if vector.equals(pos, mcl_beds.pos) then +local function kick_player_after_destruct(destruct_pos) + for name, player_bed_pos in pairs(mcl_beds.bed_pos) do + if vector.distance(destruct_pos, player_bed_pos) < 0.1 then local player = minetest.get_player_by_name(name) if player and player:is_player() then mcl_beds.kick_player(player) From 686b575f81f36471e6ccb6b5e845b0b9300409e7 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 20:59:01 +0100 Subject: [PATCH 025/798] Beds: Fix player model offset when laying down --- mods/ITEMS/mcl_beds/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 67382dfb..6824cabe 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -137,7 +137,7 @@ local function lay_down(player, pos, bed_pos, state, skip) else local yaw, param2 = get_look_yaw(bed_pos) local dir = minetest.facedir_to_dir(param2) - local p = {x = bed_pos.x + dir.x / 4, y = bed_pos.y, z = bed_pos.z + dir.z / 4} + local p = {x = bed_pos.x - dir.x/2, y = bed_pos.y, z = bed_pos.z - dir.z/2} local n1 = minetest.get_node({x=bed_pos.x, y=bed_pos.y+1, z=bed_pos.z}) local n2 = minetest.get_node({x=bed_pos.x, y=bed_pos.y+2, z=bed_pos.z}) local def1 = minetest.registered_nodes[n1.name] From 99741f3d2a7b2a0a0323b55ab53d5d4033242291 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 21:03:25 +0100 Subject: [PATCH 026/798] Beds: Update formspecs when player leaves/joins --- mods/ITEMS/mcl_beds/functions.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 6824cabe..018cf74f 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -349,6 +349,7 @@ minetest.register_on_joinplayer(function(player) end playerphysics.remove_physics_factor(player, "speed", "mcl_beds:sleeping") playerphysics.remove_physics_factor(player, "jump", "mcl_beds:sleeping") + update_formspecs(false) end) minetest.register_on_leaveplayer(function(player) @@ -363,6 +364,7 @@ minetest.register_on_leaveplayer(function(player) end end) end + update_formspecs(false) end) minetest.register_on_player_receive_fields(function(player, formname, fields) From 00851220287becbd83186f2a2ea3b2aac76eb85a Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 20 Feb 2019 21:09:04 +0100 Subject: [PATCH 027/798] Slightly rewrite a bed formspec string --- mods/ITEMS/mcl_beds/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 018cf74f..02f72ff5 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -210,7 +210,7 @@ local function update_formspecs(finished) end return elseif not is_sp then - local text = string.format("%d of %d player(s) are in bed.", player_in_bed, ges) + local text = string.format("Players in bed: %d/%d", player_in_bed, ges) if not night_skip then text = text .. "\n" .. "Note: Night skip is disabled." form_n = form_n .. bg_presleep From 31668cdde516a955bea9e49cfbd6a287fa11f61c Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 21 Feb 2019 02:37:13 +0100 Subject: [PATCH 028/798] Add brown mooshroom --- mods/ENTITIES/mobs_mc/LICENSE-media.md | 5 ++++- mods/ENTITIES/mobs_mc/cow+mooshroom.lua | 9 +++++++-- .../mobs_mc/textures/mobs_mc_mooshroom_brown.png | Bin 0 -> 1048 bytes .../mobs_mc/textures/mobs_mc_mushroom_brown.png | Bin 0 -> 374 bytes mods/ENTITIES/mobs_mc_gameconfig/init.lua | 1 + mods/ENVIRONMENT/lightning/init.lua | 8 ++++++++ 6 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 mods/ENTITIES/mobs_mc/textures/mobs_mc_mooshroom_brown.png create mode 100644 mods/ENTITIES/mobs_mc/textures/mobs_mc_mushroom_brown.png diff --git a/mods/ENTITIES/mobs_mc/LICENSE-media.md b/mods/ENTITIES/mobs_mc/LICENSE-media.md index fe5c41e7..7cad2241 100644 --- a/mods/ENTITIES/mobs_mc/LICENSE-media.md +++ b/mods/ENTITIES/mobs_mc/LICENSE-media.md @@ -70,6 +70,9 @@ Origin of those models: * `mobs_mc_wither.png` * `mobs_mc_wither_skeleton.png` * `mobs_mc_TEMP_wither_projectile.png` + * Gerold55 + * `mobs_mc_mooshroom_brown.png` (CC0) + * `mobs_mc_mushroom_brown.png` (CC0) * “Spawn egg” textures (`mobs_mc_spawn_icon_*`) by 22i * Any other texture not mentioned here are licensed under the MIT License @@ -183,4 +186,4 @@ Origin of those models: Note: Many of these sounds have been more or less modified to fit the game. -Sounds not mentioned here are licensed under CC0. +Sounds not mentioned hre are licensed under CC0. diff --git a/mods/ENTITIES/mobs_mc/cow+mooshroom.lua b/mods/ENTITIES/mobs_mc/cow+mooshroom.lua index bf1f9c1d..48a6aac7 100644 --- a/mods/ENTITIES/mobs_mc/cow+mooshroom.lua +++ b/mods/ENTITIES/mobs_mc/cow+mooshroom.lua @@ -80,7 +80,7 @@ mobs:register_mob("mobs_mc:cow", cow_def) local mooshroom_def = table.copy(cow_def) mooshroom_def.mesh = "mobs_mc_cow.b3d" -mooshroom_def.textures = { {"mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png"}, } +mooshroom_def.textures = { {"mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png"}, {"mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" } } mooshroom_def.on_rightclick = function(self, clicker) if mobs:feed_tame(self, clicker, 1, true, true) then return end if mobs:protect(self, clicker) then return end @@ -93,7 +93,12 @@ mooshroom_def.on_rightclick = function(self, clicker) if item:get_name() == mobs_mc.items.shears then local pos = self.object:get_pos() minetest.sound_play("shears", {pos = pos}) - minetest.add_item({x=pos.x, y=pos.y+1.4, z=pos.z}, mobs_mc.items.mushroom_red .. " 5") + + if self.base_texture[1] == "mobs_mc_mooshroom_brown.png" then + minetest.add_item({x=pos.x, y=pos.y+1.4, z=pos.z}, mobs_mc.items.mushroom_brown .. " 5") + else + minetest.add_item({x=pos.x, y=pos.y+1.4, z=pos.z}, mobs_mc.items.mushroom_red .. " 5") + end local oldyaw = self.object:getyaw() self.object:remove() diff --git a/mods/ENTITIES/mobs_mc/textures/mobs_mc_mooshroom_brown.png b/mods/ENTITIES/mobs_mc/textures/mobs_mc_mooshroom_brown.png new file mode 100644 index 0000000000000000000000000000000000000000..115416a53c8a5e525b8f245dac8f40f6f8be1f19 GIT binary patch literal 1048 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{V~-O<;Pfnj4m_n$;o z1_lPs0*}aI1_r)EAj~ML;nl#vz#viL8c`CQpH@<#W5tq`R@_NM(i(&pCYze_Muk^5os`_25V-z{ezIdb(%Re8-8!R7k%-ZlI`yZN|y z*`gUH`)V!kcC3BCczyZx^^#z*VO&bYM-?C+tcY{bF}x=tlUxE@JGbw*2 zH+}P`E!ICSoTykd=MCq}Z%vB{KpmvtJpm8#rE5dr&RA* zx3xxbS?u(J!iC)be*H-7&goC>{kEpu=)KXNWAY5UPp{3^jh`!EXLsut7x&HJ`9d{$ z*7LUWU)jaiKcVhz>Ds5lfAy12P}Q) z^RuKy$L9S@FVIvip1^)$$9It@ojs|0j_sSrE-=qIIH&*YL(|yUZz}qK`7BM#{xhhQ zZd{Vm#xC$=$GxCfu#_@b>f2A|2S=7Q{=V~m%9(@m3tqd=d-15LqWACj`j$P4ydNyz zO^!9%{)o{gF8RCt*Ubl#Jx|_Q!TuttX~&9&t-WRVcnd*;~tlJo_{kQRO>%*Uu-DfXu|IWU@ zh}G%e@rnFw_qKf2P4v@#DW@3NS9E@P+}b+k+mahu4|}iHU|?Wi@O1TaS?83{1OTaz B0l)wN literal 0 HcmV?d00001 diff --git a/mods/ENTITIES/mobs_mc/textures/mobs_mc_mushroom_brown.png b/mods/ENTITIES/mobs_mc/textures/mobs_mc_mushroom_brown.png new file mode 100644 index 0000000000000000000000000000000000000000..fac0f56ef0c934a782294a77e9a780e77c75ca6b GIT binary patch literal 374 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s77>k44ofy`glX=O&z`&C3 z=y_ah~ zEDEYF<7H!Dh^m-1D|X)9olI9w+Oa7(g&khkYPj_sYryiF$#xE0#z&31&+fTy#`W6o z-Vt{v28NewKB#0#mdoE`@MUKxdHC6CvaeKRz}u^JQ%)$pzb)XvgTe~DWM4fnB$Ju literal 0 HcmV?d00001 diff --git a/mods/ENTITIES/mobs_mc_gameconfig/init.lua b/mods/ENTITIES/mobs_mc_gameconfig/init.lua index ff02ccfd..f24952e9 100644 --- a/mods/ENTITIES/mobs_mc_gameconfig/init.lua +++ b/mods/ENTITIES/mobs_mc_gameconfig/init.lua @@ -44,6 +44,7 @@ mobs_mc.override.items = { shears = "mcl_tools:shears", mushroom_red = "mcl_mushrooms:mushroom_red", + mushroom_brown = "mcl_mushrooms:mushroom_brown", bucket = "mcl_buckets:bucket_empty", grass_block = "mcl_core:dirt_with_grass", string = "mcl_mobitems:string", diff --git a/mods/ENVIRONMENT/lightning/init.lua b/mods/ENVIRONMENT/lightning/init.lua index dfcfe014..207c4d1a 100644 --- a/mods/ENVIRONMENT/lightning/init.lua +++ b/mods/ENVIRONMENT/lightning/init.lua @@ -189,6 +189,14 @@ lightning.strike = function(pos) obj:remove() obj = minetest.add_entity(pos2, "mobs_mc:pigman") obj:set_yaw(rot) + -- mooshroom: toggle color red/brown + elseif lua.name == "mobs_mc:mooshroom" then + if lua.base_texture[1] == "mobs_mc_mooshroom.png" then + lua.base_texture = { "mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" } + else + lua.base_texture = { "mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png" } + end + obj:set_properties({textures = lua.base_texture}) -- villager → witch elseif lua.name == "mobs_mc:villager" then -- Witches are incomplete, this code is unused From 93c087997fe4140aee12c4c42232669008bce45f Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 21 Feb 2019 03:36:02 +0100 Subject: [PATCH 029/798] Add /lightning command --- mods/ENVIRONMENT/lightning/init.lua | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/mods/ENVIRONMENT/lightning/init.lua b/mods/ENVIRONMENT/lightning/init.lua index 207c4d1a..e3792f7e 100644 --- a/mods/ENVIRONMENT/lightning/init.lua +++ b/mods/ENVIRONMENT/lightning/init.lua @@ -230,3 +230,33 @@ minetest.after(5, function(dtime) lightning.interval_high), lightning.strike) end end) + +minetest.register_chatcommand("lightning", { + params = "[ ]", + description = "Let lightning strike at the specified position or yourself", + privs = { maphack = true }, + func = function(name, param) + local pos = {} + pos.x, pos.y, pos.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") + pos.x = tonumber(pos.x) + pos.y = tonumber(pos.y) + pos.z = tonumber(pos.z) + if not (pos.x and pos.y and pos.z) then + pos = nil + end + if name == "" and pos == nil then + return false, "No position specified and unknown player" + end + if pos then + lightning.strike(pos) + else + local player = minetest.get_player_by_name(name) + if player then + lightning.strike(player:get_pos()) + else + return false, "No position specified and unknown player" + end + end + return true + end, +}) From 236ef99359ee930f1c1604d006efcd9288200668 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 21 Feb 2019 18:08:30 +0100 Subject: [PATCH 030/798] Add hard limits to book title and book text length --- mods/ITEMS/mcl_books/init.lua | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/mods/ITEMS/mcl_books/init.lua b/mods/ITEMS/mcl_books/init.lua index de96e9e3..a2627c77 100644 --- a/mods/ITEMS/mcl_books/init.lua +++ b/mods/ITEMS/mcl_books/init.lua @@ -1,3 +1,6 @@ +local max_text_length = 4500 -- TODO: Increase to 12800 when scroll bar was added to written book +local max_title_length = 64 + -- Book minetest.register_craftitem("mcl_books:book", { description = "Book", @@ -59,6 +62,10 @@ local make_description = function(title, author, generation) return desc end +local cap_text_length = function(text, max_length) + return string.sub(text, 1, max_length) +end + local write = function(itemstack, user, pointed_thing) -- Call on_rightclick if the pointed node defines it if pointed_thing.type == "node" then @@ -102,7 +109,8 @@ end minetest.register_craftitem("mcl_books:writable_book", { description = "Book and Quill", _doc_items_longdesc = "This item can be used to write down some notes.", - _doc_items_usagehelp = "Hold it in the hand, then rightclick to read the current notes and edit then. You can edit the text as often as you like. You can also sign the book which turns it into a written book which you can stack, but it can't be edited anymore.", + _doc_items_usagehelp = "Hold it in the hand, then rightclick to read the current notes and edit then. You can edit the text as often as you like. You can also sign the book which turns it into a written book which you can stack, but it can't be edited anymore.".."\n".. + "A book can hold up to 4500 characters. The title length is limited to 64 characters.", inventory_image = "mcl_books_book_writable.png", groups = { book=1 }, stack_max = 1, @@ -115,11 +123,12 @@ minetest.register_on_player_receive_fields(function ( player, formname, fields ) local stack = player:get_wielded_item() if (stack:get_name() and (stack:get_name() == "mcl_books:writable_book")) then local meta = stack:get_meta() + local text = cap_text_length(fields.text, max_text_length) if fields.ok then - meta:set_string("text", fields.text) + meta:set_string("text", text) player:set_wielded_item(stack) elseif fields.sign then - meta:set_string("text", fields.text) + meta:set_string("text", text) player:set_wielded_item(stack) local name = player:get_player_name() @@ -138,15 +147,17 @@ minetest.register_on_player_receive_fields(function ( player, formname, fields ) local book = player:get_wielded_item() local name = player:get_player_name() if book:get_name() == "mcl_books:writable_book" then - if fields.title == "" then - fields.title = "Nameless Book" + local title = fields.title + if string.len(title) == 0 then + title = "Nameless Book" end + title = cap_text_length(title, max_title_length) local meta = newbook:get_meta() - local text = get_text(book) - meta:set_string("title", fields.title) + local text = cap_text_length(get_text(book), max_text_length) + meta:set_string("title", title) meta:set_string("author", name) meta:set_string("text", text) - meta:set_string("description", make_description(fields.title, name, 0)) + meta:set_string("description", make_description(title, name, 0)) -- The book copy counter. 0 = original, 1 = copy of original, 2 = copy of copy of original, … meta:set_int("generation", 0) @@ -235,7 +246,7 @@ minetest.register_craft_predict(function(itemstack, player, old_craft_grid, craf -- Valid copy. Let's update the description field of the result item -- so it is properly displayed in the crafting grid. local imeta = itemstack:get_meta() - local title = ometa:get_string("title") + local title = cap_text_length(ometa:get_string("title"), max_title_length) local author = ometa:get_string("author") -- Increase book generation and update description @@ -283,11 +294,11 @@ minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv -- Copy metadata local imeta = itemstack:get_meta() - local title = ometa:get_string("title") + local title = cap_text_length(ometa:get_string("title"), max_title_length) local author = ometa:get_string("author") imeta:set_string("title", title) imeta:set_string("author", author) - imeta:set_string("text", text) + imeta:set_string("text", cap_text_length(text, max_text_length)) -- Increase book generation and update description generation = generation + 1 From e614f9228a806184a5132cf998fe437daa9abcf3 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 21 Feb 2019 23:08:51 +0100 Subject: [PATCH 031/798] Version 0.46.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10aa7bcf..9654821f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An unofficial Minecraft-like game for Minetest. Forked from MineClone by daredevils. Developed by Wuzzy and contributors. Not developed or endorsed by Mojang AB. -Version: 0.45.1 +Version: 0.46.0 ### Gameplay You start in a randomly-generated world made entirely of cubes. You can explore From 2028ef40cb61d0e6e1fa99d50f6e803b1ed40720 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 22 Feb 2019 05:29:17 +0100 Subject: [PATCH 032/798] Add screwdriver --- mods/ITEMS/screwdriver/README.txt | 13 ++ mods/ITEMS/screwdriver/init.lua | 170 ++++++++++++++++++ mods/ITEMS/screwdriver/license.txt | 50 ++++++ .../screwdriver/textures/screwdriver.png | Bin 0 -> 182 bytes 4 files changed, 233 insertions(+) create mode 100644 mods/ITEMS/screwdriver/README.txt create mode 100644 mods/ITEMS/screwdriver/init.lua create mode 100644 mods/ITEMS/screwdriver/license.txt create mode 100644 mods/ITEMS/screwdriver/textures/screwdriver.png diff --git a/mods/ITEMS/screwdriver/README.txt b/mods/ITEMS/screwdriver/README.txt new file mode 100644 index 00000000..9d39c58c --- /dev/null +++ b/mods/ITEMS/screwdriver/README.txt @@ -0,0 +1,13 @@ +Minetest Game mod: screwdriver +============================== +See license.txt for license information. + +License of source code +---------------------- +Originally by RealBadAngel, Maciej Kasatkin (LGPL 2.1) +Various Minetest developers and contributors (LGPL 2.1) + +License of media (textures) +--------------------------- +Created by Gambit (CC BY-SA 3.0): + screwdriver.png diff --git a/mods/ITEMS/screwdriver/init.lua b/mods/ITEMS/screwdriver/init.lua new file mode 100644 index 00000000..4d38bbc2 --- /dev/null +++ b/mods/ITEMS/screwdriver/init.lua @@ -0,0 +1,170 @@ +screwdriver = {} + +screwdriver.ROTATE_FACE = 1 +screwdriver.ROTATE_AXIS = 2 +screwdriver.disallow = function(pos, node, user, mode, new_param2) + return false +end +screwdriver.rotate_simple = function(pos, node, user, mode, new_param2) + if mode ~= screwdriver.ROTATE_FACE then + return false + end +end + +-- For attached wallmounted nodes: returns true if rotation is valid +-- simplified version of minetest:builtin/game/falling.lua#L148. +local function check_attached_node(pos, rotation) + local d = minetest.wallmounted_to_dir(rotation) + local p2 = vector.add(pos, d) + local n = minetest.get_node(p2).name + local def2 = minetest.registered_nodes[n] + if def2 and not def2.walkable then + return false + end + return true +end + +screwdriver.rotate = {} + +local facedir_tbl = { + [screwdriver.ROTATE_FACE] = { + [0] = 1, [1] = 2, [2] = 3, [3] = 0, + [4] = 5, [5] = 6, [6] = 7, [7] = 4, + [8] = 9, [9] = 10, [10] = 11, [11] = 8, + [12] = 13, [13] = 14, [14] = 15, [15] = 12, + [16] = 17, [17] = 18, [18] = 19, [19] = 16, + [20] = 21, [21] = 22, [22] = 23, [23] = 20, + }, + [screwdriver.ROTATE_AXIS] = { + [0] = 4, [1] = 4, [2] = 4, [3] = 4, + [4] = 8, [5] = 8, [6] = 8, [7] = 8, + [8] = 12, [9] = 12, [10] = 12, [11] = 12, + [12] = 16, [13] = 16, [14] = 16, [15] = 16, + [16] = 20, [17] = 20, [18] = 20, [19] = 20, + [20] = 0, [21] = 0, [22] = 0, [23] = 0, + }, +} + +screwdriver.rotate.facedir = function(pos, node, mode) + local rotation = node.param2 % 32 -- get first 5 bits + local other = node.param2 - rotation + rotation = facedir_tbl[mode][rotation] or 0 + return rotation + other +end + +screwdriver.rotate.colorfacedir = screwdriver.rotate.facedir + +local wallmounted_tbl = { + [screwdriver.ROTATE_FACE] = {[2] = 5, [3] = 4, [4] = 2, [5] = 3, [1] = 0, [0] = 1}, + [screwdriver.ROTATE_AXIS] = {[2] = 5, [3] = 4, [4] = 2, [5] = 1, [1] = 0, [0] = 3} +} + +screwdriver.rotate.wallmounted = function(pos, node, mode) + local rotation = node.param2 % 8 -- get first 3 bits + local other = node.param2 - rotation + rotation = wallmounted_tbl[mode][rotation] or 0 + if minetest.get_item_group(node.name, "attached_node") ~= 0 then + -- find an acceptable orientation + for i = 1, 5 do + if not check_attached_node(pos, rotation) then + rotation = wallmounted_tbl[mode][rotation] or 0 + else + break + end + end + end + return rotation + other +end + +screwdriver.rotate.colorwallmounted = screwdriver.rotate.wallmounted + +-- Handles rotation +screwdriver.handler = function(itemstack, user, pointed_thing, mode, uses) + if pointed_thing.type ~= "node" then + return + end + + local pos = pointed_thing.under + local player_name = user and user:get_player_name() or "" + + if minetest.is_protected(pos, player_name) then + minetest.record_protection_violation(pos, player_name) + return + end + + local node = minetest.get_node(pos) + local ndef = minetest.registered_nodes[node.name] + if not ndef then + return itemstack + end + -- can we rotate this paramtype2? + local fn = screwdriver.rotate[ndef.paramtype2] + if not fn and not ndef.on_rotate then + return itemstack + end + + local should_rotate = true + local new_param2 + if fn then + new_param2 = fn(pos, node, mode) + else + new_param2 = node.param2 + end + + -- Node provides a handler, so let the handler decide instead if the node can be rotated + if ndef.on_rotate then + -- Copy pos and node because callback can modify it + local result = ndef.on_rotate(vector.new(pos), + {name = node.name, param1 = node.param1, param2 = node.param2}, + user, mode, new_param2) + if result == false then -- Disallow rotation + return itemstack + elseif result == true then + should_rotate = false + end + elseif ndef.on_rotate == false then + return itemstack + elseif ndef.can_dig and not ndef.can_dig(pos, user) then + return itemstack + end + + if should_rotate and new_param2 ~= node.param2 then + node.param2 = new_param2 + minetest.swap_node(pos, node) + minetest.check_for_falling(pos) + end + + if not (minetest.settings:get_bool("creative_mode")) then + itemstack:add_wear(65535 / ((uses or 200) - 1)) + end + + return itemstack +end + +-- Screwdriver +minetest.register_tool("screwdriver:screwdriver", { + description = "Screwdriver", + inventory_image = "screwdriver.png", + on_use = function(itemstack, user, pointed_thing) + screwdriver.handler(itemstack, user, pointed_thing, screwdriver.ROTATE_FACE, 200) + return itemstack + end, + on_place = function(itemstack, user, pointed_thing) + screwdriver.handler(itemstack, user, pointed_thing, screwdriver.ROTATE_AXIS, 200) + return itemstack + end, +}) + + +minetest.register_craft({ + output = "screwdriver:screwdriver", + recipe = { + {"mcl_core:iron_ingot"}, + {"mcl_core:stick"} + } +}) + +minetest.register_alias("screwdriver:screwdriver1", "screwdriver:screwdriver") +minetest.register_alias("screwdriver:screwdriver2", "screwdriver:screwdriver") +minetest.register_alias("screwdriver:screwdriver3", "screwdriver:screwdriver") +minetest.register_alias("screwdriver:screwdriver4", "screwdriver:screwdriver") diff --git a/mods/ITEMS/screwdriver/license.txt b/mods/ITEMS/screwdriver/license.txt new file mode 100644 index 00000000..d9b721bb --- /dev/null +++ b/mods/ITEMS/screwdriver/license.txt @@ -0,0 +1,50 @@ +License of source code +---------------------- + +GNU Lesser General Public License, version 2.1 +Copyright (C) 2013-2016 RealBadAngel, Maciej Kasatkin +Copyright (C) 2013-2016 Various Minetest developers and contributors + +This program is free software; you can redistribute it and/or modify it under the terms +of the GNU Lesser General Public License as published by the Free Software Foundation; +either version 2.1 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details: +https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html + + +Licenses of media (textures) +---------------------------- + +Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) +Copyright (C) 2013-2016 Gambit + +You are free to: +Share — copy and redistribute the material in any medium or format. +Adapt — remix, transform, and build upon the material for any purpose, even commercially. +The licensor cannot revoke these freedoms as long as you follow the license terms. + +Under the following terms: + +Attribution — You must give appropriate credit, provide a link to the license, and +indicate if changes were made. You may do so in any reasonable manner, but not in any way +that suggests the licensor endorses you or your use. + +ShareAlike — If you remix, transform, or build upon the material, you must distribute +your contributions under the same license as the original. + +No additional restrictions — You may not apply legal terms or technological measures that +legally restrict others from doing anything the license permits. + +Notices: + +You do not have to comply with the license for elements of the material in the public +domain or where your use is permitted by an applicable exception or limitation. +No warranties are given. The license may not give you all of the permissions necessary +for your intended use. For example, other rights such as publicity, privacy, or moral +rights may limit how you use the material. + +For more details: +http://creativecommons.org/licenses/by-sa/3.0/ diff --git a/mods/ITEMS/screwdriver/textures/screwdriver.png b/mods/ITEMS/screwdriver/textures/screwdriver.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a56d558b34392a3275da443c0ba32a3b4db340 GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdq5|d_r7(G7EiF`7$#yc628s z7z)39`EqWWSx=l*xtH3CQum7&FUG~i<>uyAR#xuYw{K^Hk@fK_H)R%)Y32O5POM=Q-*yMdNzVclr=>h< gNl@c3I>5*vUczopr0RG23A^-pY literal 0 HcmV?d00001 From 6e8be2a2c386b79b984927217e120fc50fcf4dae Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 22 Feb 2019 06:08:48 +0100 Subject: [PATCH 033/798] Fix vine not dropping all vines when dug w/ shears --- mods/ITEMS/mcl_core/nodes_climb.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/mcl_core/nodes_climb.lua b/mods/ITEMS/mcl_core/nodes_climb.lua index 52818695..25f8cdb5 100644 --- a/mods/ITEMS/mcl_core/nodes_climb.lua +++ b/mods/ITEMS/mcl_core/nodes_climb.lua @@ -141,13 +141,14 @@ minetest.register_node("mcl_core:vine", { return itemstack end, - -- If destroyed, also a “dependant” vine below it. + -- If dug, also dig a “dependant” vine below it. -- A vine is dependant if it hangs from this node and has no supporting block. - after_destruct = function(pos, oldnode) + on_dig = function(pos, node, digger) local below = {x=pos.x, y=pos.y-1, z=pos.z} local belownode = minetest.get_node(below) - if belownode.name == oldnode.name and (not mcl_core.check_vines_supported(below, belownode)) then - minetest.remove_node(below) + minetest.node_dig(pos, node, digger) + if belownode.name == node.name and (not mcl_core.check_vines_supported(below, belownode)) then + minetest.registered_nodes[node.name].on_dig(below, node, digger) end end, From 1556e6cb3e046a28ac3f29cf54418cf4e6943042 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 22 Feb 2019 06:13:31 +0100 Subject: [PATCH 034/798] Ice: Use after_dig_node instead of after_destruct --- mods/ITEMS/mcl_core/nodes_base.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_core/nodes_base.lua b/mods/ITEMS/mcl_core/nodes_base.lua index 237eff39..81395010 100644 --- a/mods/ITEMS/mcl_core/nodes_base.lua +++ b/mods/ITEMS/mcl_core/nodes_base.lua @@ -786,7 +786,7 @@ minetest.register_node("mcl_core:ice", { groups = {handy=1,pickaxey=1, slippery=3, building_block=1}, drop = "", sounds = mcl_sounds.node_sound_glass_defaults(), - after_destruct = function(pos, oldnode) + after_dig_node = function(pos, oldnode) -- Create a water source if ice is destroyed and there was something below it local below = {x=pos.x, y=pos.y-1, z=pos.z} local belownode = minetest.get_node(below) From 1044e9690954ac5c45b0878303d8d0eb639e6f4e Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 25 Feb 2019 17:46:13 +0100 Subject: [PATCH 035/798] Add tool groups (pickaxe, shovel, ...) --- GROUPS.md | 7 +++++++ mods/ITEMS/mcl_farming/hoes.lua | 10 +++++----- mods/ITEMS/mcl_tools/init.lua | 32 ++++++++++++++++---------------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/GROUPS.md b/GROUPS.md index 57c90eb4..70f61247 100644 --- a/GROUPS.md +++ b/GROUPS.md @@ -166,6 +166,13 @@ These groups are used mostly for informational purposes * `plant=1`: Plant or part of a plant * `double_plant`: Part of a double-sized plant. 1 = lower part, 2 = upper part +* `pickaxe=1`: Pickaxe +* `shovel=1`: Shovel +* `axe=1`: Axe +* `sword=1`: Sword +* `hoe=1`: Hoe (farming tool) +* `shears=1`: Shears + * `weapon=1`: Item is primarily (!) a weapon * `tool=1`: Item is primarily (!) a tool * `craftitem=1`: Item is primarily (!) used for crafting diff --git a/mods/ITEMS/mcl_farming/hoes.lua b/mods/ITEMS/mcl_farming/hoes.lua index bc048f57..1d8e9411 100644 --- a/mods/ITEMS/mcl_farming/hoes.lua +++ b/mods/ITEMS/mcl_farming/hoes.lua @@ -48,7 +48,7 @@ minetest.register_tool("mcl_farming:hoe_wood", { return itemstack end end, - groups = { tool=1 }, + groups = { tool=1, hoe=1 }, tool_capabilities = { full_punch_interval = 1, damage_groups = { fleshy = 1, } @@ -99,7 +99,7 @@ minetest.register_tool("mcl_farming:hoe_stone", { return itemstack end end, - groups = { tool=1 }, + groups = { tool=1, hoe=1 }, tool_capabilities = { full_punch_interval = 0.5, damage_groups = { fleshy = 1, } @@ -145,7 +145,7 @@ minetest.register_tool("mcl_farming:hoe_iron", { return itemstack end end, - groups = { tool=1 }, + groups = { tool=1, hoe=1 }, tool_capabilities = { -- 1/3 full_punch_interval = 0.33333333, @@ -199,7 +199,7 @@ minetest.register_tool("mcl_farming:hoe_gold", { return itemstack end end, - groups = { tool=1 }, + groups = { tool=1, hoe=1 }, tool_capabilities = { full_punch_interval = 1, damage_groups = { fleshy = 1, } @@ -254,7 +254,7 @@ minetest.register_tool("mcl_farming:hoe_diamond", { return itemstack end end, - groups = { tool=1 }, + groups = { tool=1, hoe=1 }, tool_capabilities = { full_punch_interval = 0.25, damage_groups = { fleshy = 1, } diff --git a/mods/ITEMS/mcl_tools/init.lua b/mods/ITEMS/mcl_tools/init.lua index c60aafe1..b5f8b303 100644 --- a/mods/ITEMS/mcl_tools/init.lua +++ b/mods/ITEMS/mcl_tools/init.lua @@ -64,7 +64,7 @@ minetest.register_tool("mcl_tools:pick_wood", { _doc_items_longdesc = pickaxe_longdesc, _doc_items_hidden = false, inventory_image = "default_tool_woodpick.png", - groups = { tool=1 }, + groups = { tool=1, pickaxe=1 }, tool_capabilities = { -- 1/1.2 full_punch_interval = 0.83333333, @@ -81,7 +81,7 @@ minetest.register_tool("mcl_tools:pick_stone", { description = "Stone Pickaxe", _doc_items_longdesc = pickaxe_longdesc, inventory_image = "default_tool_stonepick.png", - groups = { tool=1 }, + groups = { tool=1, pickaxe=1 }, tool_capabilities = { -- 1/1.2 full_punch_interval = 0.83333333, @@ -98,7 +98,7 @@ minetest.register_tool("mcl_tools:pick_iron", { description = "Iron Pickaxe", _doc_items_longdesc = pickaxe_longdesc, inventory_image = "default_tool_steelpick.png", - groups = { tool=1 }, + groups = { tool=1, pickaxe=1 }, tool_capabilities = { -- 1/1.2 full_punch_interval = 0.83333333, @@ -115,7 +115,7 @@ minetest.register_tool("mcl_tools:pick_gold", { description = "Golden Pickaxe", _doc_items_longdesc = pickaxe_longdesc, inventory_image = "default_tool_goldpick.png", - groups = { tool=1 }, + groups = { tool=1, pickaxe=1 }, tool_capabilities = { -- 1/1.2 full_punch_interval = 0.83333333, @@ -132,7 +132,7 @@ minetest.register_tool("mcl_tools:pick_diamond", { description = "Diamond Pickaxe", _doc_items_longdesc = pickaxe_longdesc, inventory_image = "default_tool_diamondpick.png", - groups = { tool=1 }, + groups = { tool=1, pickaxe=1 }, tool_capabilities = { -- 1/1.2 full_punch_interval = 0.83333333, @@ -244,7 +244,7 @@ minetest.register_tool("mcl_tools:shovel_wood", { _doc_items_hidden = false, inventory_image = "default_tool_woodshovel.png", wield_image = "default_tool_woodshovel.png^[transformR90", - groups = { tool=1 }, + groups = { tool=1, shovel=1 }, tool_capabilities = { full_punch_interval = 1, max_drop_level=1, @@ -263,7 +263,7 @@ minetest.register_tool("mcl_tools:shovel_stone", { _doc_items_usagehelp = shovel_use, inventory_image = "default_tool_stoneshovel.png", wield_image = "default_tool_stoneshovel.png^[transformR90", - groups = { tool=1 }, + groups = { tool=1, shovel=1 }, tool_capabilities = { full_punch_interval = 1, max_drop_level=3, @@ -282,7 +282,7 @@ minetest.register_tool("mcl_tools:shovel_iron", { _doc_items_usagehelp = shovel_use, inventory_image = "default_tool_steelshovel.png", wield_image = "default_tool_steelshovel.png^[transformR90", - groups = { tool=1 }, + groups = { tool=1, shovel=1 }, tool_capabilities = { full_punch_interval = 1, max_drop_level=4, @@ -301,7 +301,7 @@ minetest.register_tool("mcl_tools:shovel_gold", { _doc_items_usagehelp = shovel_use, inventory_image = "default_tool_goldshovel.png", wield_image = "default_tool_goldshovel.png^[transformR90", - groups = { tool=1 }, + groups = { tool=1, shovel=1 }, tool_capabilities = { full_punch_interval = 1, max_drop_level=2, @@ -320,7 +320,7 @@ minetest.register_tool("mcl_tools:shovel_diamond", { _doc_items_usagehelp = shovel_use, inventory_image = "default_tool_diamondshovel.png", wield_image = "default_tool_diamondshovel.png^[transformR90", - groups = { tool=1 }, + groups = { tool=1, shovel=1 }, tool_capabilities = { full_punch_interval = 1, max_drop_level=5, @@ -340,7 +340,7 @@ minetest.register_tool("mcl_tools:axe_wood", { _doc_items_longdesc = axe_longdesc, _doc_items_hidden = false, inventory_image = "default_tool_woodaxe.png", - groups = { tool=1 }, + groups = { tool=1, axe=1 }, tool_capabilities = { full_punch_interval = 1.25, max_drop_level=1, @@ -356,7 +356,7 @@ minetest.register_tool("mcl_tools:axe_stone", { description = "Stone Axe", _doc_items_longdesc = axe_longdesc, inventory_image = "default_tool_stoneaxe.png", - groups = { tool=1 }, + groups = { tool=1, axe=1 }, tool_capabilities = { full_punch_interval = 1.25, max_drop_level=3, @@ -372,7 +372,7 @@ minetest.register_tool("mcl_tools:axe_iron", { description = "Iron Axe", _doc_items_longdesc = axe_longdesc, inventory_image = "default_tool_steelaxe.png", - groups = { tool=1 }, + groups = { tool=1, axe=1 }, tool_capabilities = { -- 1/0.9 full_punch_interval = 1.11111111, @@ -389,7 +389,7 @@ minetest.register_tool("mcl_tools:axe_gold", { description = "Golden Axe", _doc_items_longdesc = axe_longdesc, inventory_image = "default_tool_goldaxe.png", - groups = { tool=1 }, + groups = { tool=1, axe=1 }, tool_capabilities = { full_punch_interval = 1.0, max_drop_level=2, @@ -405,7 +405,7 @@ minetest.register_tool("mcl_tools:axe_diamond", { description = "Diamond Axe", _doc_items_longdesc = axe_longdesc, inventory_image = "default_tool_diamondaxe.png", - groups = { tool=1 }, + groups = { tool=1, axe=1 }, tool_capabilities = { full_punch_interval = 1.0, max_drop_level=5, @@ -514,7 +514,7 @@ minetest.register_tool("mcl_tools:shears", { inventory_image = "default_tool_shears.png", wield_image = "default_tool_shears.png", stack_max = 1, - groups = { tool=1 }, + groups = { tool=1, shears=1 }, tool_capabilities = { full_punch_interval = 0.5, max_drop_level=1, From 6497916adec46519df18230acaca52b196980ed0 Mon Sep 17 00:00:00 2001 From: nickolas360 Date: Thu, 28 Feb 2019 15:35:18 +0100 Subject: [PATCH 036/798] Fix spawning at bed when chunk is unloaded --- mods/PLAYER/mcl_spawn/init.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mods/PLAYER/mcl_spawn/init.lua b/mods/PLAYER/mcl_spawn/init.lua index 5c8a797e..bf8c1344 100644 --- a/mods/PLAYER/mcl_spawn/init.lua +++ b/mods/PLAYER/mcl_spawn/init.lua @@ -60,15 +60,24 @@ mcl_spawn.set_spawn_pos = function(player, pos, message) return spawn_changed end +local function get_far_node(pos) + local node = minetest.get_node(pos) + if node.name ~= "ignore" then + return node + end + minetest.get_voxel_manip():read_from_map(pos, pos) + return minetest.get_node(pos) +end + -- Respawn player at specified respawn position minetest.register_on_respawnplayer(function(player) local pos, custom_spawn = mcl_spawn.get_spawn_pos(player) if pos and custom_spawn then -- Check if bed is still there -- and the spawning position is free of solid or damaging blocks. - local node_bed = minetest.get_node(pos) - local node_up1 = minetest.get_node({x=pos.x,y=pos.y+1,z=pos.z}) - local node_up2 = minetest.get_node({x=pos.x,y=pos.y+2,z=pos.z}) + local node_bed = get_far_node(pos) + local node_up1 = get_far_node({x=pos.x,y=pos.y+1,z=pos.z}) + local node_up2 = get_far_node({x=pos.x,y=pos.y+2,z=pos.z}) local bgroup = minetest.get_item_group(node_bed.name, "bed") local def1 = minetest.registered_nodes[node_up1.name] local def2 = minetest.registered_nodes[node_up2.name] From 7851cee45e639294b5dc35beb731b28a0310d3a7 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 28 Feb 2019 16:43:52 +0100 Subject: [PATCH 037/798] Fix exhauston for attacking and taking dmg --- mods/ENTITIES/mcl_falling_nodes/init.lua | 4 ---- mods/ENTITIES/mcl_mobs/api.lua | 8 +++++++- mods/ENTITIES/mcl_mobs/depends.txt | 1 + mods/ITEMS/mcl_bows/arrow.lua | 4 ---- mods/ITEMS/mcl_bows/depends.txt | 1 - mods/ITEMS/mcl_tnt/depends.txt | 1 - mods/ITEMS/mcl_tnt/init.lua | 4 ---- mods/PLAYER/mcl_hunger/init.lua | 13 ++++++++++--- mods/PLAYER/mcl_playerplus/init.lua | 1 - 9 files changed, 18 insertions(+), 19 deletions(-) diff --git a/mods/ENTITIES/mcl_falling_nodes/init.lua b/mods/ENTITIES/mcl_falling_nodes/init.lua index 92cca703..5f54f58d 100644 --- a/mods/ENTITIES/mcl_falling_nodes/init.lua +++ b/mods/ENTITIES/mcl_falling_nodes/init.lua @@ -1,5 +1,4 @@ local dmes = minetest.get_modpath("mcl_death_messages") ~= nil -local hung = minetest.get_modpath("mcl_hunger") ~= nil local get_falling_depth = function(self) if not self._startpos then @@ -56,9 +55,6 @@ local deal_falling_damage = function(self, dtime) if dmes then mcl_death_messages.player_damage(v, string.format(msg, v:get_player_name())) end - if hung then - mcl_hunger.exhaust(v:get_player_name(), mcl_hunger.EXHAUST_DAMAGE) - end end v:set_hp(hp) end diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 30b8a582..337deeb0 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -96,6 +96,7 @@ mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dir local mod_weather = minetest.get_modpath("mcl_weather") ~= nil local mod_tnt = minetest.get_modpath("mcl_tnt") ~= nil local mod_mobspawners = minetest.get_modpath("mcl_mobspawners") ~= nil +local mod_hunger = minetest.get_modpath("mcl_hunger") ~= nil -- play sound local mob_sound = function(self, sound, is_opinion) @@ -2342,10 +2343,15 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) end - -- weapon wear + -- punch interval local weapon = hitter:get_wielded_item() local punch_interval = 1.4 + -- exhaust attacker + if mod_hunger and hitter:is_player() then + mcl_hunger.exhaust(hitter:get_player_name(), mcl_hunger.EXHAUST_ATTACK) + end + -- calculate mob damage local damage = 0 local armor = self.object:get_armor_groups() or {} diff --git a/mods/ENTITIES/mcl_mobs/depends.txt b/mods/ENTITIES/mcl_mobs/depends.txt index a1d9c9aa..eb3eb2aa 100644 --- a/mods/ENTITIES/mcl_mobs/depends.txt +++ b/mods/ENTITIES/mcl_mobs/depends.txt @@ -1,6 +1,7 @@ mcl_core mcl_weather? mcl_tnt? +mcl_hunger? invisibility? intllib? lucky_block? diff --git a/mods/ITEMS/mcl_bows/arrow.lua b/mods/ITEMS/mcl_bows/arrow.lua index 52a857a0..a3c8fb4a 100644 --- a/mods/ITEMS/mcl_bows/arrow.lua +++ b/mods/ITEMS/mcl_bows/arrow.lua @@ -7,7 +7,6 @@ local GRAVITY = 9.81 local YAW_OFFSET = -math.pi/2 -local mod_mcl_hunger = minetest.get_modpath("mcl_hunger") local mod_awards = minetest.get_modpath("awards") and minetest.get_modpath("mcl_achievements") local mod_button = minetest.get_modpath("mesecons_button") @@ -193,9 +192,6 @@ ARROW_ENTITY.on_step = function(self, dtime) -- “Ding” sound for hitting another player minetest.sound_play({name="mcl_bows_hit_player", gain=0.1}, {to_player=self._shooter}) end - if mod_mcl_hunger then - mcl_hunger.exhaust(obj:get_player_name(), mcl_hunger.EXHAUST_DAMAGE) - end end if lua then diff --git a/mods/ITEMS/mcl_bows/depends.txt b/mods/ITEMS/mcl_bows/depends.txt index 736cd117..08132ddb 100644 --- a/mods/ITEMS/mcl_bows/depends.txt +++ b/mods/ITEMS/mcl_bows/depends.txt @@ -1,7 +1,6 @@ controls awards? mcl_achievements? -mcl_hunger? mcl_core? mcl_mobitems? playerphysics? diff --git a/mods/ITEMS/mcl_tnt/depends.txt b/mods/ITEMS/mcl_tnt/depends.txt index 0f49dddd..f02d2b05 100644 --- a/mods/ITEMS/mcl_tnt/depends.txt +++ b/mods/ITEMS/mcl_tnt/depends.txt @@ -1,5 +1,4 @@ mcl_sounds? mcl_mobitems? -mcl_hunger? mcl_death_messages? doc_identifier? diff --git a/mods/ITEMS/mcl_tnt/init.lua b/mods/ITEMS/mcl_tnt/init.lua index dd6ebc66..fbe82c1c 100644 --- a/mods/ITEMS/mcl_tnt/init.lua +++ b/mods/ITEMS/mcl_tnt/init.lua @@ -1,5 +1,4 @@ local mod_death_messages = minetest.get_modpath("mcl_death_messages") -local mod_hunger = minetest.get_modpath("mcl_hunger") local function spawn_tnt(pos, entname) minetest.sound_play("tnt_ignite", {pos = pos,gain = 1.0,max_hear_distance = 15,}) @@ -33,9 +32,6 @@ local function do_tnt_physics(tnt_np,tntr) if mod_death_messages then mcl_death_messages.player_damage(obj, string.format("%s was caught in an explosion.", obj:get_player_name())) end - if mod_hunger then - mcl_hunger.exhaust(obj:get_player_name(), mcl_hunger.EXHAUST_DAMAGE) - end end obj:set_hp(obj:get_hp() - damage) end diff --git a/mods/PLAYER/mcl_hunger/init.lua b/mods/PLAYER/mcl_hunger/init.lua index f71dc121..5df787e7 100644 --- a/mods/PLAYER/mcl_hunger/init.lua +++ b/mods/PLAYER/mcl_hunger/init.lua @@ -29,7 +29,7 @@ mcl_hunger.EXHAUST_SPRINT_JUMP = 200 -- jump while sprinting mcl_hunger.EXHAUST_ATTACK = 100 -- hit an enemy mcl_hunger.EXHAUST_SWIM = 10 -- player movement in water mcl_hunger.EXHAUST_SPRINT = 100 -- sprint (per node) -mcl_hunger.EXHAUST_DAMAGE = 100 -- TODO (mostly done): taking damage (protected by armor) +mcl_hunger.EXHAUST_DAMAGE = 100 -- taking damage (protected by armor) mcl_hunger.EXHAUST_REGEN = 6000 -- Regenerate 1 HP mcl_hunger.EXHAUST_LVL = 4000 -- at what exhaustion player saturation gets lowered @@ -134,12 +134,19 @@ end) -- PvP combat exhaustion minetest.register_on_punchplayer(function(victim, puncher, time_from_last_punch, tool_capabilities, dir, damage) - if victim:is_player() and puncher:is_player() then - mcl_hunger.exhaust(victim:get_player_name(), mcl_hunger.EXHAUST_DAMAGE) + if puncher:is_player() then mcl_hunger.exhaust(puncher:get_player_name(), mcl_hunger.EXHAUST_ATTACK) end end) +-- Exhaust on taking damage +minetest.register_on_player_hpchange(function(player, hp_change) + if hp_change < 0 then + local name = player:get_player_name() + mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_DAMAGE) + end +end) + local main_timer = 0 local timer = 0 -- Half second timer local timerMult = 1 -- Cycles from 0 to 7, each time when timer hits half a second diff --git a/mods/PLAYER/mcl_playerplus/init.lua b/mods/PLAYER/mcl_playerplus/init.lua index f9022a3b..17074ea9 100644 --- a/mods/PLAYER/mcl_playerplus/init.lua +++ b/mods/PLAYER/mcl_playerplus/init.lua @@ -135,7 +135,6 @@ minetest.register_globalstep(function(dtime) if dist < 1.1 or dist_feet < 1.1 then if player:get_hp() > 0 then mcl_death_messages.player_damage(player, string.format("%s was prickled by a cactus.", name)) - mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_DAMAGE) player:set_hp(player:get_hp() - 1) end end From 1daf9b7a590c905f932eef020924152178278015 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 28 Feb 2019 18:00:17 +0100 Subject: [PATCH 038/798] Put treasure loot into random inventory slots --- mods/CORE/mcl_loot/init.lua | 54 ++++++++++++++++++++++++++ mods/MAPGEN/mcl_dungeons/init.lua | 4 +- mods/MAPGEN/mcl_structures/init.lua | 8 +--- mods/MAPGEN/tsm_railcorridors/init.lua | 4 +- 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/mods/CORE/mcl_loot/init.lua b/mods/CORE/mcl_loot/init.lua index f7eff3f6..3b52e365 100644 --- a/mods/CORE/mcl_loot/init.lua +++ b/mods/CORE/mcl_loot/init.lua @@ -98,3 +98,57 @@ function mcl_loot.get_multi_loot(multi_loot_definitions, pr) end return items end + +--[[ +Returns a table of length `max_slot` and all natural numbers between 1 and `max_slot` +in a random order. +]] +local function get_random_slots(max_slot) + local slots = {} + for s=1, max_slot do + slots[s] = s + end + local slots_out = {} + while #slots > 0 do + local r = math.random(1, #slots) + table.insert(slots_out, slots[r]) + table.remove(slots, r) + end + for s=1, #slots_out do + print(slots_out[s]) + end + return slots_out +end + +--[[ +Puts items in an inventory list into random slots. +* inv: InvRef +* listname: Inventory list name +* items: table of items to add + +Items will be added from start of the table to end. +If the inventory already has occupied slots, or is +too small, placement of some items might fail. +]] +function mcl_loot.fill_inventory(inv, listname, items) + local size = inv:get_size(listname) + local slots = get_random_slots(size) + local leftovers = {} + -- 1st pass: Add items into random slots + for i=1, math.min(#items, size) do + local item = items[i] + local slot = slots[i] + local old_item = inv:get_stack(listname, slot) + local leftover = old_item:add_item(item) + inv:set_stack(listname, slot, old_item) + if not leftover:is_empty() then + table.insert(leftovers, item) + end + end + -- 2nd pass: If some items couldn't be added in first pass, + -- try again in a non-random fashion + for l=1, math.min(#leftovers, size) do + inv:add_item(listname, leftovers[l]) + end + -- If there are still items left, tough luck! +end diff --git a/mods/MAPGEN/mcl_dungeons/init.lua b/mods/MAPGEN/mcl_dungeons/init.lua index f23c33bf..dc19a6e1 100644 --- a/mods/MAPGEN/mcl_dungeons/init.lua +++ b/mods/MAPGEN/mcl_dungeons/init.lua @@ -366,9 +366,7 @@ minetest.register_on_generated(function(minp, maxp) local meta = minetest.get_meta(cpos) local inv = meta:get_inventory() local items = get_loot() - for i=1, math.min(#items, inv:get_size("main")) do - inv:set_stack("main", i, ItemStack(items[i])) - end + mcl_loot.fill_inventory(inv, "main", items) end -- Mob spawners are placed seperately, too diff --git a/mods/MAPGEN/mcl_structures/init.lua b/mods/MAPGEN/mcl_structures/init.lua index 552691bf..e98dedda 100644 --- a/mods/MAPGEN/mcl_structures/init.lua +++ b/mods/MAPGEN/mcl_structures/init.lua @@ -188,9 +188,7 @@ mcl_structures.generate_igloo_basement = function(pos, orientation) local meta = minetest.get_meta(chest_pos) local inv = meta:get_inventory() inv:set_size("main", 9*3) - for i=1, #lootitems do - inv:add_item("main", lootitems[i]) - end + mcl_loot.fill_inventory(inv, "main", lootitems) end return success end @@ -401,9 +399,7 @@ mcl_structures.generate_desert_temple = function(pos) local meta = minetest.get_meta(chests[c]) local inv = meta:get_inventory() inv:set_size("main", 9*3) - for i=1, #lootitems do - inv:add_item("main", lootitems[i]) - end + mcl_loot.fill_inventory(inv, "main", lootitems) end -- Initialize pressure plates and randomly remove up to 5 plates diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index d6de8198..6812125e 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -377,9 +377,7 @@ local function PlaceChest(pos, param2) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() local items = tsm_railcorridors.get_treasures(pr) - for i=1, math.min(#items, inv:get_size("main")) do - inv:set_stack("main", i, ItemStack(items[i])) - end + mcl_loot.fill_inventory(inv, "main", items) end end From 19b1cf5986916d4e2c75a9acd88f1a2ba3cfd322 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 28 Feb 2019 18:19:57 +0100 Subject: [PATCH 039/798] More robust initialization of chests of structs --- mods/MAPGEN/mcl_structures/init.lua | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/mods/MAPGEN/mcl_structures/init.lua b/mods/MAPGEN/mcl_structures/init.lua index e98dedda..fa233682 100644 --- a/mods/MAPGEN/mcl_structures/init.lua +++ b/mods/MAPGEN/mcl_structures/init.lua @@ -15,6 +15,18 @@ mcl_structures.get_struct = function(file) return allnode end +-- Call on_construct on pos. +-- Useful to init chests from formspec. +local init_node_construct = function(pos) + local node = minetest.get_node(pos) + local def = minetest.registered_nodes[node.name] + if def and def.on_construct then + def.on_construct(pos) + return true + end + return false +end + -- The call of Struct mcl_structures.call_struct = function(pos, struct_style, rotation) if not rotation then @@ -185,9 +197,9 @@ mcl_structures.generate_igloo_basement = function(pos, orientation) }}, pr) local chest_pos = vector.add(pos, chest_offset) + init_node_construct(chest_pos) local meta = minetest.get_meta(chest_pos) local inv = meta:get_inventory() - inv:set_size("main", 9*3) mcl_loot.fill_inventory(inv, "main", lootitems) end return success @@ -396,9 +408,10 @@ mcl_structures.generate_desert_temple = function(pos) } }}, pr) + local meta = minetest.get_meta(chests[c]) + init_node_construct(chests[c]) local meta = minetest.get_meta(chests[c]) local inv = meta:get_inventory() - inv:set_size("main", 9*3) mcl_loot.fill_inventory(inv, "main", lootitems) end From a711c7bdb7b8ac006d5d71b5a64f753a132745fb Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 1 Mar 2019 17:30:21 +0100 Subject: [PATCH 040/798] handle_node_drops no longer destroys metadata --- mods/ENTITIES/mcl_item_entity/init.lua | 10 +++++----- mods/HUD/mcl_inventory/creative.lua | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/mods/ENTITIES/mcl_item_entity/init.lua b/mods/ENTITIES/mcl_item_entity/init.lua index d1c502fc..4b10cdac 100644 --- a/mods/ENTITIES/mcl_item_entity/init.lua +++ b/mods/ENTITIES/mcl_item_entity/init.lua @@ -245,16 +245,16 @@ function minetest.handle_node_drops(pos, drops, digger) end for _,item in ipairs(drops) do - local count, name + local count if type(item) == "string" then - count = 1 - name = item + count = ItemStack(item):get_count() else count = item:get_count() - name = item:get_name() end + local drop_item = ItemStack(item) + drop_item:set_count(1) for i=1,count do - local obj = core.add_item(pos, name) + local obj = core.add_item(pos, drop_item) if obj ~= nil then local x = math.random(1, 5) if math.random(1,2) == 1 then diff --git a/mods/HUD/mcl_inventory/creative.lua b/mods/HUD/mcl_inventory/creative.lua index 7125dcb6..dd5d0bd8 100644 --- a/mods/HUD/mcl_inventory/creative.lua +++ b/mods/HUD/mcl_inventory/creative.lua @@ -555,8 +555,7 @@ if minetest.settings:get_bool("creative_mode") then local inv = digger:get_inventory() if inv then for _,item in ipairs(drops) do - item = ItemStack(item):get_name() - if not inv:contains_item("main", item) then + if not inv:contains_item("main", item, true) then inv:add_item("main", item) end end From c6111039ab18f17537d037594fd1f5f62860a491 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 1 Mar 2019 17:31:31 +0100 Subject: [PATCH 041/798] Fix annoying drops when digging banner in creative --- mods/ITEMS/mcl_banners/init.lua | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/mods/ITEMS/mcl_banners/init.lua b/mods/ITEMS/mcl_banners/init.lua index 5bab2ed7..541a69c9 100644 --- a/mods/ITEMS/mcl_banners/init.lua +++ b/mods/ITEMS/mcl_banners/init.lua @@ -47,6 +47,25 @@ local layer_ratio = 255 local standing_banner_entity_offset = { x=0, y=-0.499, z=0 } local hanging_banner_entity_offset = { x=0, y=-1.7, z=0 } +local on_dig_banner = function(pos, node, digger) + -- Check protection + local name = digger:get_player_name() + if minetest.is_protected(pos, name) then + minetest.register_protection_violation(pos, name) + return + end + -- Drop item + local meta = minetest.get_meta(pos) + local item = meta:get_inventory():get_stack("banner", 1) + if not item:is_empty() then + minetest.handle_node_drops(pos, {item:to_string()}, digger) + else + minetest.handle_node_drops(pos, {"mcl_bannes:banner_item_white"}, digger) + end + -- Remove node + minetest.remove_node(pos) +end + local on_destruct_banner = function(pos, hanging) local offset, nodename if hanging then @@ -56,7 +75,7 @@ local on_destruct_banner = function(pos, hanging) offset = standing_banner_entity_offset nodename = "mcl_banners:standing_banner" end - -- Find this node's banner entity and make it drop as an item + -- Find this node's banner entity and remove it local checkpos = vector.add(pos, offset) local objects = minetest.get_objects_inside_radius(checkpos, 0.5) for _, v in ipairs(objects) do @@ -65,14 +84,6 @@ local on_destruct_banner = function(pos, hanging) v:remove() end end - -- Drop item - local meta = minetest.get_meta(pos) - local item = meta:get_inventory():get_stack("banner", 1) - if not item:is_empty() then - minetest.add_item(pos, item) - else - minetest.add_item(pos, "mcl_banners:banner_item_white") - end end local on_destruct_standing_banner = function(pos) @@ -207,6 +218,7 @@ minetest.register_node("mcl_banners:standing_banner", { sounds = node_sounds, drop = "", -- Item drops are handled in entity code + on_dig = on_dig_banner, on_destruct = on_destruct_standing_banner, on_punch = function(pos, node) respawn_banner_entity(pos, node) @@ -238,6 +250,7 @@ minetest.register_node("mcl_banners:hanging_banner", { sounds = node_sounds, drop = "", -- Item drops are handled in entity code + on_dig = on_dig_banner, on_destruct = on_destruct_hanging_banner, on_punch = function(pos, node) respawn_banner_entity(pos, node) From dca095171c573bd8b4ecae91b09fd32f3310a610 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 1 Mar 2019 17:48:00 +0100 Subject: [PATCH 042/798] Restrict banner layers to 3 if has a gradient --- mods/ITEMS/mcl_banners/init.lua | 3 ++- mods/ITEMS/mcl_banners/patterncraft.lua | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_banners/init.lua b/mods/ITEMS/mcl_banners/init.lua index 541a69c9..49af2de6 100644 --- a/mods/ITEMS/mcl_banners/init.lua +++ b/mods/ITEMS/mcl_banners/init.lua @@ -193,7 +193,8 @@ minetest.register_node("mcl_banners:standing_banner", { _doc_items_entry_name = "Banner", _doc_items_image = "mcl_banners_item_base.png^mcl_banners_item_overlay.png", _doc_items_longdesc = "Banners are tall colorful decorative blocks. They can be placed on the floor and at walls. Banners can be emblazoned with a variety of patterns using a lot of dye in crafting.", - _doc_items_usagehelp = "Use crafting to draw a pattern on top of the banner. Emblazoned banners can be emblazoned again to combine various patterns. You can draw up to 6 layers on a banner that way. You can copy the pattern of a banner by placing two banners of the same color in the crafting grid—one needs to be emblazoned, the other one must be clean. Finally, you can use a banner on a cauldron with water to wash off its top-most layer.", + _doc_items_usagehelp = [[Use crafting to draw a pattern on top of the banner. Emblazoned banners can be emblazoned again to combine various patterns. You can draw up to 6 layers on a banner that way. If the banner includes a gradient, only 3 layers are possible. +You can copy the pattern of a banner by placing two banners of the same color in the crafting grid—one needs to be emblazoned, the other one must be clean. Finally, you can use a banner on a cauldron with water to wash off its top-most layer.]], walkable = false, is_ground_content = false, paramtype = "light", diff --git a/mods/ITEMS/mcl_banners/patterncraft.lua b/mods/ITEMS/mcl_banners/patterncraft.lua index 8d202c78..2858b3d2 100644 --- a/mods/ITEMS/mcl_banners/patterncraft.lua +++ b/mods/ITEMS/mcl_banners/patterncraft.lua @@ -5,6 +5,9 @@ -- Maximum number of layers which can be put on a banner by crafting. local max_layers_crafting = 6 +-- Maximum number of layers when banner includes a gradient (workaround, see below). +local max_layers_gradient = 3 + -- Max. number lines in the descriptions for the banner layers. -- This is done to avoid huge tooltips. local max_layer_lines = 6 @@ -386,6 +389,16 @@ local banner_pattern_craft = function(itemstack, player, old_craft_grid, craft_i if #layers >= max_layers_crafting then return ItemStack("") end + -- Lower layer limit when banner includes any gradient. + -- Workaround to circumvent bug #340 (gradients are likely to cause transparent pixels). + -- FIXME: Remove this restriction when bug #340 is fixed. + if #layers >= max_layers_gradient then + for l=1, #layers do + if layers[l].pattern == "gradient" or layers[l].pattern == "gradient_up" then + return ItemStack("") + end + end + end local matching_pattern local max_i = player:get_inventory():get_size("craft") From ab919713985fddf90e502bdec4f958cdb3606417 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 1 Mar 2019 17:53:21 +0100 Subject: [PATCH 043/798] Increase pattern layer limit to 12 --- mods/ITEMS/mcl_banners/init.lua | 2 +- mods/ITEMS/mcl_banners/patterncraft.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mods/ITEMS/mcl_banners/init.lua b/mods/ITEMS/mcl_banners/init.lua index 49af2de6..bfe7832a 100644 --- a/mods/ITEMS/mcl_banners/init.lua +++ b/mods/ITEMS/mcl_banners/init.lua @@ -193,7 +193,7 @@ minetest.register_node("mcl_banners:standing_banner", { _doc_items_entry_name = "Banner", _doc_items_image = "mcl_banners_item_base.png^mcl_banners_item_overlay.png", _doc_items_longdesc = "Banners are tall colorful decorative blocks. They can be placed on the floor and at walls. Banners can be emblazoned with a variety of patterns using a lot of dye in crafting.", - _doc_items_usagehelp = [[Use crafting to draw a pattern on top of the banner. Emblazoned banners can be emblazoned again to combine various patterns. You can draw up to 6 layers on a banner that way. If the banner includes a gradient, only 3 layers are possible. + _doc_items_usagehelp = [[Use crafting to draw a pattern on top of the banner. Emblazoned banners can be emblazoned again to combine various patterns. You can draw up to 12 layers on a banner that way. If the banner includes a gradient, only 3 layers are possible. You can copy the pattern of a banner by placing two banners of the same color in the crafting grid—one needs to be emblazoned, the other one must be clean. Finally, you can use a banner on a cauldron with water to wash off its top-most layer.]], walkable = false, is_ground_content = false, diff --git a/mods/ITEMS/mcl_banners/patterncraft.lua b/mods/ITEMS/mcl_banners/patterncraft.lua index 2858b3d2..4639b7a9 100644 --- a/mods/ITEMS/mcl_banners/patterncraft.lua +++ b/mods/ITEMS/mcl_banners/patterncraft.lua @@ -1,9 +1,9 @@ -- Pattern crafting. This file contains the code for crafting all the -- emblazonings you can put on the banners. It's quite complicated; --- normal 08/15 crafting won't work here. +-- run-of-the-mill crafting won't work here. -- Maximum number of layers which can be put on a banner by crafting. -local max_layers_crafting = 6 +local max_layers_crafting = 12 -- Maximum number of layers when banner includes a gradient (workaround, see below). local max_layers_gradient = 3 From 03c6beb9718bceb7be1cebb58293c251f72faaf2 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 1 Mar 2019 18:01:29 +0100 Subject: [PATCH 044/798] Remove mcl_imitation_mode --- .../mcl_core_jungle_bush_jungle_leaves.mts | Bin 129 -> 0 bytes mods/MAPGEN/mcl_biomes/init.lua | 8 +------- settingtypes.txt | 14 -------------- 3 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 mods/ITEMS/mcl_core/schematics/mcl_core_jungle_bush_jungle_leaves.mts diff --git a/mods/ITEMS/mcl_core/schematics/mcl_core_jungle_bush_jungle_leaves.mts b/mods/ITEMS/mcl_core/schematics/mcl_core_jungle_bush_jungle_leaves.mts deleted file mode 100644 index d2df30e888e54a2658c9c97456acb35442e8eaa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129 zcmeYb3HD`RVPFN}dgl6i24)84#LOZF(cI*m_~iVeRI9Agy!4#ZoYcg!)M5r z)YOVO$q5IzBo0mxNN929;Afk0AwogaC!q9)&#_dw%y+BTuRpHDBE-_&-1%Q&<9`Rk TkfSGFeVFNP&A_l Date: Fri, 1 Mar 2019 18:08:28 +0100 Subject: [PATCH 045/798] Add experimental setting: fallen logs --- mods/MAPGEN/mcl_biomes/init.lua | 3 +-- settingtypes.txt | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mods/MAPGEN/mcl_biomes/init.lua b/mods/MAPGEN/mcl_biomes/init.lua index a046c174..26efcee6 100644 --- a/mods/MAPGEN/mcl_biomes/init.lua +++ b/mods/MAPGEN/mcl_biomes/init.lua @@ -1,10 +1,9 @@ local mg_name = minetest.get_mapgen_setting("mg_name") -- Some mapgen settings -local imitate = minetest.settings:get("mcl_imitation_mode") local superflat = mg_name == "flat" and minetest.get_mapgen_setting("mcl_superflat_classic") == "true" -local generate_fallen_logs = false +local generate_fallen_logs = minetest.settings:get_bool("mcl_generate_fallen_logs") or false -- Jungle bush schematic. In PC/Java Edition it's Jungle Wood + Oak Leaves local jungle_bush_schematic = minetest.get_modpath("mcl_core").."/schematics/mcl_core_jungle_bush_oak_leaves.mts" diff --git a/settingtypes.txt b/settingtypes.txt index 3d65971e..ba0db568 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -87,6 +87,10 @@ craftguide_progressive_mode (Enable recipe book progressive mode) bool false # This feature is not finished yet! mob_difficulty (Mob difficulty factor) float 1.0 0.0 +# Whether to generate fallen logs in some biomes. +# They might not always look pretty and have strange overhangs. +mcl_generate_fallen_logs (Generate fallen logs) bool false + # If enabled, the “flat” map generator generates a Classic Superflat world: # Completely flat, 1 layer of grass blocks on top of 2 layers of dirt on # top of a final layer of bedrock. No caves, trees or plants. From d367a8dbad79befe9e0e3d5d01ee5b14a839a40a Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 1 Mar 2019 18:32:10 +0100 Subject: [PATCH 046/798] Update banner comment --- mods/ITEMS/mcl_banners/patterncraft.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_banners/patterncraft.lua b/mods/ITEMS/mcl_banners/patterncraft.lua index 4639b7a9..f60d5678 100644 --- a/mods/ITEMS/mcl_banners/patterncraft.lua +++ b/mods/ITEMS/mcl_banners/patterncraft.lua @@ -390,8 +390,8 @@ local banner_pattern_craft = function(itemstack, player, old_craft_grid, craft_i return ItemStack("") end -- Lower layer limit when banner includes any gradient. - -- Workaround to circumvent bug #340 (gradients are likely to cause transparent pixels). - -- FIXME: Remove this restriction when bug #340 is fixed. + -- Workaround to circumvent Minetest bug (https://github.com/minetest/minetest/issues/6210) + -- TODO: Remove this restriction when bug #6210 is fixed. if #layers >= max_layers_gradient then for l=1, #layers do if layers[l].pattern == "gradient" or layers[l].pattern == "gradient_up" then From 47389902bc76cd798a9d29b10ac191bdff2dbb88 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 1 Mar 2019 18:33:56 +0100 Subject: [PATCH 047/798] Version 0.47.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9654821f..ed7be27b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An unofficial Minecraft-like game for Minetest. Forked from MineClone by daredevils. Developed by Wuzzy and contributors. Not developed or endorsed by Mojang AB. -Version: 0.46.0 +Version: 0.47.0 ### Gameplay You start in a randomly-generated world made entirely of cubes. You can explore From 94591c8b527eb60b46ae13c9c891f34da9eac96b Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 4 Mar 2019 06:07:44 +0100 Subject: [PATCH 048/798] Fix crash when dig banner in protected area --- mods/ITEMS/mcl_banners/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_banners/init.lua b/mods/ITEMS/mcl_banners/init.lua index bfe7832a..a04d88ed 100644 --- a/mods/ITEMS/mcl_banners/init.lua +++ b/mods/ITEMS/mcl_banners/init.lua @@ -51,7 +51,7 @@ local on_dig_banner = function(pos, node, digger) -- Check protection local name = digger:get_player_name() if minetest.is_protected(pos, name) then - minetest.register_protection_violation(pos, name) + minetest.record_protection_violation(pos, name) return end -- Drop item From c19e3f455cd4c1e29e37d3e4d031953e8f107baa Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 4 Mar 2019 06:08:44 +0100 Subject: [PATCH 049/798] Version 0.47.1 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed7be27b..9a35de3e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An unofficial Minecraft-like game for Minetest. Forked from MineClone by daredevils. Developed by Wuzzy and contributors. Not developed or endorsed by Mojang AB. -Version: 0.47.0 +Version: 0.47.1 ### Gameplay You start in a randomly-generated world made entirely of cubes. You can explore From 62e3a8b9ff665a46ec2160d9b0cf72f7d4f2fdfa Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 00:11:43 +0100 Subject: [PATCH 050/798] Add player skin support, add female skin --- mods/PLAYER/simple_skins/depends.txt | 3 + mods/PLAYER/simple_skins/description.txt | 1 + mods/PLAYER/simple_skins/init.lua | 148 ++++++++++++++++++ mods/PLAYER/simple_skins/intllib.lua | 45 ++++++ mods/PLAYER/simple_skins/license.txt | 21 +++ mods/PLAYER/simple_skins/locale/fr.po | 51 ++++++ mods/PLAYER/simple_skins/locale/it.po | 52 ++++++ mods/PLAYER/simple_skins/locale/ms.po | 51 ++++++ mods/PLAYER/simple_skins/locale/template.pot | 50 ++++++ mods/PLAYER/simple_skins/meta/character.txt | 3 + mods/PLAYER/simple_skins/meta/character_1.txt | 3 + mods/PLAYER/simple_skins/mod.conf | 1 + mods/PLAYER/simple_skins/readme.md | 7 + .../simple_skins/textures/character_1.png | Bin 0 -> 5505 bytes .../textures/inventory_plus_skins.png | Bin 0 -> 2182 bytes 15 files changed, 436 insertions(+) create mode 100644 mods/PLAYER/simple_skins/depends.txt create mode 100644 mods/PLAYER/simple_skins/description.txt create mode 100644 mods/PLAYER/simple_skins/init.lua create mode 100644 mods/PLAYER/simple_skins/intllib.lua create mode 100644 mods/PLAYER/simple_skins/license.txt create mode 100644 mods/PLAYER/simple_skins/locale/fr.po create mode 100644 mods/PLAYER/simple_skins/locale/it.po create mode 100644 mods/PLAYER/simple_skins/locale/ms.po create mode 100644 mods/PLAYER/simple_skins/locale/template.pot create mode 100644 mods/PLAYER/simple_skins/meta/character.txt create mode 100644 mods/PLAYER/simple_skins/meta/character_1.txt create mode 100644 mods/PLAYER/simple_skins/mod.conf create mode 100644 mods/PLAYER/simple_skins/readme.md create mode 100644 mods/PLAYER/simple_skins/textures/character_1.png create mode 100644 mods/PLAYER/simple_skins/textures/inventory_plus_skins.png diff --git a/mods/PLAYER/simple_skins/depends.txt b/mods/PLAYER/simple_skins/depends.txt new file mode 100644 index 00000000..1927ce89 --- /dev/null +++ b/mods/PLAYER/simple_skins/depends.txt @@ -0,0 +1,3 @@ +mcl_player +intllib? +3d_armor? diff --git a/mods/PLAYER/simple_skins/description.txt b/mods/PLAYER/simple_skins/description.txt new file mode 100644 index 00000000..61c7bff6 --- /dev/null +++ b/mods/PLAYER/simple_skins/description.txt @@ -0,0 +1 @@ +Mod that allows players to set their individual skins. \ No newline at end of file diff --git a/mods/PLAYER/simple_skins/init.lua b/mods/PLAYER/simple_skins/init.lua new file mode 100644 index 00000000..3a41490f --- /dev/null +++ b/mods/PLAYER/simple_skins/init.lua @@ -0,0 +1,148 @@ +-- Simple Skins mod for Minetest (MineClone 2 Edition) + +-- Released by TenPlus1 and based on Zeg9's code under MIT license + +skins = { + skins = {}, meta = {}, + modpath = minetest.get_modpath("simple_skins"), + skin_count = 0, -- counter of _custom_ skins (all skins except character.png) +} + + +-- Load support for intllib. +local S, NS = dofile(skins.modpath .. "/intllib.lua") + + +-- load skin list and metadata +local id, f, data, skin = 1 + +while true do + + skin = "character_" .. id + + -- does skin file exist ? + f = io.open(skins.modpath .. "/textures/" .. skin .. ".png") + + -- escape loop if not found and remove last entry + if not f then + id = id - 1 + break + end + + f:close() + + -- does metadata exist for that skin file ? + f = io.open(skins.modpath .. "/meta/" .. skin .. ".txt") + + if f then + data = minetest.deserialize("return {" .. f:read('*all') .. "}") + f:close() + end + + -- add metadata to list + skins.meta[skin] = { + name = data and data.name or "", + author = data and data.author or "", + } + + id = id + 1 + skins.skin_count = skins.skin_count + 1 +end + +skins.set_player_skin = function(player, skin) + if not player then + return + end + local playername = player:get_player_name() + skins.skins[playername] = skin + player:set_attribute("simple_skins:skin", skins.skins[playername]) + skins.update_player_skin(player) + if minetest.get_modpath("3d_armor") then + armor.textures[playername].skin = skin .. ".png" + armor:update_player_visuals(player) + end +end + +skins.update_player_skin = function(player) + if not player then + return + end + local playername = player:get_player_name() + mcl_player.player_set_textures(player, { skins.skins[playername] .. ".png" }) +end + +-- load player skin on join +minetest.register_on_joinplayer(function(player) + + local name = player:get_player_name() + local skin = player:get_attribute("simple_skins:skin") + local set_skin + -- do we already have a skin in player attributes? + if skin then + set_skin = skin + + -- otherwise use random skin if not set + else + local r = math.random(0, skins.skin_count) + if r == 0 then + set_skin = "character" + else + set_skin = "character_" .. r + end + end + if set_skin then + skins.set_player_skin(player, set_skin) + end +end) + +-- command to set player skin (usually for custom skins) +minetest.register_chatcommand("setskin", { + params = "[] ", + description = S("Select player skin of yourself or another player"), + privs = {}, + func = function(name, param) + + local playername, skin_id = string.match(param, "([^ ]+) (%d+)") + if not playername or not skin_id then + skin_id = string.match(param, "(%d+)") + if not skin_id then + return false, S("Insufficient or wrong parameters") + end + playername = name + end + skin_id = tonumber(skin_id) + + local player = minetest.get_player_by_name(playername) + + if not player then + return false, S("Player @1 not online!", playername) + end + if name ~= playername then + local privs = minetest.get_player_privs(name) + if not privs.server then + return false, S("You need the “server” privilege to change the skin of other players!") + end + end + + local skin + if skin_id == nil or skin_id > skins.skin_count or skin_id < 0 then + return false, S("Invalid skin number! Valid numbers: 0 to @1", skins.skin_count) + elseif skin_id == 0 then + skin = "character" + else + skin = "character_" .. tostring(skin_id) + end + + skins.set_player_skin(player, skin) + local skinfile = skin..".png" + + local your_msg = S("Your skin has been set to: @1", skinfile) + if name == playername then + return true, your_msg + else + minetest.chat_send_player(playername, your_msg) + return true, S("Skin of @1 set to: @2", playername, skinfile) + end + + end, +}) diff --git a/mods/PLAYER/simple_skins/intllib.lua b/mods/PLAYER/simple_skins/intllib.lua new file mode 100644 index 00000000..6669d720 --- /dev/null +++ b/mods/PLAYER/simple_skins/intllib.lua @@ -0,0 +1,45 @@ + +-- Fallback functions for when `intllib` is not installed. +-- Code released under Unlicense . + +-- Get the latest version of this file at: +-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua + +local function format(str, ...) + local args = { ... } + local function repl(escape, open, num, close) + if escape == "" then + local replacement = tostring(args[tonumber(num)]) + if open == "" then + replacement = replacement..close + end + return replacement + else + return "@"..open..num..close + end + end + return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl)) +end + +local gettext, ngettext +if minetest.get_modpath("intllib") then + if intllib.make_gettext_pair then + -- New method using gettext. + gettext, ngettext = intllib.make_gettext_pair() + else + -- Old method using text files. + gettext = intllib.Getter() + end +end + +-- Fill in missing functions. + +gettext = gettext or function(msgid, ...) + return format(msgid, ...) +end + +ngettext = ngettext or function(msgid, msgid_plural, n, ...) + return format(n==1 and msgid or msgid_plural, ...) +end + +return gettext, ngettext diff --git a/mods/PLAYER/simple_skins/license.txt b/mods/PLAYER/simple_skins/license.txt new file mode 100644 index 00000000..fec6f6aa --- /dev/null +++ b/mods/PLAYER/simple_skins/license.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 TenPlus1 + +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/mods/PLAYER/simple_skins/locale/fr.po b/mods/PLAYER/simple_skins/locale/fr.po new file mode 100644 index 00000000..30d8e36e --- /dev/null +++ b/mods/PLAYER/simple_skins/locale/fr.po @@ -0,0 +1,51 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-29 07:11+0200\n" +"PO-Revision-Date: 2017-07-29 07:17+0200\n" +"Last-Translator: fat115 \n" +"Language-Team: \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.12\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: init.lua +msgid "Select Player Skin:" +msgstr "Sélectionner l'apparence du joueur :" + +#: init.lua +msgid "Name: " +msgstr "Nom : " + +#: init.lua +msgid "Author: " +msgstr "Auteur : " + +#: init.lua +msgid "Admin command to set player skin" +msgstr "Commande admin pour définir l'apparence du joueur" + +#: init.lua +msgid "'s skin set to" +msgstr ", apparence définie pour" + +#: init.lua +msgid "Set player skin" +msgstr "Définir l'apparence du joueur" + +#: init.lua +msgid "Close" +msgstr "Fermer" + +#: init.lua +msgid "[MOD] Simple Skins loaded" +msgstr "[MOD] Simple Skins chargé" diff --git a/mods/PLAYER/simple_skins/locale/it.po b/mods/PLAYER/simple_skins/locale/it.po new file mode 100644 index 00000000..d4701316 --- /dev/null +++ b/mods/PLAYER/simple_skins/locale/it.po @@ -0,0 +1,52 @@ +# simple_skin . +# Copyright (C) 2018 +# This file is distributed under the same license as the PACKAGE package. +# Stefano Peris , 2018. +# Github: +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-21 07:29+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Stefano Peris \n" +"Language-Team: \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.12\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: init.lua +msgid "Select Player Skin:" +msgstr "Seleziona la skin del giocatore" + +#: init.lua +msgid "Name: " +msgstr "Nome" + +#: init.lua +msgid "Author: " +msgstr "Autore" + +#: init.lua +msgid "Admin command to set player skin" +msgstr "Comando di admin per impostare la skin del giocatore" + +#: init.lua +msgid "'s skin set to" +msgstr ", la skin è impostata su" + +#: init.lua +msgid "Set player skin" +msgstr "Imposta la skin del giocatore" + +#: init.lua +msgid "Close" +msgstr "Chiudi" + +#: init.lua +msgid "[MOD] Simple Skins loaded" +msgstr "[MOD] Skins semplici caricate" diff --git a/mods/PLAYER/simple_skins/locale/ms.po b/mods/PLAYER/simple_skins/locale/ms.po new file mode 100644 index 00000000..bba5982d --- /dev/null +++ b/mods/PLAYER/simple_skins/locale/ms.po @@ -0,0 +1,51 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-29 07:11+0200\n" +"PO-Revision-Date: 2018-02-14 01:23+0800\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.0.6\n" +"Last-Translator: MuhdNurHidayat (MNH48) \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"Language: ms\n" + +#: init.lua +msgid "Select Player Skin:" +msgstr "Pilih Kulit Pemain:" + +#: init.lua +msgid "Name: " +msgstr "Nama: " + +#: init.lua +msgid "Author: " +msgstr "Pencipta: " + +#: init.lua +msgid "Admin command to set player skin" +msgstr "Perintah pentadbir untuk menetapkan kulit pemain" + +#: init.lua +msgid "'s skin set to" +msgstr " telah ditukarkan kulitnya kepada" + +#: init.lua +msgid "Set player skin" +msgstr "Tetapkan kulit pemain" + +#: init.lua +msgid "Close" +msgstr "Tutup" + +#: init.lua +msgid "[MOD] Simple Skins loaded" +msgstr "[MODS] Simple Skins telah dimuatkan" diff --git a/mods/PLAYER/simple_skins/locale/template.pot b/mods/PLAYER/simple_skins/locale/template.pot new file mode 100644 index 00000000..36282e43 --- /dev/null +++ b/mods/PLAYER/simple_skins/locale/template.pot @@ -0,0 +1,50 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-29 07:11+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: init.lua +msgid "Select Player Skin:" +msgstr "" + +#: init.lua +msgid "Name: " +msgstr "" + +#: init.lua +msgid "Author: " +msgstr "" + +#: init.lua +msgid "Admin command to set player skin" +msgstr "" + +#: init.lua +msgid "'s skin set to" +msgstr "" + +#: init.lua +msgid "Set player skin" +msgstr "" + +#: init.lua +msgid "Close" +msgstr "" + +#: init.lua +msgid "[MOD] Simple Skins loaded" +msgstr "" diff --git a/mods/PLAYER/simple_skins/meta/character.txt b/mods/PLAYER/simple_skins/meta/character.txt new file mode 100644 index 00000000..5a07db19 --- /dev/null +++ b/mods/PLAYER/simple_skins/meta/character.txt @@ -0,0 +1,3 @@ +name = "Steve", +author = "(Texture pack author)", +description = "The default male skin.", diff --git a/mods/PLAYER/simple_skins/meta/character_1.txt b/mods/PLAYER/simple_skins/meta/character_1.txt new file mode 100644 index 00000000..ec438955 --- /dev/null +++ b/mods/PLAYER/simple_skins/meta/character_1.txt @@ -0,0 +1,3 @@ +name = "Alex", +author = "(Texture pack author)", +description = "The default female skin.", diff --git a/mods/PLAYER/simple_skins/mod.conf b/mods/PLAYER/simple_skins/mod.conf new file mode 100644 index 00000000..aff90aab --- /dev/null +++ b/mods/PLAYER/simple_skins/mod.conf @@ -0,0 +1 @@ +name = simple_skins diff --git a/mods/PLAYER/simple_skins/readme.md b/mods/PLAYER/simple_skins/readme.md new file mode 100644 index 00000000..0c6980bb --- /dev/null +++ b/mods/PLAYER/simple_skins/readme.md @@ -0,0 +1,7 @@ +Simple Skins, MineClone 2 Edition + +Simple Skins mod to allow players to select a skin. +Use the chat command /setskin to change skin. + +Original mod: +https://forum.minetest.net/viewtopic.php?id=9100 diff --git a/mods/PLAYER/simple_skins/textures/character_1.png b/mods/PLAYER/simple_skins/textures/character_1.png new file mode 100644 index 0000000000000000000000000000000000000000..71f02471dc622249681a511a66087b803e97dbdf GIT binary patch literal 5505 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 CAYT6f literal 0 HcmV?d00001 diff --git a/mods/PLAYER/simple_skins/textures/inventory_plus_skins.png b/mods/PLAYER/simple_skins/textures/inventory_plus_skins.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc97759cd8c906632a0ff2712b647c23abc2278 GIT binary patch literal 2182 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4kiW$h6xih%orHh?5jc|N`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHU}kKDwp&hzKFh$U0f^}>kNMa?qyyq9{Wr|9qC+Ba>{QZr0&+m7<*3nnr#V<54%WVJB zHu09`zMFq-d#lsm&ieV3HGKBl&(Aq`Z*_~zKJ_Zg!$C_j_R7{N#qE8(0l$oO9-jK) zcISDq;kqW(G6S;%X@{@!8d@&ct8rn0SNF3_>(Um9J0+59i|_tS*0!@Q`#VACo9pS+ z3&uVA35%Oe_gC&rUTCa8|EHDc85<5;*@KdaNBU3K82y|6x@^lezgVT{yfp&TZ(H^n zm=*sP-|N7kd(=d(=6veBPsi*8Umr}cF%Z|;a7#@uYKeQ^&%>MD_h&S3++?c3U&_#7 zQ0`*zOklt+d1fiMtA$5A=TLd?K>_`LP*3%hz}NotYpe zAXxiJ@=5}8;~D{_PwMSWGOI2gPe|&~d={Z}d{zyY=CUb8x1>ThpI1CSr|6t&@w%ha z1Cl&drw8o_DSM-sI8iwLaO=$Mqf12BX}g7gS+RBJv*?VCOQ%I=-pO+R_2ojczwP%+ zJFh=%lfUs#xi`m6(&p8Y&X8G&$7ZI@OZ~Kld*`-4T-xileA4O;-y0bm79Y9T{8?Rk z@06`)BP{-YYBrrwyf5a$w8=XxnUnPdB+k!fRM~QS&P^x99cS-6JM(GU>K6y3j{2Tk za8^QsXL3wTN%8lDcj5PZc>Z^pfA6*2yz;mG4CC2WsqfVD*}=EQRNMKCr0`?IqJ`I%$hVxi z`q;>_Kk+x8(bD96`F^`+@4logDRDP_+}Y**K5o&WX?Z%ERo}n0iETG+>?^$f!atee z!A2$1yB4+!?`}Tj<8xCcRvkZc>8isEIYrx6`=$r^>*w9K`c~G{!BO#Y$x^3B=W=gLX&$U~GE_ykl0>fATbuk}cCU&!8obd;b^7V=w(e6czgZ^F%8r@x_5I{2A7*^GJ$=z3 zy>qiC$zObvaOU%_d2hG^--g9SS2ur?o3TaBX5mkdh)9KZZWC|6{PSbtjq__OPySdJ zbN=DPsey8{_P#S%nfCttwLMZIt1i6la=vzRx%|8B5lc+x$N#WPdA^pRltbKa|CVQu zxjQE1sJ)&Ru;pr$1kbtii)L?R@lk2sCigKYW!d}Jk3Bm1J|%<)$;+hu=lXV@HSR%p z{4cIGD|8n;+r8`lW#?Sq>9&TM`_~x7el)+l&qK-Sa%I)YCvz&)nwTp$+00z#x#_pj zxp^_0FRyX?xnnBB#1mP&T?2RgI9uB>@RGV?>#s#a*8c~yR!9p!KV|mpR`_X z%lf;bU+6=yTb$9OY4(4Z1FABYebVdu#Fv?0sAchp-!S%L(AwCo`&XS?8gwJ`tln;u z!1O1w8*U1S>6}*u@f4~+MCeTg@8{v!7Ys})7w$X;K6)>{1Ijud7FyK|y9Un#D7P``8)&p(-X#kM6| zjh?S?wK{t|Qe{Q(_U2IE?2gb?k)`fL2$v|<&zm07&r?&B8wRq^pruEv0|xx z83O|Ydx@v7EBj++7BMaM+N2du3=E7XJY5_^G|uNva`X@hlsH~*%pP*K4J*O_S`~I#AyqUF1diuGi zm&&pwj_RLQeeiDbbEj(V=koU??H|so-kYR1MeUGAoQAK;-H=PZw){$`V^3&B$xG*p Date: Tue, 5 Mar 2019 00:15:20 +0100 Subject: [PATCH 051/798] Replace damage sound to something gender-neutral --- mods/CORE/mcl_sounds/README.txt | 5 ++++- mods/CORE/mcl_sounds/sounds/player_damage.ogg | Bin 5775 -> 6176 bytes 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mods/CORE/mcl_sounds/README.txt b/mods/CORE/mcl_sounds/README.txt index 54eeab60..43e6cf30 100644 --- a/mods/CORE/mcl_sounds/README.txt +++ b/mods/CORE/mcl_sounds/README.txt @@ -67,10 +67,13 @@ blukotek (CC0 1.0) https://www.freesound.org/people/blukotek/sounds/251660/ default_dig_snappy.ogg +sonictechtonic (CC BY 3.0) +https://www.freesound.org/people/sonictechtonic/sounds/241872/ + player_damage.ogg + Voxelands project (CC BY-SA 3.0) mcl_sounds_place_node_water.ogg mcl_sounds_dug_water.ogg - player_damage.ogg (Note: Artists from the Voxelands project include: sdzen, darkrose, sapier, Tom Peter, Telaron, juskiddink) diff --git a/mods/CORE/mcl_sounds/sounds/player_damage.ogg b/mods/CORE/mcl_sounds/sounds/player_damage.ogg index 063e6c08c4da5cd7600b06c9f19edf7592537da3..788808710c1923c9ae797b7534b66a97dd18dd6d 100644 GIT binary patch literal 6176 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 z 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 From 2c5038ad058cff4219b09ce5b6c7c113e7d43fe7 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 01:50:51 +0100 Subject: [PATCH 052/798] Player skins: Adjust inventory preview image, too --- mods/HUD/mcl_inventory/creative.lua | 14 ++++-- mods/HUD/mcl_inventory/depends.txt | 1 + mods/HUD/mcl_inventory/init.lua | 15 ++++-- mods/PLAYER/mcl_player/init.lua | 14 +++++- mods/PLAYER/simple_skins/init.lua | 47 +++++++++--------- .../PLAYER/simple_skins/textures/player_1.png | Bin 0 -> 2625 bytes 6 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 mods/PLAYER/simple_skins/textures/player_1.png diff --git a/mods/HUD/mcl_inventory/creative.lua b/mods/HUD/mcl_inventory/creative.lua index dd5d0bd8..f2afb964 100644 --- a/mods/HUD/mcl_inventory/creative.lua +++ b/mods/HUD/mcl_inventory/creative.lua @@ -4,6 +4,9 @@ local players = {} -- Containing all the items for each Creative Mode tab local inventory_lists = {} +local show_armor = minetest.get_modpath("3d_armor") ~= nil +local mod_player = minetest.get_modpath("mcl_player") ~= nil + -- TODO: Brewing is disabled. Add brewing (uncommented code) when it is implemented properly -- Create tables @@ -271,15 +274,20 @@ mcl_inventory.set_creative_formspec = function(player, start_i, pagenum, inv_siz inv_bg = "crafting_inventory_creative_survival.png" -- Show armor and player image - local show_armor = minetest.get_modpath("3d_armor") - local img = "player.png" + local img, img_player + if mod_player then + img_player = mcl_player.player_get_preview(player) + else + img_player = "player.png" + end + img = img_player local player_preview = "image[3.9,1.4;1.2333,2.4666;"..img.."]" if show_armor and armor.textures[playername] and armor.textures[playername].preview then img = armor.textures[playername].preview local s1 = img:find("character_preview") if s1 ~= nil then s1 = img:sub(s1+21) - img = "player.png"..s1 + img = img_player..s1 end player_preview = "image[3.9,1.4;1.2333,2.4666;"..img.."]" end diff --git a/mods/HUD/mcl_inventory/depends.txt b/mods/HUD/mcl_inventory/depends.txt index a174c0ee..46d93c42 100644 --- a/mods/HUD/mcl_inventory/depends.txt +++ b/mods/HUD/mcl_inventory/depends.txt @@ -1,3 +1,4 @@ mcl_init +mcl_player? _mcl_autogroup? 3d_armor? diff --git a/mods/HUD/mcl_inventory/init.lua b/mods/HUD/mcl_inventory/init.lua index 4bd2ef80..3989af19 100644 --- a/mods/HUD/mcl_inventory/init.lua +++ b/mods/HUD/mcl_inventory/init.lua @@ -1,8 +1,7 @@ mcl_inventory = {} -local show_armor = false -if minetest.get_modpath("3d_armor") ~= nil then show_armor = true end - +local show_armor = minetest.get_modpath("3d_armor") ~= nil +local mod_player = minetest.get_modpath("mcl_player") ~= nil -- Returns a single itemstack in the given inventory to the main inventory, or drop it when there's no space left local function return_item(itemstack, dropper, pos, inv) @@ -59,14 +58,20 @@ local function set_inventory(player, armor_change_only) local player_name = player:get_player_name() -- Show armor and player image - local img = "player.png" + local img, img_player + if mod_player then + img_player = mcl_player.player_get_preview(player) + else + img_player = "player.png" + end + img = img_player local player_preview = "image[0.6,0.2;2,4;"..img.."]" if show_armor and armor.textures[player_name] and armor.textures[player_name].preview then img = armor.textures[player_name].preview local s1 = img:find("character_preview") if s1 ~= nil then s1 = img:sub(s1+21) - img = "player.png"..s1 + img = img_player..s1 end player_preview = "image[1.1,0.2;2,4;"..img.."]" end diff --git a/mods/PLAYER/mcl_player/init.lua b/mods/PLAYER/mcl_player/init.lua index 599acd65..b01c0b4d 100644 --- a/mods/PLAYER/mcl_player/init.lua +++ b/mods/PLAYER/mcl_player/init.lua @@ -70,10 +70,22 @@ function mcl_player.player_set_model(player, model_name) player_model[name] = model_name end -function mcl_player.player_set_textures(player, textures) +function mcl_player.player_set_textures(player, textures, preview) local name = player:get_player_name() player_textures[name] = textures player:set_properties({textures = textures,}) + if preview then + player:set_attribute("mcl_player:preview", preview) + end +end + +function mcl_player.player_get_preview(player) + local preview = player:get_attribute("mcl_player:preview") + if not preview then + return "player.png" + else + return preview + end end function mcl_player.player_set_animation(player, anim_name, speed) diff --git a/mods/PLAYER/simple_skins/init.lua b/mods/PLAYER/simple_skins/init.lua index 3a41490f..077278b7 100644 --- a/mods/PLAYER/simple_skins/init.lua +++ b/mods/PLAYER/simple_skins/init.lua @@ -3,7 +3,7 @@ -- Released by TenPlus1 and based on Zeg9's code under MIT license skins = { - skins = {}, meta = {}, + skins = {}, previews = {}, meta = {}, modpath = minetest.get_modpath("simple_skins"), skin_count = 0, -- counter of _custom_ skins (all skins except character.png) } @@ -49,18 +49,30 @@ while true do skins.skin_count = skins.skin_count + 1 end -skins.set_player_skin = function(player, skin) +skins.set_player_skin = function(player, skin_id) if not player then - return + return false end local playername = player:get_player_name() + local skin, preview + if skin_id == nil or type(skin_id) ~= "number" or skin_id < 0 or skin_id > skins.skin_count then + return false + elseif skin_id == 0 then + skin = "character" + preview = "player" + else + skin = "character_" .. tostring(skin_id) + preview = "player_" .. tostring(skin_id) + end skins.skins[playername] = skin - player:set_attribute("simple_skins:skin", skins.skins[playername]) + skins.previews[playername] = preview + player:set_attribute("simple_skins:skin_id", skin_id) skins.update_player_skin(player) if minetest.get_modpath("3d_armor") then armor.textures[playername].skin = skin .. ".png" armor:update_player_visuals(player) end + return true end skins.update_player_skin = function(player) @@ -68,27 +80,21 @@ skins.update_player_skin = function(player) return end local playername = player:get_player_name() - mcl_player.player_set_textures(player, { skins.skins[playername] .. ".png" }) + mcl_player.player_set_textures(player, { skins.skins[playername] .. ".png" }, skins.previews[playername] .. ".png" ) end -- load player skin on join minetest.register_on_joinplayer(function(player) local name = player:get_player_name() - local skin = player:get_attribute("simple_skins:skin") + local skin_id = player:get_attribute("simple_skins:skin_id") local set_skin -- do we already have a skin in player attributes? - if skin then - set_skin = skin - + if skin_id then + set_skin = tonumber(skin_id) -- otherwise use random skin if not set else - local r = math.random(0, skins.skin_count) - if r == 0 then - set_skin = "character" - else - set_skin = "character_" .. r - end + set_skin = math.random(0, skins.skin_count) end if set_skin then skins.set_player_skin(player, set_skin) @@ -125,16 +131,11 @@ minetest.register_chatcommand("setskin", { end local skin - if skin_id == nil or skin_id > skins.skin_count or skin_id < 0 then + local ok = skins.set_player_skin(player, skin_id) + if not ok then return false, S("Invalid skin number! Valid numbers: 0 to @1", skins.skin_count) - elseif skin_id == 0 then - skin = "character" - else - skin = "character_" .. tostring(skin_id) end - - skins.set_player_skin(player, skin) - local skinfile = skin..".png" + local skinfile = "Skin #"..skin_id local your_msg = S("Your skin has been set to: @1", skinfile) if name == playername then diff --git a/mods/PLAYER/simple_skins/textures/player_1.png b/mods/PLAYER/simple_skins/textures/player_1.png new file mode 100644 index 0000000000000000000000000000000000000000..3d7af2a980c2412be1f72e39fd6d7ffeda8f7fb3 GIT binary patch literal 2625 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 K=d#Wzp$Pz}JO2~_ literal 0 HcmV?d00001 From e47bd634d4a35a087d342c10629fc7af9cb90612 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 10:29:49 +0100 Subject: [PATCH 053/798] Player skins: Update inventory preview (part 2) --- mods/HUD/mcl_inventory/creative.lua | 32 ++++++++++++++++++++++++++++ mods/HUD/mcl_inventory/init.lua | 6 ++++++ mods/PLAYER/simple_skins/depends.txt | 1 + mods/PLAYER/simple_skins/init.lua | 3 +++ 4 files changed, 42 insertions(+) diff --git a/mods/HUD/mcl_inventory/creative.lua b/mods/HUD/mcl_inventory/creative.lua index f2afb964..d9ad2e94 100644 --- a/mods/HUD/mcl_inventory/creative.lua +++ b/mods/HUD/mcl_inventory/creative.lua @@ -570,6 +570,38 @@ if minetest.settings:get_bool("creative_mode") then end end + mcl_inventory.update_inventory_formspec = function(player) + local page = nil + + local name = player:get_player_name() + + if players[name].page then + page = players[name].page + else + page = "nix" + end + + -- Figure out current scroll bar from formspec + local formspec = player:get_inventory_formspec() + local start_i = players[name].start_i + + local inv_size + if page == "nix" then + local inv = minetest.get_inventory({type="detached", name="creative_"..name}) + inv_size = inv:get_size("main") + elseif page ~= nil and page ~= "inv" then + inv_size = #(inventory_lists[page]) + else + inv_size = 0 + end + + local filter = players[name].filter + if filter == nil then + filter = "" + end + + mcl_inventory.set_creative_formspec(player, start_i, start_i / (9*5) + 1, inv_size, false, page, filter) + end end minetest.register_on_joinplayer(function(player) diff --git a/mods/HUD/mcl_inventory/init.lua b/mods/HUD/mcl_inventory/init.lua index 3989af19..415c3075 100644 --- a/mods/HUD/mcl_inventory/init.lua +++ b/mods/HUD/mcl_inventory/init.lua @@ -127,6 +127,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end) +if not minetest.settings:get_bool("creative_mode") then + mcl_inventory.update_inventory_formspec = function(player) + set_inventory(player) + end +end + -- Drop crafting grid items on leaving minetest.register_on_leaveplayer(function(player) return_fields(player, "craft") diff --git a/mods/PLAYER/simple_skins/depends.txt b/mods/PLAYER/simple_skins/depends.txt index 1927ce89..e0804a6f 100644 --- a/mods/PLAYER/simple_skins/depends.txt +++ b/mods/PLAYER/simple_skins/depends.txt @@ -1,3 +1,4 @@ mcl_player +mcl_inventory? intllib? 3d_armor? diff --git a/mods/PLAYER/simple_skins/init.lua b/mods/PLAYER/simple_skins/init.lua index 077278b7..98d3e7e8 100644 --- a/mods/PLAYER/simple_skins/init.lua +++ b/mods/PLAYER/simple_skins/init.lua @@ -72,6 +72,9 @@ skins.set_player_skin = function(player, skin_id) armor.textures[playername].skin = skin .. ".png" armor:update_player_visuals(player) end + if minetest.get_modpath("mcl_inventory") then + mcl_inventory.update_inventory_formspec(player) + end return true end From aae30bba3964437830a0793aac64df5eda4743a7 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 10:36:53 +0100 Subject: [PATCH 054/798] Rename simple_skins to mcl_skins --- .../minetest-3d_armor/3d_armor/armor.lua | 15 ++++++-- .../{simple_skins => mcl_skins}/depends.txt | 0 .../description.txt | 0 .../{simple_skins => mcl_skins}/init.lua | 36 +++++++++--------- .../{simple_skins => mcl_skins}/intllib.lua | 0 .../{simple_skins => mcl_skins}/license.txt | 0 .../{simple_skins => mcl_skins}/locale/fr.po | 0 .../{simple_skins => mcl_skins}/locale/it.po | 0 .../{simple_skins => mcl_skins}/locale/ms.po | 0 .../locale/template.pot | 0 .../meta/character.txt | 0 .../meta/character_1.txt | 0 mods/PLAYER/mcl_skins/mod.conf | 1 + .../{simple_skins => mcl_skins}/readme.md | 0 .../textures/character_1.png | Bin .../textures/inventory_plus_skins.png | Bin .../textures/player_1.png | Bin mods/PLAYER/simple_skins/mod.conf | 1 - 18 files changed, 31 insertions(+), 22 deletions(-) rename mods/PLAYER/{simple_skins => mcl_skins}/depends.txt (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/description.txt (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/init.lua (78%) rename mods/PLAYER/{simple_skins => mcl_skins}/intllib.lua (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/license.txt (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/locale/fr.po (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/locale/it.po (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/locale/ms.po (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/locale/template.pot (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/meta/character.txt (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/meta/character_1.txt (100%) create mode 100644 mods/PLAYER/mcl_skins/mod.conf rename mods/PLAYER/{simple_skins => mcl_skins}/readme.md (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/textures/character_1.png (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/textures/inventory_plus_skins.png (100%) rename mods/PLAYER/{simple_skins => mcl_skins}/textures/player_1.png (100%) delete mode 100644 mods/PLAYER/simple_skins/mod.conf diff --git a/mods/ITEMS/minetest-3d_armor/3d_armor/armor.lua b/mods/ITEMS/minetest-3d_armor/3d_armor/armor.lua index 0b07fb14..ae9799f9 100644 --- a/mods/ITEMS/minetest-3d_armor/3d_armor/armor.lua +++ b/mods/ITEMS/minetest-3d_armor/3d_armor/armor.lua @@ -66,7 +66,9 @@ armor = { version = "0.4.6", } -if minetest.get_modpath("skins") then +if minetest.get_modpath("mcl_skins") then + skin_mod = "mcl_skins" +elseif minetest.get_modpath("skins") then skin_mod = "skins" elseif minetest.get_modpath("simple_skins") then skin_mod = "simple_skins" @@ -197,7 +199,9 @@ end armor.get_player_skin = function(self, name) local skin = nil - if skin_mod == "skins" or skin_mod == "simple_skins" then + if skin_mod == "mcl_skins" then + skin = mcl_skins.skins[name] + elseif skin_mod == "skins" or skin_mod == "simple_skins" then skin = skins.skins[name] elseif skin_mod == "u_skins" then skin = u_skins.u_skins[name] @@ -380,7 +384,12 @@ minetest.register_on_joinplayer(function(player) wielditem = "3d_armor_trans.png", preview = armor.default_skin.."_preview.png", } - if skin_mod == "skins" then + if skin_mod == "mcl_skins" then + local skin = mcl_skins.skins[name] + if skin then + armor.textures[name].skin = skin..".png" + end + elseif skin_mod == "skins" then local skin = skins.skins[name] if skin and skins.get_type(skin) == skins.type.MODEL then armor.textures[name].skin = skin..".png" diff --git a/mods/PLAYER/simple_skins/depends.txt b/mods/PLAYER/mcl_skins/depends.txt similarity index 100% rename from mods/PLAYER/simple_skins/depends.txt rename to mods/PLAYER/mcl_skins/depends.txt diff --git a/mods/PLAYER/simple_skins/description.txt b/mods/PLAYER/mcl_skins/description.txt similarity index 100% rename from mods/PLAYER/simple_skins/description.txt rename to mods/PLAYER/mcl_skins/description.txt diff --git a/mods/PLAYER/simple_skins/init.lua b/mods/PLAYER/mcl_skins/init.lua similarity index 78% rename from mods/PLAYER/simple_skins/init.lua rename to mods/PLAYER/mcl_skins/init.lua index 98d3e7e8..fc892471 100644 --- a/mods/PLAYER/simple_skins/init.lua +++ b/mods/PLAYER/mcl_skins/init.lua @@ -2,15 +2,15 @@ -- Released by TenPlus1 and based on Zeg9's code under MIT license -skins = { +mcl_skins = { skins = {}, previews = {}, meta = {}, - modpath = minetest.get_modpath("simple_skins"), + modpath = minetest.get_modpath("mcl_skins"), skin_count = 0, -- counter of _custom_ skins (all skins except character.png) } -- Load support for intllib. -local S, NS = dofile(skins.modpath .. "/intllib.lua") +local S, NS = dofile(mcl_skins.modpath .. "/intllib.lua") -- load skin list and metadata @@ -21,7 +21,7 @@ while true do skin = "character_" .. id -- does skin file exist ? - f = io.open(skins.modpath .. "/textures/" .. skin .. ".png") + f = io.open(mcl_skins.modpath .. "/textures/" .. skin .. ".png") -- escape loop if not found and remove last entry if not f then @@ -32,7 +32,7 @@ while true do f:close() -- does metadata exist for that skin file ? - f = io.open(skins.modpath .. "/meta/" .. skin .. ".txt") + f = io.open(mcl_skins.modpath .. "/meta/" .. skin .. ".txt") if f then data = minetest.deserialize("return {" .. f:read('*all') .. "}") @@ -40,22 +40,22 @@ while true do end -- add metadata to list - skins.meta[skin] = { + mcl_skins.meta[skin] = { name = data and data.name or "", author = data and data.author or "", } id = id + 1 - skins.skin_count = skins.skin_count + 1 + mcl_skins.skin_count = mcl_skins.skin_count + 1 end -skins.set_player_skin = function(player, skin_id) +mcl_skins.set_player_skin = function(player, skin_id) if not player then return false end local playername = player:get_player_name() local skin, preview - if skin_id == nil or type(skin_id) ~= "number" or skin_id < 0 or skin_id > skins.skin_count then + if skin_id == nil or type(skin_id) ~= "number" or skin_id < 0 or skin_id > mcl_skins.skin_count then return false elseif skin_id == 0 then skin = "character" @@ -64,10 +64,10 @@ skins.set_player_skin = function(player, skin_id) skin = "character_" .. tostring(skin_id) preview = "player_" .. tostring(skin_id) end - skins.skins[playername] = skin - skins.previews[playername] = preview + mcl_skins.skins[playername] = skin + mcl_skins.previews[playername] = preview player:set_attribute("simple_skins:skin_id", skin_id) - skins.update_player_skin(player) + mcl_skins.update_player_skin(player) if minetest.get_modpath("3d_armor") then armor.textures[playername].skin = skin .. ".png" armor:update_player_visuals(player) @@ -78,12 +78,12 @@ skins.set_player_skin = function(player, skin_id) return true end -skins.update_player_skin = function(player) +mcl_skins.update_player_skin = function(player) if not player then return end local playername = player:get_player_name() - mcl_player.player_set_textures(player, { skins.skins[playername] .. ".png" }, skins.previews[playername] .. ".png" ) + mcl_player.player_set_textures(player, { mcl_skins.skins[playername] .. ".png" }, mcl_skins.previews[playername] .. ".png" ) end -- load player skin on join @@ -97,10 +97,10 @@ minetest.register_on_joinplayer(function(player) set_skin = tonumber(skin_id) -- otherwise use random skin if not set else - set_skin = math.random(0, skins.skin_count) + set_skin = math.random(0, mcl_skins.skin_count) end if set_skin then - skins.set_player_skin(player, set_skin) + mcl_skins.set_player_skin(player, set_skin) end end) @@ -134,9 +134,9 @@ minetest.register_chatcommand("setskin", { end local skin - local ok = skins.set_player_skin(player, skin_id) + local ok = mcl_skins.set_player_skin(player, skin_id) if not ok then - return false, S("Invalid skin number! Valid numbers: 0 to @1", skins.skin_count) + return false, S("Invalid skin number! Valid numbers: 0 to @1", mcl_skins.skin_count) end local skinfile = "Skin #"..skin_id diff --git a/mods/PLAYER/simple_skins/intllib.lua b/mods/PLAYER/mcl_skins/intllib.lua similarity index 100% rename from mods/PLAYER/simple_skins/intllib.lua rename to mods/PLAYER/mcl_skins/intllib.lua diff --git a/mods/PLAYER/simple_skins/license.txt b/mods/PLAYER/mcl_skins/license.txt similarity index 100% rename from mods/PLAYER/simple_skins/license.txt rename to mods/PLAYER/mcl_skins/license.txt diff --git a/mods/PLAYER/simple_skins/locale/fr.po b/mods/PLAYER/mcl_skins/locale/fr.po similarity index 100% rename from mods/PLAYER/simple_skins/locale/fr.po rename to mods/PLAYER/mcl_skins/locale/fr.po diff --git a/mods/PLAYER/simple_skins/locale/it.po b/mods/PLAYER/mcl_skins/locale/it.po similarity index 100% rename from mods/PLAYER/simple_skins/locale/it.po rename to mods/PLAYER/mcl_skins/locale/it.po diff --git a/mods/PLAYER/simple_skins/locale/ms.po b/mods/PLAYER/mcl_skins/locale/ms.po similarity index 100% rename from mods/PLAYER/simple_skins/locale/ms.po rename to mods/PLAYER/mcl_skins/locale/ms.po diff --git a/mods/PLAYER/simple_skins/locale/template.pot b/mods/PLAYER/mcl_skins/locale/template.pot similarity index 100% rename from mods/PLAYER/simple_skins/locale/template.pot rename to mods/PLAYER/mcl_skins/locale/template.pot diff --git a/mods/PLAYER/simple_skins/meta/character.txt b/mods/PLAYER/mcl_skins/meta/character.txt similarity index 100% rename from mods/PLAYER/simple_skins/meta/character.txt rename to mods/PLAYER/mcl_skins/meta/character.txt diff --git a/mods/PLAYER/simple_skins/meta/character_1.txt b/mods/PLAYER/mcl_skins/meta/character_1.txt similarity index 100% rename from mods/PLAYER/simple_skins/meta/character_1.txt rename to mods/PLAYER/mcl_skins/meta/character_1.txt diff --git a/mods/PLAYER/mcl_skins/mod.conf b/mods/PLAYER/mcl_skins/mod.conf new file mode 100644 index 00000000..96f82764 --- /dev/null +++ b/mods/PLAYER/mcl_skins/mod.conf @@ -0,0 +1 @@ +name = mcl_skins diff --git a/mods/PLAYER/simple_skins/readme.md b/mods/PLAYER/mcl_skins/readme.md similarity index 100% rename from mods/PLAYER/simple_skins/readme.md rename to mods/PLAYER/mcl_skins/readme.md diff --git a/mods/PLAYER/simple_skins/textures/character_1.png b/mods/PLAYER/mcl_skins/textures/character_1.png similarity index 100% rename from mods/PLAYER/simple_skins/textures/character_1.png rename to mods/PLAYER/mcl_skins/textures/character_1.png diff --git a/mods/PLAYER/simple_skins/textures/inventory_plus_skins.png b/mods/PLAYER/mcl_skins/textures/inventory_plus_skins.png similarity index 100% rename from mods/PLAYER/simple_skins/textures/inventory_plus_skins.png rename to mods/PLAYER/mcl_skins/textures/inventory_plus_skins.png diff --git a/mods/PLAYER/simple_skins/textures/player_1.png b/mods/PLAYER/mcl_skins/textures/player_1.png similarity index 100% rename from mods/PLAYER/simple_skins/textures/player_1.png rename to mods/PLAYER/mcl_skins/textures/player_1.png diff --git a/mods/PLAYER/simple_skins/mod.conf b/mods/PLAYER/simple_skins/mod.conf deleted file mode 100644 index aff90aab..00000000 --- a/mods/PLAYER/simple_skins/mod.conf +++ /dev/null @@ -1 +0,0 @@ -name = simple_skins From 788ecbf178fc471ae310c9086f07750205224800 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 10:40:37 +0100 Subject: [PATCH 055/798] Update mcl_skins readme --- mods/PLAYER/mcl_skins/init.lua | 4 +--- mods/PLAYER/mcl_skins/readme.md | 12 +++++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mods/PLAYER/mcl_skins/init.lua b/mods/PLAYER/mcl_skins/init.lua index fc892471..99228f58 100644 --- a/mods/PLAYER/mcl_skins/init.lua +++ b/mods/PLAYER/mcl_skins/init.lua @@ -1,6 +1,4 @@ --- Simple Skins mod for Minetest (MineClone 2 Edition) - --- Released by TenPlus1 and based on Zeg9's code under MIT license +-- Skins for MineClone 2 mcl_skins = { skins = {}, previews = {}, meta = {}, diff --git a/mods/PLAYER/mcl_skins/readme.md b/mods/PLAYER/mcl_skins/readme.md index 0c6980bb..bbe5309a 100644 --- a/mods/PLAYER/mcl_skins/readme.md +++ b/mods/PLAYER/mcl_skins/readme.md @@ -1,7 +1,13 @@ -Simple Skins, MineClone 2 Edition += Skins for MineClone 2 = -Simple Skins mod to allow players to select a skin. +Simple mod to allow players to select a skin. Use the chat command /setskin to change skin. -Original mod: +Forked from Simple Skins by TenPlus1. https://forum.minetest.net/viewtopic.php?id=9100 + +== License == +Code under MIT license +Origial authors: +- TenPlus1 +- Zeg9 From b2c19d9ec60ee1e9c88b89685922b3decdf2e326 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 10:41:57 +0100 Subject: [PATCH 056/798] mcl_skins: Fix attribute name --- mods/PLAYER/mcl_skins/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/PLAYER/mcl_skins/init.lua b/mods/PLAYER/mcl_skins/init.lua index 99228f58..b584130c 100644 --- a/mods/PLAYER/mcl_skins/init.lua +++ b/mods/PLAYER/mcl_skins/init.lua @@ -64,7 +64,7 @@ mcl_skins.set_player_skin = function(player, skin_id) end mcl_skins.skins[playername] = skin mcl_skins.previews[playername] = preview - player:set_attribute("simple_skins:skin_id", skin_id) + player:set_attribute("mcl_skins:skin_id", tostring(skin_id)) mcl_skins.update_player_skin(player) if minetest.get_modpath("3d_armor") then armor.textures[playername].skin = skin .. ".png" @@ -88,7 +88,7 @@ end minetest.register_on_joinplayer(function(player) local name = player:get_player_name() - local skin_id = player:get_attribute("simple_skins:skin_id") + local skin_id = player:get_attribute("mcl_skins:skin_id") local set_skin -- do we already have a skin in player attributes? if skin_id then From 5601f8e0b79fab373b871bde2f5413b2cfd11fad Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 10:45:14 +0100 Subject: [PATCH 057/798] Rename some files --- mods/PLAYER/mcl_skins/{license.txt => LICENSE.txt} | 0 mods/PLAYER/mcl_skins/{readme.md => README.md} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename mods/PLAYER/mcl_skins/{license.txt => LICENSE.txt} (100%) rename mods/PLAYER/mcl_skins/{readme.md => README.md} (100%) diff --git a/mods/PLAYER/mcl_skins/license.txt b/mods/PLAYER/mcl_skins/LICENSE.txt similarity index 100% rename from mods/PLAYER/mcl_skins/license.txt rename to mods/PLAYER/mcl_skins/LICENSE.txt diff --git a/mods/PLAYER/mcl_skins/readme.md b/mods/PLAYER/mcl_skins/README.md similarity index 100% rename from mods/PLAYER/mcl_skins/readme.md rename to mods/PLAYER/mcl_skins/README.md From 62eaf60938e4b1f7c28db6b76143498911b82ea0 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 10:57:57 +0100 Subject: [PATCH 058/798] mcl_skins: Add fallback code if skin was missing --- mods/PLAYER/mcl_skins/init.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mods/PLAYER/mcl_skins/init.lua b/mods/PLAYER/mcl_skins/init.lua index b584130c..a822218d 100644 --- a/mods/PLAYER/mcl_skins/init.lua +++ b/mods/PLAYER/mcl_skins/init.lua @@ -73,6 +73,7 @@ mcl_skins.set_player_skin = function(player, skin_id) if minetest.get_modpath("mcl_inventory") then mcl_inventory.update_inventory_formspec(player) end + minetest.log("action", "[mcl_skins] Player skin for "..playername.." set to skin #"..skin_id) return true end @@ -98,7 +99,12 @@ minetest.register_on_joinplayer(function(player) set_skin = math.random(0, mcl_skins.skin_count) end if set_skin then - mcl_skins.set_player_skin(player, set_skin) + local ok = mcl_skins.set_player_skin(player, set_skin) + if not ok then + set_skin = math.random(0, mcl_skins.skin_count) + minetest.log("warning", "[mcl_skins] Player skin for "..name.." not found, falling back to skin #"..set_skin) + mcl_skins.set_player_skin(player, set_skin) + end end end) @@ -148,3 +154,5 @@ minetest.register_chatcommand("setskin", { end, }) + +minetest.log("action", "[mcl_skins] Mod initialized with "..mcl_skins.skin_count.." custom skin(s)") From 9a54383a23e50580d2cca242b27e8d8864067b6f Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 5 Mar 2019 11:43:09 +0100 Subject: [PATCH 059/798] Add mesh hand --- mods/PLAYER/mcl_meshhand/README.md | 9 ++++ mods/PLAYER/mcl_meshhand/depends.txt | 1 + mods/PLAYER/mcl_meshhand/description.txt | 1 + mods/PLAYER/mcl_meshhand/init.lua | 47 ++++++++++++++++++ mods/PLAYER/mcl_meshhand/mod.conf | 1 + .../mcl_meshhand/models/mcl_meshhand.b3d | Bin 0 -> 1023 bytes .../mcl_meshhand/models/mcl_meshhand.blend | Bin 0 -> 554856 bytes mods/PLAYER/mcl_skins/init.lua | 20 ++++++-- 8 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 mods/PLAYER/mcl_meshhand/README.md create mode 100644 mods/PLAYER/mcl_meshhand/depends.txt create mode 100644 mods/PLAYER/mcl_meshhand/description.txt create mode 100644 mods/PLAYER/mcl_meshhand/init.lua create mode 100644 mods/PLAYER/mcl_meshhand/mod.conf create mode 100644 mods/PLAYER/mcl_meshhand/models/mcl_meshhand.b3d create mode 100644 mods/PLAYER/mcl_meshhand/models/mcl_meshhand.blend diff --git a/mods/PLAYER/mcl_meshhand/README.md b/mods/PLAYER/mcl_meshhand/README.md new file mode 100644 index 00000000..2c796ff3 --- /dev/null +++ b/mods/PLAYER/mcl_meshhand/README.md @@ -0,0 +1,9 @@ +Mesh hand mod for MineClone 2. + +This mod uses a better-looking mesh for the wieldhand and applies the player skin texture to it. + +== Credits == +Based on 3D Hand [newhand] mod by jordan4ibanez. +https://forum.minetest.net/viewtopic.php?t=16435 + +License: CC0 diff --git a/mods/PLAYER/mcl_meshhand/depends.txt b/mods/PLAYER/mcl_meshhand/depends.txt new file mode 100644 index 00000000..f8be59ba --- /dev/null +++ b/mods/PLAYER/mcl_meshhand/depends.txt @@ -0,0 +1 @@ +mcl_skins? diff --git a/mods/PLAYER/mcl_meshhand/description.txt b/mods/PLAYER/mcl_meshhand/description.txt new file mode 100644 index 00000000..7a4daae5 --- /dev/null +++ b/mods/PLAYER/mcl_meshhand/description.txt @@ -0,0 +1 @@ +Applies the player skin texture to the hand. diff --git a/mods/PLAYER/mcl_meshhand/init.lua b/mods/PLAYER/mcl_meshhand/init.lua new file mode 100644 index 00000000..1e318a5b --- /dev/null +++ b/mods/PLAYER/mcl_meshhand/init.lua @@ -0,0 +1,47 @@ +local has_mcl_skins = minetest.get_modpath("mcl_skins") ~= nil + +-- mcl_skins is enabled +if has_mcl_skins == true then + --generate a node for every skin + for _,texture in pairs(mcl_skins.list) do + minetest.register_node("mcl_meshhand:"..texture, { + description = "", + tiles = {texture..".png"}, + inventory_image = "blank.png", + visual_scale = 1, + wield_scale = {x=1,y=1,z=1}, + paramtype = "light", + drawtype = "mesh", + mesh = "mcl_meshhand.b3d", + node_placement_prediction = "", + }) + end + --change the player's hand to their skin + minetest.register_on_joinplayer(function(player) + local skin = mcl_skins.skins[player:get_player_name()] + player:get_inventory():set_stack("hand", 1, "mcl_meshhand:"..skin) + end) + + mcl_skins.register_on_set_skin(function(player, skin) + local name = player:get_player_name() + player:get_inventory():set_stack("hand", 1, "mcl_meshhand:"..skin) + end) + +--do default skin if no skin mod installed +else + minetest.register_node("mcl_meshhand:hand", { + description = "", + tiles = {"character.png"}, + inventory_image = "blank.png", + visual_scale = 1, + wield_scale = {x=1,y=1,z=1}, + paramtype = "light", + drawtype = "mesh", + mesh = "mcl_meshhand.b3d", + node_placement_prediction = "", + }) + + minetest.register_on_joinplayer(function(player) + player:get_inventory():set_stack("hand", 1, "mcl_meshhand:hand") + end) +end diff --git a/mods/PLAYER/mcl_meshhand/mod.conf b/mods/PLAYER/mcl_meshhand/mod.conf new file mode 100644 index 00000000..6b57f4a4 --- /dev/null +++ b/mods/PLAYER/mcl_meshhand/mod.conf @@ -0,0 +1 @@ +name = mcl_meshhand diff --git a/mods/PLAYER/mcl_meshhand/models/mcl_meshhand.b3d b/mods/PLAYER/mcl_meshhand/models/mcl_meshhand.b3d new file mode 100644 index 0000000000000000000000000000000000000000..a38124c60799c4b19b9260a94c712be38bfd7628 GIT binary patch literal 1023 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 z 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

lw zEG$7&1?GZX1ZD+8#tmT(0E;nzNr=VZXmkND!v%|mq~;dn7bWJvG~{OHp&5~znTM_@ z8|qzf)=SDP0n>@4DVg~YGeHwoU~VvI+CCK_?UY#pu^TK4??gj*Am2kA3FbQ?n;Q={ zn4v5)B{e@jDYFEe(ZI^kc)6JsNOGXvHF>Ey#S9Ropm2n+AO$N!dVT?<30{;8nv;kJ zYXz|rE8uL<0>soJh_c+$iueN1Hl7j$AFKyvEqJa8k{9y9E!?~mhLrq*f}GSMQ04&j zY(cpXR2PD)b?|UwNjzvIwg|N3K?}Ur55WfwU{!%TP#{)nMRE>k2}Dj}C8%M+07}m;%mo)^1v!bJ4KRh!vKqEblOeUD2pZhU znR%)CdEjAK1}M8cBeNtG#!1RaOwNXJ(i3xY6B)7!j3DYk3|OS4mV*W?AzKBNv$nv(+>-%rhBr~tKE7%IUesE-6@fGWPsB(RaG@!%=})N)RR z6t^jvxv6;|e}k42LY5R&Wafg)`+~$2Glq)dC||5;|wNHTvAk;44$>l zPX;aD2QBo3EH4CW%r8!4NGk#j8bU$?v>3QJF)g*Ek^wrX3vvdi=mrfmfto0Z$t9re z0J%vyrA6Sv8&m{WBo-GiRDuYoVe#OhwDiQ%V$e8BPJU5*Zh8qMRTbnHmp~F4hy~iR z0HTW%OBhlz!DTyhKoup(Ct!V`LI%tLE%JsA8iV--i7AE*1)0gvUL$ymAE;8#Er