--[[ Everness. Never ending discovery in Everness mapgen. Copyright (C) 2025 SaKeL mod_support_mcl total rewrite 2025 by Gunnar Zötl This library 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 library 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. --]] local S = core.get_translator(core.get_current_modname()) local function register_alias(name, convert_to) if not core.registered_aliases[name] and rawget(core.registered_items, name) == nil then core.register_alias(name, convert_to) core.registered_aliases[name] = convert_to end end -- Test whether we are in VoxeLibre or Mineclonia local isVL = core.get_modpath('vl_trusted') ~= nil or core.get_modpath('vl_core') ~= nil local mod_emu = {} --- -- Fences / Gates --- mod_emu.default = { register_fence = function(name, def) local sname = string.match(name, ':(.+)$') local fence_id, gate_id, open_gate_id = mcl_fences.register_fence_and_fence_gate( sname, def.description, def.description .. ' Gate', def.texture, { handy = 1, axey = 1, flammable = 2, fence_wood = 1, fire_encouragement = 5, fire_flammability = 20 }, 2, 15, { 'group:fence_wood' }, def.sounds ) if fence_id == name then register_alias('mcl_fences:' .. sname, name) register_alias('mcl_fences:' .. sname .. '_gate', name .. '_gate') register_alias('mcl_fences:' .. sname .. '_gate_open', name .. '_gate_open') else register_alias(name, fence_id) if gate_id then register_alias(name .. '_gate', gate_id) end if open_gate_id then register_alias(name .. '_gate_open', open_gate_id) end end end, register_fence_rail = function(name, def) local sname = string.match(name, ':(.+)$') local fence_id = mcl_fences.register_fence_and_fence_gate( sname, def.description, def.description .. ' Gate', def.texture, { handy = 1, axey = 1, flammable = 2, fence_wood = 1, fire_encouragement = 5, fire_flammability = 20 }, 2, 15, { 'group:fence_wood' }, def.sounds ) if fence_id == name then register_alias('mcl_fences:' .. sname, name) else register_alias(name, fence_id) end end, register_mesepost = function(name, def) -- Not applicable in MCL end, } --- -- Walls --- mod_emu.walls = { register = function(name, desc, tex, mat, snd) mcl_walls.register_wall(name, desc, mat, tex, nil, nil, snd) end } --- -- Stairs and Slabs --- mod_emu.stairs = { register_stair_and_slab = function(base, mat, stat, tex, stairdesc, slabdesc, snd, bool) local groups = {} if stat.cracky then -- In MTG: cracky 3 is softest, 1 is hardest. -- In MCL: pickaxey 1 is wood, 2 is stone/iron, 3 is diamond. groups.pickaxey = stat.cracky == 1 and 2 or 1 groups.material_stone = 1 groups.building_block = 1 elseif stat.choppy then groups.axey = 1 groups.handy = 1 groups.wood = 1 groups.material_wood = 1 groups.building_block = 1 groups.flammable = stat.flammable or 2 groups.fire_encouragement = 5 groups.fire_flammability = 20 end mcl_stairs.register_stair_and_slab( base, mat, groups, tex, stairdesc, slabdesc, snd, 6, 2, 'Double ' .. slabdesc, nil ) register_alias('stairs:stair_' .. base, 'mcl_stairs:stair_' .. base) register_alias('stairs:stair_inner_' .. base, 'mcl_stairs:stair_' .. base .. '_inner') register_alias('stairs:stair_outer_' .. base, 'mcl_stairs:stair_' .. base .. '_outer') register_alias('stairs:slab_' .. base, 'mcl_stairs:slab_' .. base) end } --- -- Doors & Trapdoors --- mod_emu.doors = { register = function(name, def) local sname = string.match(name, ':(.+)$') local modpath = core.get_modpath(core.get_current_modname()) local utex = io.open(modpath .. '/textures/everness_mcl_doors_' .. sname .. '_lower.png', 'r') if not utex then core.log('warning', '[everness] mod_support_mcl not registering door type ' .. name .. ' because of missing textures') return end utex:close() local newdef = { description = def.description, inventory_image = def.inventory_image, groups = { handy = 1, axey = 1, material_wood = 1, flammable = -1 }, sounds = def.sounds, sound_open = def.sound_open, sound_close = def.sound_close, _mcl_hardness = 3, _mcl_blast_resistance = 3, } if isVL then newdef.tiles_bottom = 'everness_mcl_doors_' .. sname .. '_lower.png' newdef.tiles_top = 'everness_mcl_doors_' .. sname .. '_upper.png' else newdef.tiles_bottom = { 'everness_mcl_doors_' .. sname .. '_lower.png^[transformFX', 'everness_mcl_doors_' .. sname .. '_lower_side.png' } newdef.tiles_top = { 'everness_mcl_doors_' .. sname .. '_upper.png^[transformFX', 'everness_mcl_doors_' .. sname .. '_upper_side.png' } end mcl_doors:register_door(name, newdef) -- Register aliases for MTG door nodes (_a bottom, _b top) used in schematics register_alias(name .. '_a', name .. '_b_1') register_alias(name .. '_b', name .. '_t_1') if def.recipe then core.register_craft({ output = name .. ' 3', recipe = def.recipe }) end end, register_trapdoor = function(name, def) mcl_doors:register_trapdoor(name, { description = def.description, inventory_image = def.inventory_image, tile_front = def.tile_front, tile_side = def.tile_side, wield_image = def.wield_image, groups = { handy = 1, axey = 1, material_wood = 1, flammable = -1 }, sounds = def.sounds, sound_open = def.sound_open, sound_close = def.sound_close, _mcl_hardness = 3, _mcl_blast_resistance = 3, }) if def.recipe then core.register_craft({ output = name, recipe = def.recipe }) end end, register_fencegate = function(name, def) local gate_name_map = { ['everness:gate_bamboo'] = 'everness:fence_bamboo_wood_gate', ['everness:gate_crystal_wood'] = 'everness:fence_crystal_wood_gate', ['everness:gate_palm_wood'] = 'everness:fence_palm_wood_gate', ['everness:gate_coral_wood'] = 'everness:fence_coral_wood_gate', ['everness:gate_lava_tree_wood'] = 'everness:fence_lava_tree_wood_gate', } if gate_name_map[name] then register_alias(name, gate_name_map[name]) register_alias(name .. '_open', gate_name_map[name] .. '_open') end end } --- -- Buckets --- if core.global_exists('mcl_buckets') and mcl_buckets.register_liquid then mcl_buckets.register_liquid({ source_place = 'everness:mineral_water_source', source_take = { 'everness:mineral_water_source' }, bucketname = 'everness:bucket_mineral_water', inventory_image = 'everness_mcl_bucket_mineral_water.png', name = S('Mineral') .. ' ' .. S('Water') .. ' ' .. S('Bucket'), longdesc = S('A bucket can be used to collect and release liquids. This one is filled with mineral water.'), usagehelp = S('Place it to empty the bucket and create a mineral water source.'), tt_help = S('Places a mineral water source'), groups = { water_bucket = 1 }, }) end --- -- Panes --- if not (core.global_exists('xpanes') and xpanes.register_pane) then mod_emu.xpanes = { register_pane = function(name, def) if core.global_exists('mcl_panes') and mcl_panes.register_pane then local mcl_def = table.copy(def) if mcl_def.textures then local t1 = mcl_def.textures[1] or '' local t2 = (mcl_def.textures[2] and mcl_def.textures[2] ~= '') and mcl_def.textures[2] or t1 local t3 = mcl_def.textures[3] or t1 mcl_def.textures = { t1, t2, t3 } end mcl_panes.register_pane(name, mcl_def) end register_alias('xpanes:' .. name, 'mcl_panes:' .. name) register_alias('xpanes:' .. name .. '_flat', 'mcl_panes:' .. name .. '_flat') end } else register_alias('mcl_panes:cursed_bar', 'xpanes:cursed_bar') register_alias('mcl_panes:cursed_bar_flat', 'xpanes:cursed_bar_flat') register_alias('mcl_panes:pyrite_pane', 'xpanes:pyrite_pane') register_alias('mcl_panes:pyrite_pane_flat', 'xpanes:pyrite_pane_flat') end --- -- Scoped Execution Runner --- local function load_derivative_nodes() local prev_default = rawget(_G, 'default') local prev_walls = rawget(_G, 'walls') local prev_stairs = rawget(_G, 'stairs') local prev_doors = rawget(_G, 'doors') local prev_xpanes = rawget(_G, 'xpanes') rawset(_G, 'default', mod_emu.default) rawset(_G, 'walls', mod_emu.walls) rawset(_G, 'stairs', mod_emu.stairs) rawset(_G, 'doors', mod_emu.doors) if mod_emu.xpanes then rawset(_G, 'xpanes', mod_emu.xpanes) end local path = core.get_modpath('everness') dofile(path .. '/panes.lua') dofile(path .. '/stairs.lua') dofile(path .. '/walls.lua') dofile(path .. '/fences.lua') dofile(path .. '/doors.lua') rawset(_G, 'default', prev_default) rawset(_G, 'walls', prev_walls) rawset(_G, 'stairs', prev_stairs) rawset(_G, 'doors', prev_doors) if mod_emu.xpanes then rawset(_G, 'xpanes', prev_xpanes) end end load_derivative_nodes() --- -- Trees & Leafdecay Compatibility with Mineclonia --- -- In Mineclonia, mcl_trees overrides on_construct and after_destruct on all nodes -- with group:tree ~= 0 in an on_mods_loaded callback. -- This triggers heavy VoxelManip updates (reading/writing an 8-block radius) and -- massive 15,625-node area searches whenever an Everness tree log is dug or placed. -- Because Everness leaves do not use Mineclonia's distance param2 leafdecay, -- mcl_trees's callbacks do no useful work on Everness trees while causing severe lag, -- massive Lua memory allocations (~500 KB per log), and server OOM crashes on large trees. -- -- Since everness is loaded after mcl_trees, this on_mods_loaded callback executes after -- mcl_trees's on_mods_loaded, cleanly restoring Everness's own lightweight leafdecay. core.register_on_mods_loaded(function() if not core.get_modpath('mcl_trees') then return end if Everness.leafdecay_trunk_defs then for trunk, def in pairs(Everness.leafdecay_trunk_defs) do if core.registered_nodes[trunk] then core.registered_nodes[trunk].on_construct = nil if core.registered_items[trunk] then core.registered_items[trunk].on_construct = nil end core.override_item(trunk, { after_destruct = function(pos, oldnode) Everness.leafdecay_after_destruct(pos, oldnode, def) end, }) end end end -- Also handle other Everness trunks that do not have leafdecay (e.g. hollow_tree) if core.registered_nodes['everness:hollow_tree'] then core.registered_nodes['everness:hollow_tree'].on_construct = nil core.registered_nodes['everness:hollow_tree'].after_destruct = nil if core.registered_items['everness:hollow_tree'] then core.registered_items['everness:hollow_tree'].on_construct = nil core.registered_items['everness:hollow_tree'].after_destruct = nil end end end)