diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb74c5a35..88370f62e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,45 +7,45 @@ But first, some things to note: MineClone 2's development target is to make a free software clone of Minecraft, ***version 1.11***, ***PC edition***. -MineClone 2 is maintained by one person. Namely, Wuzzy. You can find me, -Wuzzy, in the Minetest forums (forums.minetest.net), in IRC in the #minetest +MineClone 2 is maintained by two persons. Namely, kay27 and EliasFleckenstein. You can find us +in the Minetest forums (forums.minetest.net), in IRC in the #minetest channel on irc.freenode.net. And finally, you can send e-mails to -. + or . -There is **no** guarantee I will accept anything from anybody. +There is **no** guarantee we will accept anything from anybody. -By sending me patches or asking me to include your changes in this game, +By sending us patches or asking us to include your changes in this game, you agree that they fall under the terms of the LGPLv2.1, which basically means they will become part of a free software. ## The suggested workflow -I don't **dictate** your workflow, but in order to work with me in an efficient -way, you can follow my suggestions. +We don't **dictate** your workflow, but in order to work with us in an efficient +way, you can follow these suggestions: For small and medium changes: * Fork the repository * Do your change in a new branch * Upload the repository somewhere where it can be accessed from the Internet and - notify me + notify us -For small changes, sending me a patch is also good. +For small changes, sending us a patch is also good. -For big changes: Same as above, but consider notifying me first to avoid +For big changes: Same as above, but consider notifying us first to avoid duplicate work and possible tears of rejection. ;-) -For people that I trust, I might give them direct commit access to this +For trusted people, we might give them direct commit access to this repository. In this case, you obviously don't need to fork, but you still -need to show your contributions align with the project goals. I still -reserve the right to revert everything that I don't like. -For bigger changes, I strongly recommend to use feature branches and +need to show your contributions align with the project goals. We still +reserve the right to revert everything that we don't like. +For bigger changes, we strongly recommend to use feature branches and discuss with me first. Contributors will be credited in `README.md`. ## Quality remarks -Again: There is ***no*** guarantee I will accept anything from anybody. -But I will gladly take in code from others when I feel it saves me work +Again: There is ***no*** guarantee we will accept anything from anybody. +But we will gladly take in code from others when we feel it saves us work in the long run. ### Inclusion criteria @@ -79,9 +79,18 @@ Depending on what you add, the chances for inclusion vary: ## Reporting bugs Report all bugs and missing Minecraft features here: - + ## Direct discussion We have an IRC channel! Join us on #mineclone2 in freenode.net. + +## Creating releases +* Launch MineClone2 to make sure it still runs +* Update the version number in README.md +* Use `git tag ` to tag the latest commit with the version number +* Push to repo (don't forget `--tags`!) +* Update ContentDB (https://content.minetest.net/packages/Wuzzy/mineclone2/) +* Update first post in forum thread (https://forum.minetest.net/viewtopic.php?f=50&t=16407) +* Post release announcement and changelog in forums diff --git a/GROUPS.md b/GROUPS.md index 1ac531b8a..f94b04979 100644 --- a/GROUPS.md +++ b/GROUPS.md @@ -70,6 +70,7 @@ Please read to learn how digging times * `coral_fan=X`: Coral fan (1 = alive, 2 = dead) * `coral_block=X`: Coral block (1 = alive, 2 = dead) * `coral_species=X`: Specifies the species of a coral; equal X means equal species +* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching #### Footnotes @@ -96,6 +97,8 @@ Please read to learn how digging times * `carpet=1:` (Wool) carpet * `stick=1`: Stick * `water_bucket=1`: Bucket containing a liquid of group “water” +* `enchantability=X`: How good the enchantments are the item gets (1 equals book) +* `enchanted=1`: The item is already enchanted, meaning that it can't be enchanted using an enchanting table ### Material groups @@ -197,6 +200,7 @@ These groups are used mostly for informational purposes * `building_block=1`: Block is a building block * `deco_block=1`: Block is a decorational block + ## Fake item groups These groups put similar items together which should all be treated by the gameplay or the GUI as a single item. You should not add custom items to these groups for no good reason, this is likely to cause a ton of conflicts. diff --git a/MISSING_ENGINE_FEATURES.md b/MISSING_ENGINE_FEATURES.md index 16f7e1572..fddb89f6c 100644 --- a/MISSING_ENGINE_FEATURES.md +++ b/MISSING_ENGINE_FEATURES.md @@ -20,7 +20,6 @@ For these features, no easy Lua workaround could be found. ## Interface - Inventory: Hold down right mouse button while holding an item stack to drop items into the slots as you move the mouse. Makes crafting MUCH faster - Sneak+Leftclick on crafting output crafts as many items as possible and immediately puts it into the player inventory ([issue 5211](https://github.com/minetest/minetest/issues/5211)) -- Sneak+click on inventory slot should be able to put items into additional “fallback inventories” if the first inventory is full. Required for large chests - Sneak+click puts items in different inventories depending on the item type (maybe group-based)? Required for sneak-clicking to armor slots ## Workaround theoretically possible @@ -35,6 +34,7 @@ For these features, a workaround (or hack ;-)) by using Lua is theoretically pos - Set frequency in which players lose breath. 2 seconds are hardcoded in Minetest, in Minecraft it's 1 second - Set damage frequency of `damage_per_second`. In Minecraft many things damage players every half-second rather than every second - Possible to damage players directly when they are with the head inside. This allows to add Minecraft-like suffocation +- Sneak+click on inventory slot should be able to put items into additional “fallback inventories” if the first inventory is full. Useful for large chests #### Nice-to-haye - Utility function to rotate pillar-like nodes, requiring only 3 possible orientations (X, Y, Z). Basically this is `minetest.rotate_node` but with less orientations; the purpur pillar would mess up if a mirrored rotation would be possible. This is already implemented in MCL2, See `mcl_util` for more infos diff --git a/README.md b/README.md index 83bd4d861..12f5658e2 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # MineClone 2 An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils. -Developed by Wuzzy and contributors. Not developed or endorsed by Mojang AB. +Developed by many people. Not developed or endorsed by Mojang AB. -Version: 0.68.0 +Version: 0.70.0 ### Gameplay You start in a randomly-generated world made entirely of cubes. You can explore @@ -14,9 +14,10 @@ Or you can play in “creative mode” in which you can build almost anything in #### Gameplay summary -* Sandbox-style gameplay, no goals (for now) +* Sandbox-style gameplay, no goals * Survive: Fight against hostile monsters and hunger * Mine for ores and other treasures +* Magic: Gain experience and enchant your tools * Use the collected blocks to create great buildings, your imagination is the limit * Collect flowers (and other dye sources) and colorize your world * Find some seeds and start farming @@ -102,7 +103,7 @@ big bugs (such as “missing node” errors or even crashes). The following main features are available: * Tools, weapons -* Armor (unbalanced) +* Armor * Crafting system: 2×2 grid, crafting table (3×3 grid), furnace, including a crafting guide * Chests, large chests, ender chests, shulker boxes * Furnaces, hoppers @@ -117,6 +118,8 @@ The following main features are available: * Redstone circuits (partially) * Minecarts (partial) * Status effects (partial) +* Experience +* Enchanting * Brewing, potions, tipped arrow (partial) * Boats * Fire @@ -142,12 +145,9 @@ The following main features are available: The following features are incomplete: * Generated structures (especially villages) -* NPCs * Some monsters and animals * Redstone-related things * The End -* Enchanting -* Experience * Special minecarts * A couple of non-trivial blocks and items @@ -182,7 +182,7 @@ Technical differences from Minecraft: ## Reporting bugs Please report all bugs and missing Minecraft features here: - + ## Other readme files @@ -195,7 +195,7 @@ Please report all bugs and missing Minecraft features here: There are so many people to list (sorry). Check out the respective mod directories for details. This section is only a rough overview of the core authors of this game. ### Coding -* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main programmer of most mods +* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main programmer of most mods (retired) * davedevils: Creator of MineClone on which MineClone 2 is based on * [ex-bart](https://github.com/ex-bart): Redstone comparators * [Rootyjr](https://github.com/Rootyjr): Fishing rod and bugfixes @@ -203,7 +203,8 @@ There are so many people to list (sorry). Check out the respective mod directori * [ryvnf](https://github.com/ryvnf): Explosion mechanics * MysticTempest: Bugfixes * [bzoss](https://github.com/bzoss): Status effects, potions, brewing stand -* kay27 : Experience system, bugfixes, optimizations +* kay27 : Experience system, bugfixes, optimizations (Current maintainer) +* [EliasFleckenstein03](https://github.com/EliasFleckenstein03): End crystals, enchanting, burning mobs / players, animated chests, bugfixes (Current maintainer) * 2mac: Fix bug with powered rail * Lots of other people: TO BE WRITTEN (see mod directories for details) @@ -250,6 +251,7 @@ Various sources. See the respective mod directories for details. ### Special thanks * davedevils for starting MineClone, the original version of this game +* Wuzzy for starting and maintaining MineClone2 for several years * celeron55 for creating Minetest * Minetest's modding community for providing a huge selection of mods, some of which ended up in MineClone 2 * Jordach for the jukebox music compilation from Big Freaking Dig @@ -261,7 +263,6 @@ Various sources. See the respective mod directories for details. ## Info for programmers You find interesting and useful infos in `API.md`. -This project is currently mostly a one-person project. ## Legal information This is a fan game, not developed or endorsed by Mojang AB. @@ -270,7 +271,7 @@ Copying is an act of love. Please copy and share! <3 Here's the detailed legalese for those who need it: ### License of source code -MineClone 2 (by Wuzzy, davedevils and countless others) +MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others) is an imitation of Minecraft. MineClone 2 is free software: you can redistribute it and/or modify diff --git a/mods/CORE/mcl_explosions/init.lua b/mods/CORE/mcl_explosions/init.lua index 293fdba83..b23489861 100644 --- a/mods/CORE/mcl_explosions/init.lua +++ b/mods/CORE/mcl_explosions/init.lua @@ -32,6 +32,10 @@ local STEP_LENGTH = 0.3 -- How many rays to compute entity exposure to explosion local N_EXPOSURE_RAYS = 16 +-- Nodes having a blast resistance of this value or higher are treated as +-- indestructible +local INDESTRUCT_BLASTRES = 1000000 + minetest.register_on_mods_loaded(function() -- Store blast resistance values by content ids to improve performance. for name, def in pairs(minetest.registered_nodes) do @@ -135,14 +139,21 @@ end -- strength - The strength of each ray -- raydirs - The directions for each ray -- radius - The maximum distance each ray will go --- drop_chance - The chance that destroyed nodes will drop their items --- fire - If true, 1/3 of destroyed nodes become fire +-- info - Table containing information about explosion -- puncher - object that punches other objects (optional) -- +-- Values in info: +-- drop_chance - The chance that destroyed nodes will drop their items +-- fire - If true, 1/3 nodes become fire +-- griefing - If true, the explosion will destroy nodes (default: true) +-- max_blast_resistance - The explosion will treat all non-indestructible nodes +-- as having a blast resistance of no more than this +-- value +-- -- Note that this function has been optimized, it contains code which has been -- inlined to avoid function calls and unnecessary table creation. This was -- measured to give a significant performance increase. -local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire, puncher, creative_enabled) +local function trace_explode(pos, strength, raydirs, radius, info, puncher) local vm = minetest.get_voxel_manip() local emin, emax = vm:read_from_map(vector.subtract(pos, radius), @@ -164,39 +175,49 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire, local data = vm:get_data() local destroy = {} + local drop_chance = info.drop_chance + local fire = info.fire + local max_blast_resistance = info.max_blast_resistance + -- Trace rays for environment destruction - for i = 1, #raydirs do - local rpos_x = pos.x - local rpos_y = pos.y - local rpos_z = pos.z - local rdir_x = raydirs[i].x - local rdir_y = raydirs[i].y - local rdir_z = raydirs[i].z - local rstr = (0.7 + math.random() * 0.6) * strength + if info.griefing then + for i = 1, #raydirs do + local rpos_x = pos.x + local rpos_y = pos.y + local rpos_z = pos.z + local rdir_x = raydirs[i].x + local rdir_y = raydirs[i].y + local rdir_z = raydirs[i].z + local rstr = (0.7 + math.random() * 0.6) * strength - for r = 0, math.ceil(radius * (1.0 / STEP_LENGTH)) do - local npos_x = math.floor(rpos_x + 0.5) - local npos_y = math.floor(rpos_y + 0.5) - local npos_z = math.floor(rpos_z + 0.5) - local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride + - npos_x - emin_x + 1 + for r = 0, math.ceil(radius * (1.0 / STEP_LENGTH)) do + local npos_x = math.floor(rpos_x + 0.5) + local npos_y = math.floor(rpos_y + 0.5) + local npos_z = math.floor(rpos_z + 0.5) + local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride + + npos_x - emin_x + 1 - local cid = data[idx] - local br = node_blastres[cid] - local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z}) + local cid = data[idx] + local br = node_blastres[cid] + if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then + br = max_blast_resistance + end - rpos_x = rpos_x + STEP_LENGTH * rdir_x - rpos_y = rpos_y + STEP_LENGTH * rdir_y - rpos_z = rpos_z + STEP_LENGTH * rdir_z + local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z}) - rstr = rstr - 0.75 * STEP_LENGTH - (br + 0.3) * STEP_LENGTH + rpos_x = rpos_x + STEP_LENGTH * rdir_x + rpos_y = rpos_y + STEP_LENGTH * rdir_y + rpos_z = rpos_z + STEP_LENGTH * rdir_z - if rstr <= 0 then - break - end + rstr = rstr - 0.75 * STEP_LENGTH - (br + 0.3) * STEP_LENGTH - if cid ~= minetest.CONTENT_AIR and not minetest.is_protected({x = npos_x, y = npos_y, z = npos_z}, "") then - destroy[hash] = idx + if rstr <= 0 then + break + end + + if cid ~= minetest.CONTENT_AIR and not minetest.is_protected({x = npos_x, y = npos_y, z = npos_z}, "") then + destroy[hash] = idx + end end end end @@ -284,8 +305,18 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire, impact = 0 end local damage = math.floor((impact * impact + impact) * 7 * strength + 1) + local source = puncher or obj + + local sleep_formspec_doesnt_close_mt53 = false if obj:is_player() then local name = obj:get_player_name() + if mcl_beds then + local meta = obj:get_meta() + if meta:get_string("mcl_beds:sleeping") == "true" then + minetest.close_formspec(name, "") -- ABSOLUTELY NECESSARY FOR MT5.3 -- TODO: REMOVE THIS IN THE FUTURE + sleep_formspec_doesnt_close_mt53 = true + end + end if mod_death_messages then mcl_death_messages.player_damage(obj, S("@1 was caught in an explosion.", name)) end @@ -293,17 +324,21 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire, armor.last_damage_types[name] = "explosion" end end - local source = puncher - if not source then - source = obj - end - obj:punch(source, 10, { damage_groups = { full_punch_interval = 1, - fleshy = damage, knockback = impact * 20.0 } }, punch_dir) - if obj:is_player() then - obj:add_player_velocity(vector.multiply(punch_dir, impact * 20)) - elseif ent.tnt_knockback then - obj:add_velocity(vector.multiply(punch_dir, impact * 20)) + if sleep_formspec_doesnt_close_mt53 then + minetest.after(0.3, function(obj, damage, impact, punch_dir) -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE + if not obj then return end + obj:punch(obj, 10, { damage_groups = { full_punch_interval = 1, fleshy = damage, knockback = impact * 20.0 } }, punch_dir) + obj:add_player_velocity(vector.multiply(punch_dir, impact * 20)) + end, obj, damage, impact, vector.new(punch_dir)) + else + obj:punch(source, 10, { damage_groups = { full_punch_interval = 1, fleshy = damage, knockback = impact * 20.0 } }, punch_dir) + + if obj:is_player() then + obj:add_player_velocity(vector.multiply(punch_dir, impact * 20)) + elseif ent.tnt_knockback then + obj:add_velocity(vector.multiply(punch_dir, impact * 20)) + end end end end @@ -313,14 +348,14 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire, -- Remove destroyed blocks and drop items for hash, idx in pairs(destroy) do - local do_drop = not creative_enabled and math.random() <= drop_chance + local do_drop = math.random() <= drop_chance local on_blast = node_on_blast[data[idx]] local remove = true if do_drop or on_blast ~= nil then local npos = minetest.get_position_from_hash(hash) if on_blast ~= nil then - on_blast(npos, 1.0) + on_blast(npos, 1.0, do_drop) remove = false else local name = minetest.get_name_from_content_id(data[idx]) @@ -363,7 +398,6 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire, -- Log explosion minetest.log('action', 'Explosion at ' .. minetest.pos_to_string(pos) .. ' with strength ' .. strength .. ' and radius ' .. radius) - end -- Create an explosion with strength at pos. @@ -371,16 +405,24 @@ end -- Parameters: -- pos - The position where the explosion originates from -- strength - The blast strength of the explosion (a TNT explosion uses 4) --- info - Table containing information about explosion. +-- info - Table containing information about explosion -- puncher - object that is reported as source of punches/damage (optional) -- -- Values in info: -- drop_chance - If specified becomes the drop chance of all nodes in the --- explosion (defaults to 1.0 / strength) --- no_sound - If true then the explosion will not play a sound --- no_particle - If true then the explosion will not create particles +-- explosion (default: 1.0 / strength) +-- max_blast_resistance - If specified the explosion will treat all +-- non-indestructible nodes as having a blast resistance +-- of no more than this value +-- sound - If true, the explosion will play a sound (default: true) +-- particles - If true, the explosion will create particles (default: true) -- fire - If true, 1/3 nodes become fire (default: false) +-- griefing - If true, the explosion will destroy nodes (default: true) function mcl_explosions.explode(pos, strength, info, puncher) + if info == nil then + info = {} + end + -- The maximum blast radius (in the air) local radius = math.ceil(1.3 * strength / (0.3 * 0.75) * 0.3) @@ -389,13 +431,31 @@ function mcl_explosions.explode(pos, strength, info, puncher) end local shape = sphere_shapes[radius] - local creative_enabled = minetest.is_creative_enabled("") - trace_explode(pos, strength, shape, radius, (info and info.drop_chance) or 1 / strength, info.fire == true, puncher, creative_enabled) + -- Default values + if info.drop_chance == nil then info.drop_chance = 1 / strength end + if info.particles == nil then info.particles = true end + if info.sound == nil then info.sound = true end + if info.fire == nil then info.fire = false end + if info.griefing == nil then info.griefing = true end + if info.max_blast_resistance == nil then + info.max_blast_resistance = INDESTRUCT_BLASTRES + end - if not (info and info.no_particle) then + -- For backwards compatibility + if info.no_particle then info.particles = false end + if info.no_sound then info.sound = false end + + -- Dont do drops in creative mode + if minetest.is_creative_enabled("") then + info.drop_chance = 0 + end + + trace_explode(pos, strength, shape, radius, info, puncher) + + if info.particles then add_particles(pos, radius) end - if not (info and info.no_sound) then + if info.sound then minetest.sound_play("tnt_explode", { pos = pos, gain = 1.0, max_hear_distance = strength * 16 diff --git a/mods/CORE/mcl_init/init.lua b/mods/CORE/mcl_init/init.lua index a1346f50b..ebbfd5591 100644 --- a/mods/CORE/mcl_init/init.lua +++ b/mods/CORE/mcl_init/init.lua @@ -25,6 +25,7 @@ mcl_vars.inventory_header = "" local mg_name = minetest.get_mapgen_setting("mg_name") local minecraft_height_limit = 256 local superflat = mg_name == "flat" and minetest.get_mapgen_setting("mcl_superflat_classic") == "true" +local singlenode = mg_name == "singlenode" -- Calculate mapgen_edge_min/mapgen_edge_max mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5) @@ -45,7 +46,7 @@ local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / chunk_size_in_ mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes -if not superflat then +if not superflat and not singlenode then -- Normal mode --[[ Realm stacking (h is for height) - Overworld (h>=256) @@ -66,6 +67,14 @@ if not superflat then mcl_vars.mg_lava = true mcl_vars.mg_bedrock_is_rough = true +elseif singlenode then + mcl_vars.mg_overworld_min = -66 + mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit + mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min + mcl_vars.mg_bedrock_overworld_max = mcl_vars.mg_bedrock_overworld_min + mcl_vars.mg_lava = false + mcl_vars.mg_lava_overworld_max = mcl_vars.mg_overworld_min + mcl_vars.mg_bedrock_is_rough = false else -- Classic superflat local ground = minetest.get_mapgen_setting("mgflat_ground_level") @@ -128,3 +137,4 @@ minetest.craftitemdef_default.stack_max = 64 -- Set random seed for all other mods (Remember to make sure no other mod calls this function) math.randomseed(os.time()) + diff --git a/mods/CORE/mcl_loot/init.lua b/mods/CORE/mcl_loot/init.lua index 35c72539d..e3db73be1 100644 --- a/mods/CORE/mcl_loot/init.lua +++ b/mods/CORE/mcl_loot/init.lua @@ -11,12 +11,15 @@ Parameters: stacks_max = 3, -- Maximum number of item stacks to get. Default: 1 items = { -- Table of possible loot items. This function selects between stacks_min and stacks_max of these. { + weight = 5, -- Likelihood of this item being selected (see below). Optional (default: 1) + + itemstack = ItemStack("example:item1"), -- Itemstack to select + -- OR itemstring = "example:item1", -- Which item to select amount_min = 1, -- Minimum size of itemstack. Must not be larger than 6553. Optional (default: 1) amount_max = 10, -- Maximum size of item stack. Must not be larger than item definition's stack_max or 6553. Optional (default: 1) wear_min = 1, -- Minimum wear value. Must be at least 1. Optional (default: no wear) wear_max = 1, -- Maxiumum wear value. Must be at least 1. Optional (default: no wear) - weight = 5, -- Likelihood of this item being selected (see below). Optional (default: 1) }, { -- more tables like above, one table per item stack } } @@ -56,24 +59,29 @@ function mcl_loot.get_loot(loot_definitions, pr) end if item then local itemstring = item.itemstring - if item.amount_min and item.amount_max then - itemstring = itemstring .. " " .. pr:next(item.amount_min, item.amount_max) - end - if item.wear_min and item.wear_max then - -- Sadly, PseudoRandom only allows very narrow ranges, so we set wear in steps of 10 - local wear_min = math.floor(item.wear_min / 10) - local wear_max = math.floor(item.wear_max / 10) - local wear = pr:next(wear_min, wear_max) * 10 - - if not item.amount_min and not item.amount_max then - itemstring = itemstring .. " 1" + local itemstack = item.itemstack + if itemstring then + if item.amount_min and item.amount_max then + itemstring = itemstring .. " " .. pr:next(item.amount_min, item.amount_max) end + if item.wear_min and item.wear_max then + -- Sadly, PseudoRandom only allows very narrow ranges, so we set wear in steps of 10 + local wear_min = math.floor(item.wear_min / 10) + local wear_max = math.floor(item.wear_max / 10) + local wear = pr:next(wear_min, wear_max) * 10 - itemstring = itemstring .. " " .. tostring(wear) + if not item.amount_min and not item.amount_max then + itemstring = itemstring .. " 1" + end + + itemstring = itemstring .. " " .. tostring(wear) + end + table.insert(items, itemstring) + elseif itemstack then + table.insert(items, itemstack) + else + minetest.log("error", "[mcl_loot] INTERNAL ERROR! Failed to select random loot item!") end - table.insert(items, itemstring) - else - minetest.log("error", "[mcl_loot] INTERNAL ERROR! Failed to select random loot item!") end end diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 7e8513365..6c63c21ab 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -395,4 +395,13 @@ function mcl_util.generate_on_place_plant_function(condition) end end - +-- adjust the y level of an object to the center of its collisionbox +-- used to get the origin position of entity explosions +function mcl_util.get_object_center(obj) + local collisionbox = obj:get_properties().collisionbox + local pos = obj:get_pos() + local ymin = collisionbox[2] + local ymax = collisionbox[5] + pos.y = pos.y + (ymax - ymin) / 2.0 + return pos +end diff --git a/mods/ENTITIES/mcl_boats/init.lua b/mods/ENTITIES/mcl_boats/init.lua index 9d0e45557..5f666709c 100644 --- a/mods/ENTITIES/mcl_boats/init.lua +++ b/mods/ENTITIES/mcl_boats/init.lua @@ -50,8 +50,10 @@ local boat = { mesh = "mcl_boats_boat.b3d", textures = {"mcl_boats_texture_oak_boat.png"}, visual_size = boat_visual_size, + hp_max = 4, _driver = nil, -- Attached driver (player) or nil if none + _passenger = nil, _v = 0, -- Speed _last_v = 0, -- Temporary speed variable _removed = false, -- If true, boat entity is considered removed (e.g. after punch) and should be ignored @@ -59,54 +61,81 @@ local boat = { _animation = 0, -- 0: not animated; 1: paddling forwards; -1: paddling forwards } +local function detach_player(player, change_pos) + player:set_detach() + player:set_properties({visual_size = {x=1, y=1}}) + mcl_player.player_attached[player:get_player_name()] = false + mcl_player.player_set_animation(player, "stand" , 30) + if change_pos then + player:set_pos(vector.add(player:get_pos(), vector.new(0, 0.2, 0))) + end +end + +local function check_object(obj) + return obj and (obj:is_player() or obj:get_luaentity()) and obj +end + +local function set_attach(boat) + boat._driver:set_attach(boat.object, "", + {x = 0, y = 0.42, z = -1}, {x = 0, y = 0, z = 0}) +end + +local function set_double_attach(boat) + boat._driver:set_attach(boat.object, "", + {x = 0, y = 0.42, z = 0.8}, {x = 0, y = 0, z = 0}) + boat._passenger:set_attach(boat.object, "", + {x = 0, y = 0.42, z = -2.2}, {x = 0, y = 0, z = 0}) +end + +minetest.register_on_respawnplayer(detach_player) + function boat.on_rightclick(self, clicker) - if not clicker or not clicker:is_player() then + if self._passenger or not clicker or clicker:get_attach() then return end local name = clicker:get_player_name() - if self._driver and clicker == self._driver then - self._driver = nil + --[[if attach and attach:get_luaentity() then + local luaentity = attach:get_luaentity() + if luaentity._driver then + luaentity._driver = nil + end clicker:set_detach() clicker:set_properties({visual_size = {x=1, y=1}}) - mcl_player.player_attached[name] = false - mcl_player.player_set_animation(clicker, "stand" , 30) - local pos = clicker:get_pos() - pos = {x = pos.x, y = pos.y + 0.2, z = pos.z} - clicker:set_pos(pos) - elseif not self._driver then - local attach = clicker:get_attach() - if attach and attach:get_luaentity() then - local luaentity = attach:get_luaentity() - if luaentity._driver then - luaentity._driver = nil - end - clicker:set_detach() - clicker:set_properties({visual_size = {x=1, y=1}}) + end--]] + if self._driver then + if self._driver:is_player() then + self._passenger = clicker + else + -- for later use: transport mobs in boats + self._passenger = self._driver + self._driver = clicker end + set_double_attach(self) + else self._driver = clicker - clicker:set_attach(self.object, "", - {x = 0, y = 0.42, z = -1}, {x = 0, y = 0, z = 0}) - clicker:set_properties({ visual_size = driver_visual_size }) - mcl_player.player_attached[name] = true - minetest.after(0.2, function(name) - local player = minetest.get_player_by_name(name) - if player then - mcl_player.player_set_animation(player, "sit" , 30) - end - end, name) - clicker:set_look_horizontal(self.object:get_yaw()) + set_attach(self) end + clicker:set_properties({ visual_size = driver_visual_size }) + mcl_player.player_attached[name] = true + minetest.after(0.2, function(name) + local player = minetest.get_player_by_name(name) + if player then + mcl_player.player_set_animation(player, "sit" , 30) + end + end, name) + clicker:set_look_horizontal(self.object:get_yaw()) + mcl_tmp_message.message(clicker, S("Sneak to dismount")) end function boat.on_activate(self, staticdata, dtime_s) - self.object:set_armor_groups({immortal = 1}) + --self.object:set_armor_groups({immortal = 1}) local data = minetest.deserialize(staticdata) if type(data) == "table" then self._v = data.v self._last_v = self._v self._itemstring = data.itemstring - self.object:set_properties({textures=data.textures}) + self.object:set_properties({textures = data.textures, damage_texture_modifier = ""}) end end @@ -120,32 +149,26 @@ function boat.get_staticdata(self) end -function boat.on_punch(self, puncher) - if not puncher or not puncher:is_player() or self._removed then - return - end - if self._driver and puncher == self._driver then - self._driver = nil - puncher:set_detach() - puncher:set_properties({visual_size = {x=1, y=1}}) - mcl_player.player_attached[puncher:get_player_name()] = false - end - if not self._driver then - self._removed = true - -- Drop boat as item on the ground after punching - if not minetest.is_creative_enabled(puncher:get_player_name()) then - minetest.add_item(self.object:get_pos(), self._itemstring) - else - local inv = puncher:get_inventory() - if not inv:contains_item("main", self._itemstring) then - inv:add_item("main", self._itemstring) - end +function boat.on_death(self, killer) + if killer and killer:is_player() and minetest.is_creative_enabled(killer:get_player_name()) then + local inv = killer:get_inventory() + if not inv:contains_item("main", self._itemstring) then + inv:add_item("main", self._itemstring) end - self.object:remove() + else + minetest.add_item(self.object:get_pos(), self._itemstring) end + if self._driver then + detach_player(self._driver) + end + if self._passenger then + detach_player(self._passenger) + end + self._driver = nil + self._passenger = nil end -function boat.on_step(self, dtime) +function boat.on_step(self, dtime, moveresult) self._v = get_v(self.object:get_velocity()) * get_sign(self._v) local on_water = true local in_water = false @@ -163,8 +186,43 @@ function boat.on_step(self, dtime) v_slowdown = 0.05 end + if moveresult and moveresult.collides then + for _, collision in ipairs(moveresult.collisions) do + local pos = collision.node_pos + if collision.type == "node" and minetest.get_node_group(minetest.get_node(pos).name, "dig_by_boat") > 0 then + minetest.dig_node(pos) + end + end + end + + local had_passenger = self._passenger + + self._driver = check_object(self._driver) + self._passenger = check_object(self._passenger) + + if self._passenger then + if not self._driver then + self._driver = self._passenger + self._passenger = nil + else + local ctrl = self._passenger:get_player_control() + if ctrl and ctrl.sneak then + detach_player(self._passenger, true) + self._passenger = nil + end + end + end + if self._driver then + if had_passenger and not self._passenger then + set_attach(self) + end local ctrl = self._driver:get_player_control() + if ctrl and ctrl.sneak then + detach_player(self._driver, true) + self._driver = nil + return + end local yaw = self.object:get_yaw() if ctrl.up then -- Forwards @@ -191,13 +249,13 @@ function boat.on_step(self, dtime) self._animation = 0 end end - if ctrl.left then + if ctrl and ctrl.left then if self._v < 0 then self.object:set_yaw(yaw - (1 + dtime) * 0.03 * v_factor) else self.object:set_yaw(yaw + (1 + dtime) * 0.03 * v_factor) end - elseif ctrl.right then + elseif ctrl and ctrl.right then if self._v < 0 then self.object:set_yaw(yaw + (1 + dtime) * 0.03 * v_factor) else diff --git a/mods/ENTITIES/mcl_burning/engine.lua b/mods/ENTITIES/mcl_burning/engine.lua new file mode 100644 index 000000000..57890dd2f --- /dev/null +++ b/mods/ENTITIES/mcl_burning/engine.lua @@ -0,0 +1,304 @@ +local S = minetest.get_translator("mcl_burning") + +function mcl_burning.get_default(datatype) + local default_table = {string = "", float = 0.0, int = 0, bool = false} + return default_table[datatype] +end + +function mcl_burning.get(obj, datatype, name) + local key + if obj:is_player() then + local meta = obj:get_meta() + return meta["get_" .. datatype](meta, "mcl_burning:" .. name) + else + local luaentity = obj:get_luaentity() + return luaentity and luaentity["mcl_burning_" .. name] or mcl_burning.get_default(datatype) + end +end + +function mcl_burning.set(obj, datatype, name, value) + if obj:is_player() then + local meta = obj:get_meta() + meta["set_" .. datatype](meta, "mcl_burning:" .. name, value or mcl_burning.get_default(datatype)) + else + local luaentity = obj:get_luaentity() + if mcl_burning.get_default(datatype) == value then + value = nil + end + luaentity["mcl_burning_" .. name] = value + end +end + +function mcl_burning.is_burning(obj) + return mcl_burning.get(obj, "float", "burn_time") > 0 +end + +function mcl_burning.is_affected_by_rain(obj) + return mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos()) +end + +function mcl_burning.get_collisionbox(obj, smaller) + local box = obj:get_properties().collisionbox + local minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6]) + if smaller then + local s_vec = vector.new(0.1, 0.1, 0.1) + minp = vector.add(minp, s_vec) + maxp = vector.subtract(maxp, s_vec) + end + return minp, maxp +end + +function mcl_burning.get_touching_nodes(obj, nodenames) + local pos = obj:get_pos() + local box = obj:get_properties().collisionbox + local minp, maxp = mcl_burning.get_collisionbox(obj, true) + local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames) + return nodes +end + +function mcl_burning.get_highest_group_value(obj, groupname) + local nodes = mcl_burning.get_touching_nodes(obj, "group:" .. groupname, true) + local highest_group_value = 0 + + for _, pos in pairs(nodes) do + local node = minetest.get_node(pos) + local group_value = minetest.get_item_group(node.name, groupname) + if group_value > highest_group_value then + highest_group_value = group_value + end + end + + return highest_group_value +end + +function mcl_burning.damage(obj) + local luaentity = obj:get_luaentity() + local health + + if luaentity then + health = luaentity.health + end + + local hp = health or obj:get_hp() + + if hp <= 0 then + return + end + + local do_damage = true + + if obj:is_player() then + if mcl_potions.player_has_effect(obj, "fire_proof") then + do_damage = false + else + local name = obj:get_player_name() + armor.last_damage_types[name] = "fire" + local deathmsg = S("@1 burned to death.", name) + local reason = mcl_burning.get(obj, "string", "reason") + if reason ~= "" then + deathmsg = S("@1 was burned by @2.", name, reason) + end + mcl_death_messages.player_damage(obj, deathmsg) + end + else + if luaentity.fire_damage_resistant then + do_damage = false + end + end + + if do_damage then + local damage = mcl_burning.get(obj, "float", "damage") + if damage == 0 then + damage = 1 + end + local new_hp = hp - damage + if health then + luaentity.health = new_hp + else + obj:set_hp(new_hp) + end + end +end + +function mcl_burning.set_on_fire(obj, burn_time, damage, reason) + local luaentity = obj:get_luaentity() + if luaentity and luaentity.fire_resistant then + return + end + + local old_burn_time = mcl_burning.get(obj, "float", "burn_time") + local max_fire_prot_lvl = 0 + + if obj:is_player() then + if minetest.is_creative_enabled(obj:get_player_name()) then + burn_time = burn_time / 100 + end + + local inv = obj:get_inventory() + + for i = 2, 5 do + local stack = inv:get_stack("armor", i) + + local fire_prot_lvl = mcl_enchanting.get_enchantment(stack, "fire_protection") + max_fire_prot_lvl = math.max(max_fire_prot_lvl, fire_prot_lvl) + end + end + + if max_fire_prot_lvl > 0 then + burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15) + end + + if old_burn_time <= burn_time then + local sound_id = mcl_burning.get(obj, "int", "sound_id") + if sound_id == 0 then + sound_id = minetest.sound_play("fire_fire", { + object = obj, + gain = 0.18, + max_hear_distance = 16, + loop = true, + }) + 1 + end + + local hud_id + if obj:is_player() then + hud_id = mcl_burning.get(obj, "int", "hud_id") + if hud_id == 0 then + hud_id = obj:hud_add({ + hud_elem_type = "image", + position = {x = 0.5, y = 0.5}, + scale = {x = -100, y = -100}, + text = "fire_basic_flame.png", + z_index = 1000, + }) + 1 + end + end + mcl_burning.set(obj, "float", "burn_time", burn_time) + mcl_burning.set(obj, "float", "damage", damage) + mcl_burning.set(obj, "string", "reason", reason) + mcl_burning.set(obj, "int", "hud_id", hud_id) + mcl_burning.set(obj, "int", "sound_id", sound_id) + + local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire") + local minp, maxp = mcl_burning.get_collisionbox(obj) + local obj_size = obj:get_properties().visual_size + + local vertical_grow_factor = 1.2 + local horizontal_grow_factor = 1.1 + local grow_vector = vector.new(horizontal_grow_factor, vertical_grow_factor, horizontal_grow_factor) + + local size = vector.subtract(maxp, minp) + size = vector.multiply(size, grow_vector) + size = vector.divide(size, obj_size) + local offset = vector.new(0, size.y * 10 / 2, 0) + + fire_entity:set_properties({visual_size = size}) + fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0}) + mcl_burning.update_animation_frame(obj, fire_entity, 0) + end +end + +function mcl_burning.extinguish(obj) + if mcl_burning.is_burning(obj) then + local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1 + minetest.sound_stop(sound_id) + + if obj:is_player() then + local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1 + obj:hud_remove(hud_id) + end + + mcl_burning.set(obj, "float", "damage") + mcl_burning.set(obj, "string", "reason") + mcl_burning.set(obj, "float", "burn_time") + mcl_burning.set(obj, "float", "damage_timer") + mcl_burning.set(obj, "int", "hud_id") + mcl_burning.set(obj, "int", "sound_id") + end +end + +function mcl_burning.catch_fire_tick(obj, dtime) + if mcl_burning.is_affected_by_rain(obj) or #mcl_burning.get_touching_nodes(obj, "group:puts_out_fire") > 0 then + mcl_burning.extinguish(obj) + else + local set_on_fire_value = mcl_burning.get_highest_group_value(obj, "set_on_fire") + + if set_on_fire_value > 0 then + mcl_burning.set_on_fire(obj, set_on_fire_value) + end + end +end + +function mcl_burning.tick(obj, dtime) + local burn_time = mcl_burning.get(obj, "float", "burn_time") - dtime + + if burn_time <= 0 then + mcl_burning.extinguish(obj) + else + mcl_burning.set(obj, "float", "burn_time", burn_time) + + local damage_timer = mcl_burning.get(obj, "float", "damage_timer") + dtime + + if damage_timer >= 1 then + damage_timer = 0 + mcl_burning.damage(obj) + end + + mcl_burning.set(obj, "float", "damage_timer", damage_timer) + end + + mcl_burning.catch_fire_tick(obj, dtime) +end + +function mcl_burning.update_animation_frame(obj, fire_entity, animation_frame) + local fire_texture = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame + local fire_HUD_texture = "mcl_burning_hud_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame + fire_entity:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}}) + if obj:is_player() then + local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1 + obj:hud_change(hud_id, "text", fire_HUD_texture) + end +end + +function mcl_burning.fire_entity_step(self, dtime) + if self.removed then + return + end + + local obj = self.object + local parent = obj:get_attach() + local do_remove + + self.doing_step = true + + if not parent or not mcl_burning.is_burning(parent) then + do_remove = true + else + for _, other in ipairs(minetest.get_objects_inside_radius(obj:get_pos(), 0)) do + local luaentity = obj:get_luaentity() + if luaentity and luaentity.name == "mcl_burning:fire" and not luaentity.doing_step and not luaentity.removed then + do_remove = true + break + end + end + end + + self.doing_step = false + + if do_remove then + self.removed = true + obj:remove() + return + end + + local animation_timer = self.animation_timer + dtime + if animation_timer >= 0.015 then + animation_timer = 0 + local animation_frame = self.animation_frame + 1 + if animation_frame > mcl_burning.animation_frames - 1 then + animation_frame = 0 + end + mcl_burning.update_animation_frame(parent, obj, animation_frame) + self.animation_frame = animation_frame + end + self.animation_timer = animation_timer +end diff --git a/mods/ENTITIES/mcl_burning/init.lua b/mods/ENTITIES/mcl_burning/init.lua new file mode 100644 index 000000000..1b341273e --- /dev/null +++ b/mods/ENTITIES/mcl_burning/init.lua @@ -0,0 +1,36 @@ +local S = minetest.get_translator("mcl_burning") +local modpath = minetest.get_modpath("mcl_burning") + +mcl_burning = { + animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8 +} + +dofile(modpath .. "/engine.lua") + +minetest.register_entity("mcl_burning:fire", { + initial_properties = { + physical = false, + collisionbox = {0, 0, 0, 0, 0, 0}, + visual = "cube", + pointable = false, + glow = -1, + }, + + animation_frame = 0, + animation_timer = 0, + on_step = mcl_burning.fire_entity_step, +}) + +minetest.register_globalstep(function(dtime) + for _, player in ipairs(minetest.get_connected_players()) do + mcl_burning.tick(player, dtime) + end +end) + +minetest.register_on_respawnplayer(function(player) + mcl_burning.extinguish(player) +end) + +minetest.register_on_leaveplayer(function(player) + mcl_burning.set(player, "int", "hud_id") +end) \ No newline at end of file diff --git a/mods/ENTITIES/mcl_burning/mod.conf b/mods/ENTITIES/mcl_burning/mod.conf new file mode 100644 index 000000000..c64959cbb --- /dev/null +++ b/mods/ENTITIES/mcl_burning/mod.conf @@ -0,0 +1,3 @@ +name = mcl_burning +description = Burning Objects for MineClone2 +author = Fleckenstein diff --git a/mods/ENTITIES/mcl_item_entity/init.lua b/mods/ENTITIES/mcl_item_entity/init.lua index 641348132..e5863abbc 100644 --- a/mods/ENTITIES/mcl_item_entity/init.lua +++ b/mods/ENTITIES/mcl_item_entity/init.lua @@ -2,6 +2,7 @@ local item_drop_settings = {} --settings table item_drop_settings.age = 1.0 --how old a dropped item (_insta_collect==false) has to be before collecting item_drop_settings.radius_magnet = 2.0 --radius of item magnet. MUST BE LARGER THAN radius_collect! +item_drop_settings.xp_radius_magnet = 7.25 --radius of xp magnet. MUST BE LARGER THAN radius_collect! item_drop_settings.radius_collect = 0.2 --radius of collection item_drop_settings.player_collect_height = 1.0 --added to their pos y value item_drop_settings.collection_safety = false --do this to prevent items from flying away on laggy servers @@ -60,8 +61,8 @@ minetest.register_globalstep(function(dtime) local checkpos = {x=pos.x,y=pos.y + item_drop_settings.player_collect_height,z=pos.z} --magnet and collection - for _,object in ipairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.radius_magnet)) do - if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then + for _,object in ipairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do + if not object:is_player() and vector.distance(checkpos, object:get_pos()) < item_drop_settings.radius_magnet and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then object:get_luaentity()._magnet_timer = object:get_luaentity()._magnet_timer + dtime local collected = false if object:get_luaentity()._magnet_timer >= 0 and object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then @@ -193,6 +194,12 @@ local check_can_drop = function(node_name, tool_capabilities) if toolgroupcaps[plus] then return true end + for e=1,5 do + local effplus = plus .. "_efficiency_" .. e + if toolgroupcaps[effplus] then + return true + end + end end end for b=1, #basegroups do @@ -204,6 +211,12 @@ local check_can_drop = function(node_name, tool_capabilities) if toolgroupcaps[plus] then return true end + for e=1,5 do + local effplus = plus .. "_efficiency_" .. e + if toolgroupcaps[effplus] then + return true + end + end end end end @@ -262,7 +275,7 @@ function minetest.handle_node_drops(pos, drops, digger) -- by hand. Creative Mode is intentionally ignored in this case. local doTileDrops = minetest.settings:get_bool("mcl_doTileDrops", true) - if (digger ~= nil and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then + if (digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then return end @@ -285,9 +298,9 @@ function minetest.handle_node_drops(pos, drops, digger) * true: Drop itself when dug by shears / silk touch tool * table: Drop every itemstring in this table when dug by shears _mcl_silk_touch_drop ]] - + local enchantments = tool and mcl_enchanting.get_enchantments(tool, "silk_touch") - + local silk_touch_drop = false local nodedef = minetest.registered_nodes[dug_node.name] if toolcaps ~= nil and toolcaps.groupcaps and toolcaps.groupcaps.shearsy_dig and nodedef._mcl_shears_drop then @@ -304,7 +317,7 @@ function minetest.handle_node_drops(pos, drops, digger) drops = nodedef._mcl_silk_touch_drop end end - + if tool and nodedef._mcl_fortune_drop and enchantments.fortune then local fortune_level = enchantments.fortune local fortune_drop = nodedef._mcl_fortune_drop @@ -319,7 +332,7 @@ function minetest.handle_node_drops(pos, drops, digger) end else -- Fixed Behavior - local drop = get_fortune_drops(fortune_drops, fortune_level) + local drop = get_fortune_drops(fortune_drop, fortune_level) drops = get_drops(drop, tool:get_name(), dug_node.param2, nodedef.paramtype2) end end @@ -330,7 +343,7 @@ function minetest.handle_node_drops(pos, drops, digger) mcl_experience.throw_experience(pos, experience_amount) end end - + for _,item in ipairs(drops) do local count if type(item) == "string" then @@ -632,12 +645,15 @@ minetest.register_entity(":__builtin:item", { local fg = minetest.get_item_group(nn, "fire") local dg = minetest.get_item_group(nn, "destroys_items") if (def and (lg ~= 0 or fg ~= 0 or dg == 1)) then - if dg ~= 2 then - minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.5}, true) + --Wait 2 seconds to allow mob drops to be cooked, & picked up instead of instantly destroyed. + if self.age > 2 then + if dg ~= 2 then + minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.5}) + end + self._removed = true + self.object:remove() + return end - self._removed = true - self.object:remove() - return end -- Push item out when stuck inside solid opaque node diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 596843ac6..430c97166 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -3,7 +3,7 @@ mobs = {} mobs.mod = "mrm" -mobs.version = "20180531" -- don't rely too much on this, rarely updated, if ever +mobs.version = "20210106" -- don't rely too much on this, rarely updated, if ever local MAX_MOB_NAME_LENGTH = 30 local HORNY_TIME = 30 @@ -226,7 +226,7 @@ local collision = function(self) local z = 0 local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5 - for _,object in ipairs(minetest.env:get_objects_inside_radius(pos, width)) do + for _,object in ipairs(minetest.get_objects_inside_radius(pos, width)) do if object:is_player() or (object:get_luaentity()._cmi_is_mob == true and object ~= self.object) then @@ -276,13 +276,16 @@ end local get_velocity = function(self) local v = self.object:get_velocity() + if v then + return (v.x * v.x + v.z * v.z) ^ 0.5 + end - return (v.x * v.x + v.z * v.z) ^ 0.5 + return 0 end -- set and return valid yaw -local set_yaw = function(self, yaw, delay) +local set_yaw = function(self, yaw, delay, dtime) if not yaw or yaw ~= yaw then yaw = 0 @@ -291,6 +294,9 @@ local set_yaw = function(self, yaw, delay) delay = delay or 0 if delay == 0 then + if self.shaking and dtime then + yaw = yaw + (math.random() * 2 - 1) * 5 * dtime + end self.object:set_yaw(yaw) return yaw end @@ -302,8 +308,8 @@ local set_yaw = function(self, yaw, delay) end -- global function to set mob yaw -function mobs:yaw(self, yaw, delay) - set_yaw(self, yaw, delay) +function mobs:yaw(self, yaw, delay, dtime) + set_yaw(self, yaw, delay, dtime) end local add_texture_mod = function(self, mod) @@ -390,7 +396,7 @@ local is_node_dangerous = function(self, nodename) return true end end - if minetest.registered_nodes[nn].damage_per_second > 0 then + if minetest.registered_nodes[nn].damage_per_second and minetest.registered_nodes[nn].damage_per_second > 0 then return true end return false @@ -643,11 +649,13 @@ end -- drop items -local item_drop = function(self, cooked) +local item_drop = function(self, cooked, looting_level) -- no drops if disabled by setting if not mobs_drop_items then return end + looting_level = looting_level or 0 + -- no drops for child mobs (except monster) if (self.child and self.type ~= "monster") then return @@ -659,11 +667,33 @@ local item_drop = function(self, cooked) self.drops = self.drops or {} -- nil check for n = 1, #self.drops do + local dropdef = self.drops[n] + local chance = 1 / dropdef.chance + local looting_type = dropdef.looting - if random(1, self.drops[n].chance) == 1 then + if looting_level > 0 then + local chance_function = dropdef.looting_chance_function + if chance_function then + chance = chance_function(looting_level) + elseif looting_type == "rare" then + chance = chance + (dropdef.looting_factor or 0.01) * looting_level + end + end - num = random(self.drops[n].min or 1, self.drops[n].max or 1) - item = self.drops[n].name + local num = 0 + local do_common_looting = (looting_level > 0 and looting_type == "common") + if random() < chance then + num = random(dropdef.min or 1, dropdef.max or 1) + elseif not dropdef.looting_ignore_chance then + do_common_looting = false + end + + if do_common_looting then + num = num + math.floor(math.random(0, looting_level) + 0.5) + end + + if num > 0 then + item = dropdef.name -- cook items when true if cooked then @@ -752,15 +782,22 @@ local check_for_death = function(self, cause, cmi_cause) local function death_handle(self) -- dropped cooked item if mob died in fire or lava if cause == "lava" or cause == "fire" then - item_drop(self, true) + item_drop(self, true, 0) else - item_drop(self, nil) - end + local wielditem = ItemStack() + if cause == "hit" then + local puncher = cmi_cause.puncher + if puncher then + wielditem = puncher:get_wielded_item() - local pos = self.object:get_pos() - - if mod_experience and ((not self.child) or self.type ~= "animal") then - mcl_experience.throw_experience(pos, math.random(self.xp_min, self.xp_max)) + if mod_experience and ((not self.child) or self.type ~= "animal") then + mcl_experience.throw_experience(self.object:get_pos(), math.random(self.xp_min, self.xp_max)) + end + end + end + local cooked = mcl_burning.is_burning(self.object) or mcl_enchanting.has_enchantment(wielditem, "fire_aspect") + local looting = mcl_enchanting.get_enchantment(wielditem, "looting") + item_drop(self, cooked, looting) end end @@ -768,7 +805,7 @@ local check_for_death = function(self, cause, cmi_cause) if self.on_die then local pos = self.object:get_pos() - local on_die_exit = self.on_die(self, pos) + local on_die_exit = self.on_die(self, pos, cmi_cause) if on_die_exit ~= true then death_handle(self) end @@ -779,6 +816,7 @@ local check_for_death = function(self, cause, cmi_cause) if on_die_exit == true then self.state = "die" + mcl_burning.extinguish(self.object) self.object:remove() return true end @@ -841,6 +879,7 @@ local check_for_death = function(self, cause, cmi_cause) local dpos = self.object:get_pos() local cbox = self.collisionbox local yaw = self.object:get_rotation().y + mcl_burning.extinguish(self.object) self.object:remove() mobs.death_effect(dpos, yaw, cbox, not self.instant_death) end @@ -903,7 +942,7 @@ local is_at_cliff_or_danger = function(self) return true else local def = minetest.registered_nodes[bnode.name] - if def and def.walkable then + if def and def.walkable then return false end end @@ -916,7 +955,7 @@ end -- copy the 'mob facing cliff_or_danger check' from above, and rework to avoid water local is_at_water_danger = function(self) - + if not self.object:get_luaentity() then return false end @@ -941,7 +980,7 @@ local is_at_water_danger = function(self) return true else local def = minetest.registered_nodes[bnode.name] - if def and def.walkable then + if def and def.walkable then return false end end @@ -989,6 +1028,7 @@ local do_env_damage = function(self) -- remove mob if beyond map limits if not within_limits(pos, 0) then + mcl_burning.extinguish(self.object) self.object:remove() return true end @@ -1017,8 +1057,11 @@ local do_env_damage = function(self) if mod_worlds then _, dim = mcl_worlds.y_to_layer(pos.y) end - if self.sunlight_damage ~= 0 and (minetest.get_node_light(pos) or 0) >= minetest.LIGHT_MAX and dim == "overworld" then - if deal_light_damage(self, pos, self.sunlight_damage) then + if (self.sunlight_damage ~= 0 or self.ignited_by_sunlight) and (minetest.get_node_light(pos) or 0) >= minetest.LIGHT_MAX and dim == "overworld" then + if self.ignited_by_sunlight then + mcl_burning.set_on_fire(self.object, 10, self.sunlight_damage or 1) + else + deal_light_damage(self, pos, self.sunlight_damage) return true end end @@ -1106,7 +1149,7 @@ local do_env_damage = function(self) end -- damage_per_second node check - elseif nodef.damage_per_second ~= 0 then + elseif nodef.damage_per_second ~= 0 and not nodef.groups.lava and not nodef.groups.fire then self.health = self.health - nodef.damage_per_second @@ -1238,7 +1281,7 @@ local do_jump = function(self) }, "air") -- we don't attempt to jump if there's a stack of blocks blocking - if minetest.registered_nodes[nodTop.name] == true then + if minetest.registered_nodes[nodTop.name].walkable == true then return false end @@ -1269,7 +1312,7 @@ local do_jump = function(self) end self.object:set_acceleration({ x = v.x * 2, - y = 0, + y = -10, z = v.z * 2, }) end, self, v) @@ -2059,6 +2102,7 @@ local follow_flop = function(self) or self.order == "follow") and not self.following and self.state ~= "attack" + and self.order ~= "sit" and self.state ~= "runaway" then local s = self.object:get_pos() @@ -2079,6 +2123,7 @@ local follow_flop = function(self) if self.type == "npc" and self.order == "follow" and self.state ~= "attack" + and self.order ~= "sit" and self.owner ~= "" then -- npc stop following player if not owner @@ -2211,7 +2256,6 @@ local dogswitch = function(self, dtime) return self.dogshoot_switch end - -- execute current state (stand, walk, run, attacks) -- returns true if mob has died local do_states = function(self, dtime) @@ -2309,10 +2353,10 @@ local do_states = function(self, dtime) lp = minetest.find_nodes_in_area_under_air( {x = s.x - 5, y = s.y - 0.5, z = s.z - 5}, {x = s.x + 5, y = s.y + 1, z = s.z + 5}, - {"group:solid"}) + {"group:solid"}) lp = #lp > 0 and lp[random(#lp)] - + -- did we find land? if lp then @@ -2364,6 +2408,8 @@ local do_states = function(self, dtime) set_velocity(self, 0) self.state = "stand" set_animation(self, "stand") + local yaw = self.object:get_yaw() or 0 + yaw = set_yaw(self, yaw + 0.78, 8) else set_velocity(self, self.walk_velocity) @@ -2390,6 +2436,8 @@ local do_states = function(self, dtime) set_velocity(self, 0) self.state = "stand" set_animation(self, "stand") + local yaw = self.object:get_yaw() or 0 + yaw = set_yaw(self, yaw + 0.78, 8) else set_velocity(self, self.run_velocity) set_animation(self, "run") @@ -2434,7 +2482,7 @@ local do_states = function(self, dtime) if p.x > s.x then yaw = yaw + pi end - yaw = set_yaw(self, yaw) + yaw = set_yaw(self, yaw, 0, dtime) local node_break_radius = self.explosion_radius or 1 local entity_damage_radius = self.explosion_damage_radius @@ -2499,7 +2547,7 @@ local do_states = function(self, dtime) if mod_explosions then if mobs_griefing and not minetest.is_protected(pos, "") then - mcl_explosions.explode(self.object:get_pos(), self.explosion_strength, { drop_chance = 1.0 }, self.object) + mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { drop_chance = 1.0 }, self.object) else minetest.sound_play(self.sounds.explode, { pos = pos, @@ -2511,6 +2559,7 @@ local do_states = function(self, dtime) effect(pos, 32, "mcl_particles_smoke.png", nil, nil, node_break_radius, 1, 0) end end + mcl_burning.extinguish(self.object) self.object:remove() return true @@ -2606,7 +2655,7 @@ local do_states = function(self, dtime) if p.x > s.x then yaw = yaw + pi end - yaw = set_yaw(self, yaw) + yaw = set_yaw(self, yaw, 0, dtime) -- move towards enemy if beyond mob reach if dist > self.reach then @@ -2622,6 +2671,8 @@ local do_states = function(self, dtime) set_velocity(self, 0) set_animation(self, "stand") + local yaw = self.object:get_yaw() or 0 + yaw = set_yaw(self, yaw + 0.78, 8) else if self.path.stuck then @@ -2709,12 +2760,16 @@ local do_states = function(self, dtime) if p.x > s.x then yaw = yaw + pi end - yaw = set_yaw(self, yaw) + yaw = set_yaw(self, yaw, 0, dtime) set_velocity(self, 0) + local p = self.object:get_pos() + p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 + if self.shoot_interval and self.timer > self.shoot_interval + and not minetest.raycast(p, self.attack:get_pos(), false, false):next() and random(1, 100) <= 60 then self.timer = 0 @@ -2723,10 +2778,6 @@ local do_states = function(self, dtime) -- play shoot attack sound mob_sound(self, "shoot_attack") - local p = self.object:get_pos() - - p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 - -- Shoot arrow if minetest.registered_entities[self.arrow] then @@ -2915,6 +2966,13 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) end end + if weapon then + local fire_aspect_level = mcl_enchanting.get_enchantment(weapon, "fire_aspect") + if fire_aspect_level > 0 then + mcl_burning.set_on_fire(self.object, 4, fire_aspect_level * 2) + end + end + -- check for tool immunity or special damage for n = 1, #self.immune_to do @@ -3019,6 +3077,18 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) kb = kb * 1.5 end + + local luaentity + if hitter then + luaentity = hitter:get_luaentity() + end + if hitter and hitter:is_player() then + local wielditem = hitter:get_wielded_item() + kb = kb + 3 * mcl_enchanting.get_enchantment(wielditem, "knockback") + elseif luaentity and luaentity._knockback then + kb = kb + luaentity._knockback + end + self.object:set_velocity({ x = dir.x * kb, y = dir.y * kb + up, @@ -3121,7 +3191,8 @@ local mob_staticdata = function(self) and ((not self.nametag) or (self.nametag == "")) and self.lifetimer <= 20 then - minetest.log("action", "Mob "..name.." despawns in mob_staticdata at "..minetest.pos_to_string(self.object.get_pos())) + minetest.log("action", "Mob "..name.." despawns in mob_staticdata at "..minetest.pos_to_string(self.object.get_pos(), 1)) + mcl_burning.extinguish(self.object) self.object:remove() return ""-- nil @@ -3160,7 +3231,7 @@ local mob_activate = function(self, staticdata, def, dtime) -- remove monsters in peaceful mode if self.type == "monster" and minetest.settings:get_bool("only_peaceful_mobs", false) then - + mcl_burning.extinguish(self.object) self.object:remove() return @@ -3324,6 +3395,10 @@ end -- main mob function local mob_step = function(self, dtime) + if not self.fire_resistant then + mcl_burning.tick(self.object, dtime) + end + if use_cmi then cmi.notify_step(self.object, dtime) end @@ -3385,6 +3460,9 @@ local mob_step = function(self, dtime) end self.delay = self.delay - 1 + if self.shaking then + yaw = yaw + (math.random() * 2 - 1) * 5 * dtime + end self.object:set_yaw(yaw) end @@ -3516,39 +3594,27 @@ local mob_step = function(self, dtime) set_velocity(self, 0) self.state = "stand" set_animation(self, "stand") + local yaw = self.object:get_yaw() or 0 + yaw = set_yaw(self, yaw + 0.78, 8) end -- Despawning: when lifetimer expires, remove mob if remove_far and self.can_despawn == true - and ((not self.nametag) or (self.nametag == "")) then + and ((not self.nametag) or (self.nametag == "")) + and self.state ~= "attack" + and self.following == nil then self.lifetimer = self.lifetimer - dtime - if self.lifetimer <= 10 then - - -- only despawn away from player - local far_objs = minetest.get_objects_inside_radius(pos, 48) - for n = 1, #far_objs do - - if far_objs[n]:is_player() then - - local close_objs = minetest.get_objects_inside_radius(pos, 16) - for n = 1, #close_objs do - if close_objs[n]:is_player() then - self.lifetimer = 20 - else - if math.random(1,10) <= 3 then - minetest.log("action", "Mob "..self.name.." despawns in mob_step at "..minetest.pos_to_string(pos)) - self.object:remove() - return - end - end - end - else - minetest.log("action", "Mob "..self.name.." despawns in mob_step at "..minetest.pos_to_string(pos)) - self.object:remove() - return - end + if self.despawn_immediately or self.lifetimer <= 0 then + minetest.log("action", "Mob "..self.name.." despawns in mob_step at "..minetest.pos_to_string(pos, 1)) + mcl_burning.extinguish(self.object) + self.object:remove() + elseif self.lifetimer <= 10 then + if math.random(10) < 4 then + self.despawn_immediately = true + else + self.lifetimer = 20 end end end @@ -3750,6 +3816,9 @@ minetest.register_entity(name, { suffocation_timer = 0, follow_velocity = def.follow_velocity or 2.4, instant_death = def.instant_death or false, + fire_resistant = def.fire_resistant or false, + fire_damage_resistant = def.fire_damage_resistant or false, + ignited_by_sunlight = def.ignited_by_sunlight or false, -- End of MCL2 extensions on_spawn = def.on_spawn, @@ -3775,7 +3844,7 @@ minetest.register_entity(name, { get_staticdata = function(self) return mob_staticdata(self) end, - + harmed_by_heal = def.harmed_by_heal, }) @@ -3930,7 +3999,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, pos.y = pos.y + 1 -- only spawn away from player - local objs = minetest.get_objects_inside_radius(pos, 10) + local objs = minetest.get_objects_inside_radius(pos, 24) for n = 1, #objs do @@ -4106,7 +4175,7 @@ function mobs:register_arrow(name, def) if self.switch == 0 or self.timer > 150 or not within_limits(pos, 0) then - + mcl_burning.extinguish(self.object) self.object:remove(); return @@ -4489,3 +4558,20 @@ function mobs:alias_mob(old_name, new_name) }) end + +local timer = 0 +minetest.register_globalstep(function(dtime) + timer = timer + dtime + if timer < 1 then return end + for _, player in ipairs(minetest.get_connected_players()) do + local pos = player:get_pos() + for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 47)) do + local lua = obj:get_luaentity() + if lua and lua._cmi_is_mob then + lua.lifetimer = math.max(20, lua.lifetimer) + lua.despawn_immediately = false + end + end + end + timer = 0 +end) diff --git a/mods/ENTITIES/mcl_mobs/api.txt b/mods/ENTITIES/mcl_mobs/api.txt index d1c478a99..ee97489b5 100644 --- a/mods/ENTITIES/mcl_mobs/api.txt +++ b/mods/ENTITIES/mcl_mobs/api.txt @@ -249,6 +249,9 @@ functions needed for the mob to work properly which contains the following: 'instant_death' If true, mob dies instantly (no death animation or delay) (default: false) 'xp_min' the minimum XP it drops on death (default: 0) 'xp_max' the maximum XP it drops on death (default: 0) + 'fire_resistant' If true, the mob can't burn + 'fire_damage_resistant' If true the mob will not take damage when burning + 'ignited_by_sunlight' If true the mod will burn at daytime. (Takes sunlight_damage per second) diff --git a/mods/ENTITIES/mobs_mc/5_spawn_abm_check.lua b/mods/ENTITIES/mobs_mc/5_spawn_abm_check.lua index 303feb1ad..85886a995 100644 --- a/mods/ENTITIES/mobs_mc/5_spawn_abm_check.lua +++ b/mods/ENTITIES/mobs_mc/5_spawn_abm_check.lua @@ -1,11 +1,20 @@ +local function is_forbidden_node(pos, node) + node = node or minetest.get_node(pos) + return minetest.get_item_group(node.name, "stair") > 0 or minetest.get_item_group(node.name, "slab") > 0 or minetest.get_item_group(node.name, "carpet") > 0 +end + function mobs:spawn_abm_check(pos, node, name) -- Don't spawn monsters on mycelium if (node.name == "mcl_core:mycelium" or node.name == "mcl_core:mycelium_snow") and minetest.registered_entities[name].type == "monster" then return true + --Don't Spawn mobs on stairs, slabs, or carpets + elseif is_forbidden_node(pos, node) or is_forbidden_node(vector.add(pos, vector.new(0, 1, 0))) then + return true -- Spawn on opaque or liquid nodes elseif minetest.get_item_group(node.name, "opaque") ~= 0 or minetest.registered_nodes[node.name].liquidtype ~= "none" then return false end + -- Reject everything else return true end diff --git a/mods/ENTITIES/mobs_mc/blaze.lua b/mods/ENTITIES/mobs_mc/blaze.lua index aae82a39b..00988a903 100644 --- a/mods/ENTITIES/mobs_mc/blaze.lua +++ b/mods/ENTITIES/mobs_mc/blaze.lua @@ -41,7 +41,8 @@ mobs:register_mob("mobs_mc:blaze", { {name = mobs_mc.items.blaze_rod, chance = 1, min = 0, - max = 1,}, + max = 1, + looting = "common",}, }, animation = { stand_speed = 25, @@ -73,6 +74,7 @@ mobs:register_mob("mobs_mc:blaze", { makes_footstep_sound = false, fear_height = 0, glow = 14, + fire_resistant = true, }) mobs:spawn_specific("mobs_mc:blaze", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 5000, 3, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max) @@ -89,6 +91,7 @@ mobs:register_arrow("mobs_mc:blaze_fireball", { if rawget(_G, "armor") and armor.last_damage_types then armor.last_damage_types[player:get_player_name()] = "fireball" end + mcl_burning.set_on_fire(player, 5, 1, "blaze") player:punch(self.object, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = 5}, @@ -96,6 +99,7 @@ mobs:register_arrow("mobs_mc:blaze_fireball", { end, hit_mob = function(self, mob) + mcl_burning.set_on_fire(mob, 5) mob:punch(self.object, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = 5}, diff --git a/mods/ENTITIES/mobs_mc/chicken.lua b/mods/ENTITIES/mobs_mc/chicken.lua index 618d1e0f0..325371e2b 100644 --- a/mods/ENTITIES/mobs_mc/chicken.lua +++ b/mods/ENTITIES/mobs_mc/chicken.lua @@ -32,11 +32,13 @@ mobs:register_mob("mobs_mc:chicken", { {name = mobs_mc.items.chicken_raw, chance = 1, min = 1, - max = 1,}, + max = 1, + looting = "common",}, {name = mobs_mc.items.feather, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, }, fall_damage = 0, fall_speed = -2.25, diff --git a/mods/ENTITIES/mobs_mc/cow+mooshroom.lua b/mods/ENTITIES/mobs_mc/cow+mooshroom.lua index e8482d820..005af2980 100644 --- a/mods/ENTITIES/mobs_mc/cow+mooshroom.lua +++ b/mods/ENTITIES/mobs_mc/cow+mooshroom.lua @@ -23,11 +23,13 @@ local cow_def = { {name = mobs_mc.items.beef_raw, chance = 1, min = 1, - max = 3,}, + max = 3, + looting = "common",}, {name = mobs_mc.items.leather, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, }, runaway = true, sounds = { diff --git a/mods/ENTITIES/mobs_mc/creeper.lua b/mods/ENTITIES/mobs_mc/creeper.lua index a52e6e1e1..f1648525a 100644 --- a/mods/ENTITIES/mobs_mc/creeper.lua +++ b/mods/ENTITIES/mobs_mc/creeper.lua @@ -37,7 +37,7 @@ mobs:register_mob("mobs_mc:creeper", { run_velocity = 2.1, runaway_from = { "mobs_mc:ocelot", "mobs_mc:cat" }, attack_type = "explode", - + explosion_strength = 3, reach = 4, explosion_timer = 1.5, @@ -71,17 +71,21 @@ mobs:register_mob("mobs_mc:creeper", { if self._forced_explosion_countdown_timer ~= nil then self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime if self._forced_explosion_countdown_timer <= 0 then - mobs:boom(self, self.object:get_pos(), self.explosion_strength) + mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength) self.object:remove() end end end, - on_die = function(self, pos) - -- Drop a random music disc - -- TODO: Only do this if killed by skeleton - if math.random(1, 200) == 1 then - local r = math.random(1, #mobs_mc.items.music_discs) - minetest.add_item({x=pos.x, y=pos.y+1, z=pos.z}, mobs_mc.items.music_discs[r]) + on_die = function(self, pos, cmi_cause) + -- Drop a random music disc when killed by skeleton or stray + if cmi_cause and cmi_cause.type == "punch" then + local luaentity = cmi_cause.puncher and cmi_cause.puncher:get_luaentity() + if luaentity and luaentity.name:find("arrow") then + local shooter_luaentity = luaentity._shooter and luaentity._shooter:get_luaentity() + if shooter_luaentity and (shooter_luaentity.name == "mobs_mc:skeleton" or shooter_luaentity.name == "mobs_mc:stray") then + minetest.add_item({x=pos.x, y=pos.y+1, z=pos.z}, mobs_mc.items.music_discs[math.random(1, #mobs_mc.items.music_discs)]) + end + end end end, maxdrops = 2, @@ -89,7 +93,8 @@ mobs:register_mob("mobs_mc:creeper", { {name = mobs_mc.items.gunpowder, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, -- Head -- TODO: Only drop if killed by charged creeper diff --git a/mods/ENTITIES/mobs_mc/ender_dragon.lua b/mods/ENTITIES/mobs_mc/ender_dragon.lua index dd68cbcce..c579213a0 100644 --- a/mods/ENTITIES/mobs_mc/ender_dragon.lua +++ b/mods/ENTITIES/mobs_mc/ender_dragon.lua @@ -49,6 +49,8 @@ mobs:register_mob("mobs_mc:enderdragon", { arrow = "mobs_mc:dragon_fireball", shoot_interval = 0.5, shoot_offset = -1.0, + xp_min = 12000, + xp_max = 12000, animation = { fly_speed = 8, stand_speed = 8, stand_start = 0, stand_end = 20, @@ -65,7 +67,8 @@ mobs:register_mob("mobs_mc:enderdragon", { --end end minetest.add_item(own_pos, mobs_mc.items.dragon_egg) - end + end, + fire_resistant = true, }) diff --git a/mods/ENTITIES/mobs_mc/enderman.lua b/mods/ENTITIES/mobs_mc/enderman.lua index 934497a10..b2971ae45 100644 --- a/mods/ENTITIES/mobs_mc/enderman.lua +++ b/mods/ENTITIES/mobs_mc/enderman.lua @@ -43,10 +43,10 @@ end local pr = PseudoRandom(os.time()*(-334)) -- How freqeuntly to take and place blocks, in seconds -local take_frequency_min = 25 -local take_frequency_max = 90 -local place_frequency_min = 10 -local place_frequency_max = 30 +local take_frequency_min = 235 +local take_frequency_max = 245 +local place_frequency_min = 235 +local place_frequency_max = 245 -- Create the textures table for the enderman, depending on which kind of block -- the enderman holds (if any). @@ -220,7 +220,8 @@ mobs:register_mob("mobs_mc:enderman", { {name = mobs_mc.items.ender_pearl, chance = 1, min = 0, - max = 1,}, + max = 1, + looting = "common"}, }, animation = select_enderman_animation("normal"), _taken_node = "", @@ -273,10 +274,10 @@ mobs:register_mob("mobs_mc:enderman", { end -- AGRESSIVELY WARP/CHASE PLAYER BEHAVIOUR HERE. if self.state == "attack" then - if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then - self:teleport(nil) - self.state = "" - else + --if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then + --self:teleport(nil) + --self.state = "" + --else if self.attack then local target = self.attack local pos = target:get_pos() @@ -286,7 +287,7 @@ mobs:register_mob("mobs_mc:enderman", { end end end - end + --end end -- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE. -- Check for arrows and people nearby. @@ -297,9 +298,9 @@ mobs:register_mob("mobs_mc:enderman", { if obj then if minetest.is_player(obj) then -- Warp from players during day. - if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then - self:teleport(nil) - end + --if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then + -- self:teleport(nil) + --end else local lua = obj:get_luaentity() if lua then @@ -314,14 +315,14 @@ mobs:register_mob("mobs_mc:enderman", { local enderpos = self.object:get_pos() if self.provoked == "broke_contact" then self.provoked = "false" - if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then - self:teleport(nil) - self.state = "" - else + --if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then + -- self:teleport(nil) + -- self.state = "" + --else if self.attack ~= nil then self.state = 'attack' end - end + --end end -- Check to see if people are near by enough to look at us. local objs = minetest.get_objects_inside_radius(enderpos, 64) @@ -527,13 +528,13 @@ mobs:register_mob("mobs_mc:enderman", { do_punch = function(self, hitter, tflp, tool_caps, dir) -- damage from rain caused by itself so we don't want it to attack itself. if hitter ~= self.object and hitter ~= nil then - if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then - self:teleport(nil) - else + --if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then + -- self:teleport(nil) + --else self:teleport(hitter) self.attack=hitter self.state="attack" - end + --end end end, armor = { fleshy = 100, water_vulnerable = 100 }, diff --git a/mods/ENTITIES/mobs_mc/ghast.lua b/mods/ENTITIES/mobs_mc/ghast.lua index 713333085..679a28c13 100644 --- a/mods/ENTITIES/mobs_mc/ghast.lua +++ b/mods/ENTITIES/mobs_mc/ghast.lua @@ -38,8 +38,8 @@ mobs:register_mob("mobs_mc:ghast", { walk_velocity = 1.6, run_velocity = 3.2, drops = { - {name = mobs_mc.items.gunpowder, chance = 1, min = 0, max = 2,}, - {name = mobs_mc.items.ghast_tear, chance = 3,min = 0,max = 1,}, + {name = mobs_mc.items.gunpowder, chance = 1, min = 0, max = 2, looting = "common"}, + {name = mobs_mc.items.ghast_tear, chance = 10/6, min = 0, max = 1, looting = "common", looting_ignore_chance = true}, }, animation = { stand_speed = 50, walk_speed = 50, run_speed = 50, @@ -62,6 +62,7 @@ mobs:register_mob("mobs_mc:ghast", { fly = true, makes_footstep_sound = false, instant_death = true, + fire_resistant = true, }) diff --git a/mods/ENTITIES/mobs_mc/guardian.lua b/mods/ENTITIES/mobs_mc/guardian.lua index dd273c1eb..13c857ea3 100644 --- a/mods/ENTITIES/mobs_mc/guardian.lua +++ b/mods/ENTITIES/mobs_mc/guardian.lua @@ -46,7 +46,8 @@ mobs:register_mob("mobs_mc:guardian", { {name = mobs_mc.items.prismarine_shard, chance = 1, min = 0, - max = 32,}, + max = 32, + looting = "common",}, -- TODO: Reduce of drops when ocean monument is ready. -- The following drops are approximations @@ -54,29 +55,39 @@ mobs:register_mob("mobs_mc:guardian", { {name = mobs_mc.items.fish_raw, chance = 4, min = 1, - max = 1,}, + max = 1, + looting = "common",}, {name = mobs_mc.items.prismarine_crystals, chance = 4, min = 1, - max = 2,}, + max = 2, + looting = "common",}, -- Rare drop: fish {name = mobs_mc.items.fish_raw, chance = 160, -- 2.5% / 4 min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.0025,}, {name = mobs_mc.items.salmon_raw, chance = 160, min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.0025,}, {name = mobs_mc.items.clownfish_raw, chance = 160, min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.0025,}, {name = mobs_mc.items.pufferfish_raw, chance = 160, min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.0025,}, }, fly = true, makes_footstep_sound = false, diff --git a/mods/ENTITIES/mobs_mc/guardian_elder.lua b/mods/ENTITIES/mobs_mc/guardian_elder.lua index 7094dee24..a58a4a5b7 100644 --- a/mods/ENTITIES/mobs_mc/guardian_elder.lua +++ b/mods/ENTITIES/mobs_mc/guardian_elder.lua @@ -51,7 +51,8 @@ mobs:register_mob("mobs_mc:guardian_elder", { {name = mobs_mc.items.prismarine_shard, chance = 1, min = 1, - max = 64,}, + max = 64, + looting = "common",}, -- TODO: Only drop if killed by player {name = mobs_mc.items.wet_sponge, @@ -64,29 +65,39 @@ mobs:register_mob("mobs_mc:guardian_elder", { {name = mobs_mc.items.fish_raw, chance = 4, min = 1, - max = 1,}, + max = 1, + looting = "common",}, {name = mobs_mc.items.prismarine_crystals, chance = 1, min = 1, - max = 10,}, + max = 10, + looting = "common",}, -- Rare drop: fish {name = mobs_mc.items.fish_raw, chance = 160, -- 2.5% / 4 min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 4,}, {name = mobs_mc.items.salmon_raw, chance = 160, min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 4,}, {name = mobs_mc.items.clownfish_raw, chance = 160, min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 4,}, {name = mobs_mc.items.pufferfish_raw, chance = 160, min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 4,}, }, fly = true, makes_footstep_sound = false, diff --git a/mods/ENTITIES/mobs_mc/horse.lua b/mods/ENTITIES/mobs_mc/horse.lua index 95a02bfca..b9d82660c 100644 --- a/mods/ENTITIES/mobs_mc/horse.lua +++ b/mods/ENTITIES/mobs_mc/horse.lua @@ -127,7 +127,8 @@ local horse = { {name = mobs_mc.items.leather, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, }, do_custom = function(self, dtime) diff --git a/mods/ENTITIES/mobs_mc/llama.lua b/mods/ENTITIES/mobs_mc/llama.lua index 58a70de2f..36d020a65 100644 --- a/mods/ENTITIES/mobs_mc/llama.lua +++ b/mods/ENTITIES/mobs_mc/llama.lua @@ -54,7 +54,8 @@ mobs:register_mob("mobs_mc:llama", { {name = mobs_mc.items.leather, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, }, fear_height = 4, sounds = { diff --git a/mods/ENTITIES/mobs_mc/parrot.lua b/mods/ENTITIES/mobs_mc/parrot.lua index 90bf215ed..407cb4466 100644 --- a/mods/ENTITIES/mobs_mc/parrot.lua +++ b/mods/ENTITIES/mobs_mc/parrot.lua @@ -37,7 +37,8 @@ mobs:register_mob("mobs_mc:parrot", { {name = mobs_mc.items.feather, chance = 1, min = 1, - max = 2,}, + max = 2, + looting = "common",}, }, animation = { stand_speed = 50, diff --git a/mods/ENTITIES/mobs_mc/pig.lua b/mods/ENTITIES/mobs_mc/pig.lua index 09bda3199..38700b6ca 100644 --- a/mods/ENTITIES/mobs_mc/pig.lua +++ b/mods/ENTITIES/mobs_mc/pig.lua @@ -27,7 +27,8 @@ mobs:register_mob("mobs_mc:pig", { {name = mobs_mc.items.porkchop_raw, chance = 1, min = 1, - max = 3,}, + max = 3, + looting = "common",}, }, fear_height = 4, sounds = { diff --git a/mods/ENTITIES/mobs_mc/polar_bear.lua b/mods/ENTITIES/mobs_mc/polar_bear.lua index d974c7a60..459ca29b4 100644 --- a/mods/ENTITIES/mobs_mc/polar_bear.lua +++ b/mods/ENTITIES/mobs_mc/polar_bear.lua @@ -36,12 +36,14 @@ mobs:register_mob("mobs_mc:polar_bear", { {name = mobs_mc.items.fish_raw, chance = 2, min = 0, - max = 2,}, + max = 2, + looting = "common",}, -- 1/4 to drop raw salmon {name = mobs_mc.items.salmon_raw, chance = 4, min = 0, - max = 2,}, + max = 2, + looting = "common",}, }, floats = 1, diff --git a/mods/ENTITIES/mobs_mc/rabbit.lua b/mods/ENTITIES/mobs_mc/rabbit.lua index e2f9d213f..e167649f6 100644 --- a/mods/ENTITIES/mobs_mc/rabbit.lua +++ b/mods/ENTITIES/mobs_mc/rabbit.lua @@ -41,11 +41,9 @@ local rabbit = { runaway = true, jump = true, drops = { - {name = mobs_mc.items.rabbit_raw, chance = 1, min = 0, max = 1}, - {name = mobs_mc.items.rabbit_hide, chance = 1, min = 0, max = 1}, - {name = mobs_mc.items.rabbit_foot, chance = 10, min = 0, max = 1}, - -- TODO: Drop rabbit's foot when it's useful - --{name = mobs_mc.items.rabbit_foot, chance = 10, min = 1, max = 1}, + {name = mobs_mc.items.rabbit_raw, chance = 1, min = 0, max = 1, looting = "common",}, + {name = mobs_mc.items.rabbit_hide, chance = 1, min = 0, max = 1, looting = "common",}, + {name = mobs_mc.items.rabbit_foot, chance = 10, min = 0, max = 1, looting = "rare", looting_factor = 0.03,}, }, fear_height = 4, animation = { diff --git a/mods/ENTITIES/mobs_mc/sheep.lua b/mods/ENTITIES/mobs_mc/sheep.lua index ecf3ad5c0..681c68e1b 100644 --- a/mods/ENTITIES/mobs_mc/sheep.lua +++ b/mods/ENTITIES/mobs_mc/sheep.lua @@ -63,11 +63,13 @@ mobs:register_mob("mobs_mc:sheep", { {name = mobs_mc.items.mutton_raw, chance = 1, min = 1, - max = 2,}, + max = 2, + looting = "common",}, {name = colors["unicolor_white"][1], chance = 1, min = 1, - max = 1,}, + max = 1, + looting = "common",}, }, fear_height = 4, sounds = { diff --git a/mods/ENTITIES/mobs_mc/shulker.lua b/mods/ENTITIES/mobs_mc/shulker.lua index 97de5aba6..faaf2ac40 100644 --- a/mods/ENTITIES/mobs_mc/shulker.lua +++ b/mods/ENTITIES/mobs_mc/shulker.lua @@ -35,9 +35,11 @@ mobs:register_mob("mobs_mc:shulker", { jump = false, drops = { {name = mobs_mc.items.shulker_shell, - chance = 1, - min = 0, - max = 1,}, + chance = 2, + min = 1, + max = 1, + looting = "rare", + looting_factor = 0.0625}, }, animation = { stand_speed = 25, walk_speed = 25, run_speed = 50, punch_speed = 25, diff --git a/mods/ENTITIES/mobs_mc/skeleton+stray.lua b/mods/ENTITIES/mobs_mc/skeleton+stray.lua index 45cc6b979..cb12e905d 100644 --- a/mods/ENTITIES/mobs_mc/skeleton+stray.lua +++ b/mods/ENTITIES/mobs_mc/skeleton+stray.lua @@ -46,15 +46,18 @@ local skeleton = { {name = mobs_mc.items.arrow, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, {name = mobs_mc.items.bow, - chance = 11, + chance = 100 / 8.5, min = 1, - max = 1,}, + max = 1, + looting = "rare",}, {name = mobs_mc.items.bone, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, -- Head -- TODO: Only drop if killed by charged creeper @@ -78,7 +81,7 @@ local skeleton = { die_speed = 15, die_loop = false, }, - sunlight_damage = 1, + ignited_by_sunlight = true, view_range = 16, fear_height = 4, attack_type = "dogshoot", @@ -116,12 +119,21 @@ stray.textures = { -- TODO: different sound (w/ echo) -- TODO: stray's arrow inflicts slowness status table.insert(stray.drops, { - -- Chance to drop additional arrow. - -- TODO: Should be tipped arrow of slowness - name = mobs_mc.items.arrow, + name = "mcl_potions:slowness_arrow", chance = 2, min = 1, max = 1, + looting = "rare", + looting_chance_function = function(lvl) + local chance = 0.5 + for i = 1, lvl do + if chance > 1 then + return 1 + end + chance = chance + (1 - chance) / 2 + end + return chance + end, }) mobs:register_mob("mobs_mc:stray", stray) diff --git a/mods/ENTITIES/mobs_mc/skeleton_wither.lua b/mods/ENTITIES/mobs_mc/skeleton_wither.lua index 6dd2fcdbe..e4a1f86fc 100644 --- a/mods/ENTITIES/mobs_mc/skeleton_wither.lua +++ b/mods/ENTITIES/mobs_mc/skeleton_wither.lua @@ -45,17 +45,20 @@ mobs:register_mob("mobs_mc:witherskeleton", { {name = mobs_mc.items.coal, chance = 1, min = 0, - max = 1,}, + max = 1, + looting = "common",}, {name = mobs_mc.items.bone, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, -- Head {name = mobs_mc.items.head_wither_skeleton, chance = 40, -- 2.5% chance min = 1, - max = 1,}, + max = 1, + looting = "rare",}, }, animation = { stand_start = 0, @@ -87,6 +90,7 @@ mobs:register_mob("mobs_mc:witherskeleton", { dogshoot_count_max =0.5, fear_height = 4, harmed_by_heal = true, + fire_resistant = true, }) --spawn diff --git a/mods/ENTITIES/mobs_mc/slime+magma_cube.lua b/mods/ENTITIES/mobs_mc/slime+magma_cube.lua index 743733930..98c29870c 100644 --- a/mods/ENTITIES/mobs_mc/slime+magma_cube.lua +++ b/mods/ENTITIES/mobs_mc/slime+magma_cube.lua @@ -108,7 +108,8 @@ local slime_big = { jump_height = 5.2, fear_height = 0, spawn_small_alternative = "mobs_mc:slime_small", - on_die = spawn_children_on_die("mobs_mc:slime_small", 4, 1.0, 1.5) + on_die = spawn_children_on_die("mobs_mc:slime_small", 4, 1.0, 1.5), + fire_resistant = true, } mobs:register_mob("mobs_mc:slime_big", slime_big) @@ -220,7 +221,8 @@ local magma_cube_big = { walk_chance = 0, fear_height = 0, spawn_small_alternative = "mobs_mc:magma_cube_small", - on_die = spawn_children_on_die("mobs_mc:magma_cube_small", 3, 0.8, 1.5) + on_die = spawn_children_on_die("mobs_mc:magma_cube_small", 3, 0.8, 1.5), + fire_resistant = true, } mobs:register_mob("mobs_mc:magma_cube_big", magma_cube_big) diff --git a/mods/ENTITIES/mobs_mc/spider.lua b/mods/ENTITIES/mobs_mc/spider.lua index 72a996038..0bb03a9c7 100644 --- a/mods/ENTITIES/mobs_mc/spider.lua +++ b/mods/ENTITIES/mobs_mc/spider.lua @@ -49,8 +49,10 @@ local spider = { view_range = 16, floats = 1, drops = { - {name = mobs_mc.items.string, chance = 1, min = 0, max = 2,}, - {name = mobs_mc.items.spider_eye, chance = 3, min = 1, max = 1,}, + {name = mobs_mc.items.string, chance = 1, min = 0, max = 2, looting = "common"}, + {name = mobs_mc.items.spider_eye, chance = 3, min = 1, max = 1, looting = "common", looting_chance_function = function(lvl) + return 1 - 2 / (lvl + 3) + end}, }, specific_attack = { "player", "mobs_mc:iron_golem" }, fear_height = 4, diff --git a/mods/ENTITIES/mobs_mc/squid.lua b/mods/ENTITIES/mobs_mc/squid.lua index 392143321..1877a2104 100644 --- a/mods/ENTITIES/mobs_mc/squid.lua +++ b/mods/ENTITIES/mobs_mc/squid.lua @@ -42,7 +42,8 @@ mobs:register_mob("mobs_mc:squid", { {name = mobs_mc.items.black_dye, chance = 1, min = 1, - max = 3,}, + max = 3, + looting = "common",}, }, visual_size = {x=3, y=3}, makes_footstep_sound = false, diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 9788d58c6..dc3fbd9ca 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -99,8 +99,7 @@ local professions = { { { { "mcl_fishing:fish_raw", 6, 6, "mcl_core:emerald", 1, 1 }, { "mcl_fishing:fish_cooked", 6, 6 } }, { { "mcl_mobitems:string", 15, 20 }, E1 }, - -- TODO: replace with enchanted fishing rod - { { "mcl_core:emerald", 3, 11 }, { "mcl_fishing:fishing_rod", 1, 1} }, + { { "mcl_core:emerald", 3, 11 }, { "mcl_fishing:fishing_rod_enchanted", 1, 1} }, }, }, }, @@ -154,23 +153,27 @@ local professions = { trades = { { { { "mcl_core:paper", 24, 36 }, E1 }, - -- TODO: enchanted book { { "mcl_books:book", 8, 10 }, E1 }, { { "mcl_core:emerald", 10, 12 }, { "mcl_compass:compass", 1 ,1 }}, { { "mcl_core:emerald", 3, 4 }, { "mcl_books:bookshelf", 1 ,1 }}, + { { "mcl_core:emerald", 5, 64 }, { "mcl_enchanting:book_enchanted", 1 ,1 }}, }, { { { "mcl_books:written_book", 2, 2 }, E1 }, { { "mcl_core:emerald", 10, 12 }, { "mcl_clock:clock", 1, 1 } }, { E1, { "mcl_core:glass", 3, 5 } }, + { { "mcl_core:emerald", 5, 64 }, { "mcl_enchanting:book_enchanted", 1 ,1 }}, }, { { E1, { "mcl_core:glass", 3, 5 } }, + { { "mcl_core:emerald", 5, 64 }, { "mcl_enchanting:book_enchanted", 1 ,1 }}, }, - -- TODO: 2 enchanted book tiers + { + { { "mcl_core:emerald", 5, 64 }, { "mcl_enchanting:book_enchanted", 1 ,1 }}, + }, { { { "mcl_core:emerald", 20, 22 }, { "mcl_mobs:nametag", 1, 1 } }, @@ -214,8 +217,7 @@ local professions = { { { { "mcl_core:diamond", 3, 4 }, E1 }, - -- TODO: enchant - { { "mcl_core:emerald", 16, 19 }, { "mcl_armor:chestplate_diamond", 1, 1 } }, + { { "mcl_core:emerald", 16, 19 }, { "mcl_armor:chestplate_diamond_enchanted", 1, 1 } }, }, { @@ -236,8 +238,7 @@ local professions = { }, { - -- TODO: enchant - { { "mcl_core:emerald", 7, 12 }, { "mcl_armor:chestplate_leather", 1, 1 } }, + { { "mcl_core:emerald", 7, 12 }, { "mcl_armor:chestplate_leather_enchanted", 1, 1 } }, }, { @@ -272,16 +273,13 @@ local professions = { { { { "mcl_core:iron_ingot", 7, 9 }, E1 }, - -- TODO: enchant - { { "mcl_core:emerald", 9, 10 }, { "mcl_tools:sword_iron", 1, 1 } }, + { { "mcl_core:emerald", 9, 10 }, { "mcl_tools:sword_iron_enchanted", 1, 1 } }, }, { { { "mcl_core:diamond", 3, 4 }, E1 }, - -- TODO: enchant - { { "mcl_core:emerald", 12, 15 }, { "mcl_tools:sword_diamond", 1, 1 } }, - -- TODO: enchant - { { "mcl_core:emerald", 9, 12 }, { "mcl_tools:axe_diamond", 1, 1 } }, + { { "mcl_core:emerald", 12, 15 }, { "mcl_tools:sword_diamond_enchanted", 1, 1 } }, + { { "mcl_core:emerald", 9, 12 }, { "mcl_tools:axe_diamond_enchanted", 1, 1 } }, }, }, }, @@ -291,20 +289,17 @@ local professions = { trades = { { { { "mcl_core:coal_lump", 16, 24 }, E1 }, - -- TODO: enchant - { { "mcl_core:emerald", 5, 7 }, { "mcl_tools:shovel_iron", 1, 1 } }, + { { "mcl_core:emerald", 5, 7 }, { "mcl_tools:shovel_iron_enchanted", 1, 1 } }, }, { { { "mcl_core:iron_ingot", 7, 9 }, E1 }, - -- TODO: enchant - { { "mcl_core:emerald", 9, 11 }, { "mcl_tools:pick_iron", 1, 1 } }, + { { "mcl_core:emerald", 9, 11 }, { "mcl_tools:pick_iron_enchanted", 1, 1 } }, }, { { { "mcl_core:diamond", 3, 4 }, E1 }, - -- TODO: enchant - { { "mcl_core:emerald", 12, 15 }, { "mcl_tools:pick_diamond", 1, 1 } }, + { { "mcl_core:emerald", 12, 15 }, { "mcl_tools:pick_diamond_enchanted", 1, 1 } }, }, }, }, @@ -328,7 +323,10 @@ local professions = { TRADE_V6_RED_SANDSTONE, }, - -- TODO: Bottle 'o enchanting + { + { { "mcl_nether:nether_wart_item", 22, 22 }, E1 }, + { { "mcl_core:emerald", 3, 3 }, { "mcl_experience:bottle", 1, 1 } }, + }, }, }, nitwit = { @@ -408,6 +406,15 @@ local init_trades = function(self, inv) local offered_item = trade[2][1] local offered_count = math.random(trade[2][2], trade[2][3]) + local offered_stack = ItemStack({name = offered_item, count = offered_count}) + if mcl_enchanting.is_enchanted(offered_item) then + if mcl_enchanting.is_book(offered_item) then + offered_stack = mcl_enchanting.get_uniform_randomly_enchanted_book({"soul_speed"}) + else + mcl_enchanting.enchant_randomly(offered_stack, math.random(5, 19), false, false, true) + end + end + local wanted = { wanted1_item .. " " ..wanted1_count } if trade[1][4] then local wanted2_item = trade[1][4] @@ -417,7 +424,7 @@ local init_trades = function(self, inv) table.insert(trades, { wanted = wanted, - offered = offered_item .. " " .. offered_count, + offered = offered_stack:to_table(), tier = tiernum, -- tier of this trade traded_once = false, -- true if trade was traded at least once trade_counter = 0, -- how often the this trade was mate after the last time it got unlocked @@ -426,6 +433,7 @@ local init_trades = function(self, inv) end end self._trades = minetest.serialize(trades) + minetest.deserialize(self._trades) end local set_trade = function(trader, player, inv, concrete_tradenum) @@ -513,7 +521,7 @@ local function show_trade_formspec(playername, trader, tradenum) .."item_image[2,1;1,1;"..wanted1:to_string().."]" .."tooltip[2,1;0.8,0.8;"..F(wanted1:get_description()).."]" ..w2_formspec - .."item_image[5.76,1;1,1;"..offered:to_string().."]" + .."item_image[5.76,1;1,1;"..offered:get_name().." "..offered:get_count().."]" .."tooltip[5.76,1;0.8,0.8;"..F(offered:get_description()).."]" .."list["..tradeinv..";input;2,2.5;2,1;]" .."list["..tradeinv..";output;5.76,2.55;1,1;]" @@ -954,6 +962,7 @@ mobs:register_mob("mobs_mc:villager", { walk_velocity = 1.2, run_velocity = 2.4, drops = {}, + can_despawn = false, -- TODO: sounds animation = { stand_speed = 25, diff --git a/mods/ENTITIES/mobs_mc/villager_evoker.lua b/mods/ENTITIES/mobs_mc/villager_evoker.lua index b865a2bd5..226c82a32 100644 --- a/mods/ENTITIES/mobs_mc/villager_evoker.lua +++ b/mods/ENTITIES/mobs_mc/villager_evoker.lua @@ -55,7 +55,8 @@ mobs:register_mob("mobs_mc:evoker", { {name = mobs_mc.items.emerald, chance = 1, min = 0, - max = 1,}, + max = 1, + looting = "common",}, {name = mobs_mc.items.totem, chance = 1, min = 1, diff --git a/mods/ENTITIES/mobs_mc/villager_vindicator.lua b/mods/ENTITIES/mobs_mc/villager_vindicator.lua index 825ffd88f..3e611acdd 100644 --- a/mods/ENTITIES/mobs_mc/villager_vindicator.lua +++ b/mods/ENTITIES/mobs_mc/villager_vindicator.lua @@ -41,11 +41,13 @@ mobs:register_mob("mobs_mc:vindicator", { {name = mobs_mc.items.emerald, chance = 1, min = 0, - max = 1,}, + max = 1, + looting = "common",}, {name = mobs_mc.items.iron_axe, - chance = 11, + chance = 100 / 8.5, min = 1, - max = 1,}, + max = 1, + looting = "rare",}, }, -- TODO: sounds animation = { @@ -66,7 +68,6 @@ mobs:register_mob("mobs_mc:vindicator", { }, view_range = 16, fear_height = 4, - }) -- spawn eggs diff --git a/mods/ENTITIES/mobs_mc/villager_zombie.lua b/mods/ENTITIES/mobs_mc/villager_zombie.lua index 524a918de..d7f2203e1 100644 --- a/mods/ENTITIES/mobs_mc/villager_zombie.lua +++ b/mods/ENTITIES/mobs_mc/villager_zombie.lua @@ -5,12 +5,25 @@ local S = minetest.get_translator("mobs_mc") --- TODO: Turn villagers to zombie villager - --################### --################### ZOMBIE VILLAGER --################### +local professions = { + farmer = "mobs_mc_villager_farmer.png", + fisherman = "mobs_mc_villager_farmer.png", + fletcher = "mobs_mc_villager_farmer.png", + shepherd = "mobs_mc_villager_farmer.png", + librarian = "mobs_mc_villager_librarian.png", + cartographer = "mobs_mc_villager_librarian.png", + armorer = "mobs_mc_villager_smith.png", + leatherworker = "mobs_mc_villager_butcher.png", + butcher = "mobs_mc_villager_butcher.png", + weapon_smith = "mobs_mc_villager_smith.png", + tool_smith = "mobs_mc_villager_smith.png", + cleric = "mobs_mc_villager_priest.png", + nitwit = "mobs_mc_villager.png", +} mobs:register_mob("mobs_mc:villager_zombie", { type = "monster", @@ -44,19 +57,26 @@ mobs:register_mob("mobs_mc:villager_zombie", { {name = mobs_mc.items.rotten_flesh, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, {name = mobs_mc.items.iron_ingot, chance = 120, -- 2.5% / 3 min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 3,}, {name = mobs_mc.items.carrot, chance = 120, -- 2.5% / 3 min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 3,}, {name = mobs_mc.items.potato, chance = 120, -- 2.5% / 3 min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 3,}, }, sounds = { random = "mobs_mc_zombie_growl", @@ -75,14 +95,60 @@ mobs:register_mob("mobs_mc:villager_zombie", { run_start = 0, run_end = 20, }, - sunlight_damage = 1, + on_rightclick = function(self, clicker) + if not self._curing and clicker and clicker:is_player() then + local wielditem = clicker:get_wielded_item() + -- ToDo: Only cure if zombie villager has the weakness effect + if wielditem:get_name() == "mcl_core:apple_gold" then + wielditem:take_item() + clicker:set_wielded_item(wielditem) + self._curing = math.random(3 * 60, 5 * 60) + self.shaking = true + end + end + end, + do_custom = function(self, dtime) + if self._curing then + self._curing = self._curing - dtime + local obj = self.object + if self._curing <= 0 then + local villager_obj = minetest.add_entity(obj:get_pos(), "mobs_mc:villager") + local villager = villager_obj:get_luaentity() + local yaw = obj:get_yaw() + villager_obj:set_yaw(yaw) + villager.target_yaw = yaw + villager.nametag = self.nametag + local texture = self.base_texture[1]:gsub("zombie", "villager") + if texture == "mobs_mc_villager_villager.png" then + texture = "mobs_mc_villager.png" + end + local textures = {texture} + villager.base_texture = textures + villager_obj:set_properties({textures = textures}) + local matches = {} + for prof, tex in pairs(professions) do + if texture == tex then + table.insert(matches, prof) + end + end + villager._profession = matches[math.random(#matches)] + self._curing = nil + mcl_burning.extinguish(obj) + obj:remove() + return false + end + end + end, + sunlight_damage = 2, + ignited_by_sunlight = true, view_range = 16, fear_height = 4, harmed_by_heal = true, - }) -mobs:spawn_specific("mobs_mc:villager_zombie", mobs_mc.spawn.village, {"air"}, 0, 7, 30, 4090, 4, mobs_mc.spawn_height.water+1, mobs_mc.spawn_height.overworld_max) +mobs:spawn_specific("mobs_mc:villager_zombie", mobs_mc.spawn.village, {"air"}, 0, 7, 30, 4090, 4, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max) +mobs:spawn_specific("mobs_mc:villager_zombie", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 60000, 4, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max) -- spawn eggs mobs:register_egg("mobs_mc:villager_zombie", S("Zombie Villager"), "mobs_mc_spawn_icon_zombie_villager.png", 0) + diff --git a/mods/ENTITIES/mobs_mc/witch.lua b/mods/ENTITIES/mobs_mc/witch.lua index 5cddd5b42..e95357564 100644 --- a/mods/ENTITIES/mobs_mc/witch.lua +++ b/mods/ENTITIES/mobs_mc/witch.lua @@ -41,13 +41,13 @@ mobs:register_mob("mobs_mc:witch", { dogshoot_count_max =1.8, max_drops = 3, drops = { - {name = mobs_mc.items.glass_bottle, chance = 8, min = 0, max = 2,}, - {name = mobs_mc.items.glowstone_dust, chance = 8, min = 0, max = 2,}, - {name = mobs_mc.items.gunpowder, chance = 8, min = 0, max = 2,}, - {name = mobs_mc.items.redstone, chance = 8, min = 0, max = 2,}, - {name = mobs_mc.items.spider_eye, chance = 8, min = 0, max = 2,}, - {name = mobs_mc.items.sugar, chance = 8, min = 0, max = 2,}, - {name = mobs_mc.items.stick, chance = 4, min = 0, max = 2,}, + {name = mobs_mc.items.glass_bottle, chance = 8, min = 0, max = 2, looting = "common",}, + {name = mobs_mc.items.glowstone_dust, chance = 8, min = 0, max = 2, looting = "common",}, + {name = mobs_mc.items.gunpowder, chance = 8, min = 0, max = 2, looting = "common",}, + {name = mobs_mc.items.redstone, chance = 8, min = 0, max = 2, looting = "common",}, + {name = mobs_mc.items.spider_eye, chance = 8, min = 0, max = 2, looting = "common",}, + {name = mobs_mc.items.sugar, chance = 8, min = 0, max = 2, looting = "common",}, + {name = mobs_mc.items.stick, chance = 4, min = 0, max = 2, looting = "common",}, }, -- TODO: sounds animation = { @@ -68,7 +68,6 @@ mobs:register_mob("mobs_mc:witch", { }, view_range = 16, fear_height = 4, - }) -- potion projectile (EXPERIMENTAL) diff --git a/mods/ENTITIES/mobs_mc/zombie.lua b/mods/ENTITIES/mobs_mc/zombie.lua index beb51d89f..df9727d34 100644 --- a/mods/ENTITIES/mobs_mc/zombie.lua +++ b/mods/ENTITIES/mobs_mc/zombie.lua @@ -13,19 +13,26 @@ local drops_common = { {name = mobs_mc.items.rotten_flesh, chance = 1, min = 0, - max = 2,}, + max = 2, + looting = "common",}, {name = mobs_mc.items.iron_ingot, chance = 120, -- 2.5% / 3 min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 3,}, {name = mobs_mc.items.carrot, chance = 120, -- 2.5% / 3 min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 3,}, {name = mobs_mc.items.potato, chance = 120, -- 2.5% / 3 min = 1, - max = 1,}, + max = 1, + looting = "rare", + looting_factor = 0.01 / 3,}, } local drops_zombie = table.copy(drops_common) @@ -78,6 +85,7 @@ local zombie = { walk_start = 0, walk_end = 40, run_start = 0, run_end = 40, }, + ignited_by_sunlight = true, sunlight_damage = 2, view_range = 16, attack_type = "dogfight", @@ -104,6 +112,7 @@ mobs:register_mob("mobs_mc:baby_zombie", baby_zombie) -- Desert variant of the zombie local husk = table.copy(zombie) husk.textures = {{"mobs_mc_husk.png"}} +husk.ignited_by_sunlight = false husk.sunlight_damage = 0 husk.drops = drops_common -- TODO: Husks avoid water diff --git a/mods/ENTITIES/mobs_mc/zombiepig.lua b/mods/ENTITIES/mobs_mc/zombiepig.lua index a552f6d23..8c29a4bff 100644 --- a/mods/ENTITIES/mobs_mc/zombiepig.lua +++ b/mods/ENTITIES/mobs_mc/zombiepig.lua @@ -19,7 +19,6 @@ local pigman = { hp_max = 20, xp_min = 6, xp_max = 6, - breath_max = -1, armor = {undead = 90, fleshy = 90}, attack_type = "dogfight", group_attack = { "mobs_mc:pigman", "mobs_mc:baby_pigman" }, @@ -50,19 +49,23 @@ local pigman = { {name = mobs_mc.items.rotten_flesh, chance = 1, min = 1, - max = 1,}, + max = 1, + looting = "common"}, {name = mobs_mc.items.gold_nugget, chance = 1, min = 0, - max = 1,}, + max = 1, + looting = "common"}, {name = mobs_mc.items.gold_ingot, chance = 40, -- 2.5% min = 1, - max = 1,}, + max = 1, + looting = "rare"}, {name = mobs_mc.items.gold_sword, - chance = 12, -- 8.333%, approximation to 8.5% + chance = 100 / 8.5, min = 1, - max = 1,}, + max = 1, + looting = "rare"}, }, animation = { stand_speed = 25, @@ -82,6 +85,7 @@ local pigman = { fear_height = 4, view_range = 16, harmed_by_heal = true, + fire_damage_resistant = true, } mobs:register_mob("mobs_mc:pigman", pigman) diff --git a/mods/ENVIRONMENT/mcl_void_damage/init.lua b/mods/ENVIRONMENT/mcl_void_damage/init.lua index bdd60508f..205198a46 100644 --- a/mods/ENVIRONMENT/mcl_void_damage/init.lua +++ b/mods/ENVIRONMENT/mcl_void_damage/init.lua @@ -65,7 +65,7 @@ minetest.register_globalstep(function(dtime) if is_immortal or not enable_damage then -- If damage is disabled, we can't kill players. -- So we just teleport the player back to spawn. - local spawn = mcl_spawn.get_spawn_pos(player) + local spawn = mcl_spawn.get_player_spawn_pos(player) player:set_pos(spawn) mcl_worlds.dimension_change(player, mcl_worlds.pos_to_dimension(spawn)) minetest.chat_send_player(player:get_player_name(), S("The void is off-limits to you!")) diff --git a/mods/ENVIRONMENT/mcl_weather/init.lua b/mods/ENVIRONMENT/mcl_weather/init.lua index c7b8fb6de..e4ebfb2dc 100644 --- a/mods/ENVIRONMENT/mcl_weather/init.lua +++ b/mods/ENVIRONMENT/mcl_weather/init.lua @@ -1,10 +1,10 @@ -local modpath = minetest.get_modpath("mcl_weather"); +local modpath = minetest.get_modpath("mcl_weather") mcl_weather = {} -- If not located then embeded skycolor mod version will be loaded. if minetest.get_modpath("skycolor") == nil then - dofile(modpath.."/skycolor.lua") + dofile(modpath.."/skycolor.lua") end dofile(modpath.."/weather_core.lua") @@ -13,5 +13,5 @@ dofile(modpath.."/rain.lua") dofile(modpath.."/nether_dust.lua") if minetest.get_modpath("lightning") ~= nil then - dofile(modpath.."/thunder.lua") + dofile(modpath.."/thunder.lua") end diff --git a/mods/ENVIRONMENT/mcl_weather/nether_dust.lua b/mods/ENVIRONMENT/mcl_weather/nether_dust.lua index e41746a71..735676454 100644 --- a/mods/ENVIRONMENT/mcl_weather/nether_dust.lua +++ b/mods/ENVIRONMENT/mcl_weather/nether_dust.lua @@ -24,11 +24,8 @@ end local timer = 0 minetest.register_globalstep(function(dtime) timer = timer + dtime - if timer >= 0.7 then - timer = 0 - else - return - end + if timer < 0.7 then return end + timer = 0 for _, player in ipairs(minetest.get_connected_players()) do if not mcl_worlds.has_dust(player:get_pos()) then diff --git a/mods/ENVIRONMENT/mcl_weather/rain.lua b/mods/ENVIRONMENT/mcl_weather/rain.lua index 0dde0d2d8..9b4210060 100644 --- a/mods/ENVIRONMENT/mcl_weather/rain.lua +++ b/mods/ENVIRONMENT/mcl_weather/rain.lua @@ -2,198 +2,198 @@ local PARTICLES_COUNT_RAIN = 30 local PARTICLES_COUNT_THUNDER = 45 mcl_weather.rain = { - -- max rain particles created at time - particles_count = PARTICLES_COUNT_RAIN, + -- max rain particles created at time + particles_count = PARTICLES_COUNT_RAIN, - -- flag to turn on/off extinguish fire for rain - extinguish_fire = true, + -- flag to turn on/off extinguish fire for rain + extinguish_fire = true, - -- flag useful when mixing weathers - raining = false, + -- flag useful when mixing weathers + raining = false, - -- keeping last timeofday value (rounded). - -- Defaulted to non-existing value for initial comparing. - sky_last_update = -1, + -- keeping last timeofday value (rounded). + -- Defaulted to non-existing value for initial comparing. + sky_last_update = -1, - init_done = false, + init_done = false, } mcl_weather.rain.sound_handler = function(player) - return minetest.sound_play("weather_rain", { - to_player = player:get_player_name(), - loop = true, - }) + return minetest.sound_play("weather_rain", { + to_player = player:get_player_name(), + loop = true, + }) end -- set skybox based on time (uses skycolor api) mcl_weather.rain.set_sky_box = function() - if mcl_weather.state == "rain" then - mcl_weather.skycolor.add_layer( - "weather-pack-rain-sky", - {{r=0, g=0, b=0}, - {r=85, g=86, b=98}, - {r=135, g=135, b=151}, - {r=85, g=86, b=98}, - {r=0, g=0, b=0}}) - mcl_weather.skycolor.active = true - for _, player in pairs(minetest.get_connected_players()) do - player:set_clouds({color="#5D5D5FE8"}) - end - end + if mcl_weather.state == "rain" then + mcl_weather.skycolor.add_layer( + "weather-pack-rain-sky", + {{r=0, g=0, b=0}, + {r=85, g=86, b=98}, + {r=135, g=135, b=151}, + {r=85, g=86, b=98}, + {r=0, g=0, b=0}}) + mcl_weather.skycolor.active = true + for _, player in pairs(minetest.get_connected_players()) do + player:set_clouds({color="#5D5D5FE8"}) + end + end end -- creating manually parctiles instead of particles spawner because of easier to control -- spawn position. mcl_weather.rain.add_rain_particles = function(player) - mcl_weather.rain.last_rp_count = 0 - for i=mcl_weather.rain.particles_count, 1,-1 do - local random_pos_x, random_pos_y, random_pos_z = mcl_weather.get_random_pos_by_player_look_dir(player) - if mcl_weather.is_outdoor({x=random_pos_x, y=random_pos_y, z=random_pos_z}) then - mcl_weather.rain.last_rp_count = mcl_weather.rain.last_rp_count + 1 - minetest.add_particle({ - pos = {x=random_pos_x, y=random_pos_y, z=random_pos_z}, - velocity = {x=0, y=-10, z=0}, - acceleration = {x=0, y=-30, z=0}, - expirationtime = 1.0, - size = math.random(0.5, 3), - collisiondetection = true, - collision_removal = true, - vertical = true, - texture = mcl_weather.rain.get_texture(), - playername = player:get_player_name() - }) - end - end + mcl_weather.rain.last_rp_count = 0 + for i=mcl_weather.rain.particles_count, 1,-1 do + local random_pos_x, random_pos_y, random_pos_z = mcl_weather.get_random_pos_by_player_look_dir(player) + if mcl_weather.is_outdoor({x=random_pos_x, y=random_pos_y, z=random_pos_z}) then + mcl_weather.rain.last_rp_count = mcl_weather.rain.last_rp_count + 1 + minetest.add_particle({ + pos = {x=random_pos_x, y=random_pos_y, z=random_pos_z}, + velocity = {x=0, y=-10, z=0}, + acceleration = {x=0, y=-30, z=0}, + expirationtime = 1.0, + size = math.random(0.5, 3), + collisiondetection = true, + collision_removal = true, + vertical = true, + texture = mcl_weather.rain.get_texture(), + playername = player:get_player_name() + }) + end + end end -- Simple random texture getter mcl_weather.rain.get_texture = function() - local texture_name - local random_number = math.random() - if random_number > 0.33 then - texture_name = "weather_pack_rain_raindrop_1.png" - elseif random_number > 0.66 then - texture_name = "weather_pack_rain_raindrop_2.png" - else - texture_name = "weather_pack_rain_raindrop_3.png" - end - return texture_name; + local texture_name + local random_number = math.random() + if random_number > 0.33 then + texture_name = "weather_pack_rain_raindrop_1.png" + elseif random_number > 0.66 then + texture_name = "weather_pack_rain_raindrop_2.png" + else + texture_name = "weather_pack_rain_raindrop_3.png" + end + return texture_name; end -- register player for rain weather. -- basically needs for origin sky reference and rain sound controls. mcl_weather.rain.add_player = function(player) - if mcl_weather.players[player:get_player_name()] == nil then - local player_meta = {} - player_meta.origin_sky = {player:get_sky()} - mcl_weather.players[player:get_player_name()] = player_meta - end + if mcl_weather.players[player:get_player_name()] == nil then + local player_meta = {} + player_meta.origin_sky = {player:get_sky()} + mcl_weather.players[player:get_player_name()] = player_meta + end end -- remove player from player list effected by rain. -- be sure to remove sound before removing player otherwise soundhandler reference will be lost. mcl_weather.rain.remove_player = function(player) - local player_meta = mcl_weather.players[player:get_player_name()] - if player_meta ~= nil and player_meta.origin_sky ~= nil then - player:set_clouds({color="#FFF0F0E5"}) - mcl_weather.players[player:get_player_name()] = nil - end + local player_meta = mcl_weather.players[player:get_player_name()] + if player_meta ~= nil and player_meta.origin_sky ~= nil then + player:set_clouds({color="#FFF0F0E5"}) + mcl_weather.players[player:get_player_name()] = nil + end end mcl_worlds.register_on_dimension_change(function(player, dimension) - if dimension ~= "overworld" and dimension ~= "void" then - mcl_weather.rain.remove_sound(player) - mcl_weather.rain.remove_player(player) - elseif dimension == "overworld" then - mcl_weather.rain.update_sound(player) - if mcl_weather.rain.raining then - mcl_weather.rain.add_rain_particles(player) - mcl_weather.rain.add_player(player) - end - end + if dimension ~= "overworld" and dimension ~= "void" then + mcl_weather.rain.remove_sound(player) + mcl_weather.rain.remove_player(player) + elseif dimension == "overworld" then + mcl_weather.rain.update_sound(player) + if mcl_weather.rain.raining then + mcl_weather.rain.add_rain_particles(player) + mcl_weather.rain.add_player(player) + end + end end) -- adds and removes rain sound depending how much rain particles around player currently exist. -- have few seconds delay before each check to avoid on/off sound too often -- when player stay on 'edge' where sound should play and stop depending from random raindrop appearance. mcl_weather.rain.update_sound = function(player) - local player_meta = mcl_weather.players[player:get_player_name()] - if player_meta ~= nil then - if player_meta.sound_updated ~= nil and player_meta.sound_updated + 5 > minetest.get_gametime() then - return false - end + local player_meta = mcl_weather.players[player:get_player_name()] + if player_meta ~= nil then + if player_meta.sound_updated ~= nil and player_meta.sound_updated + 5 > minetest.get_gametime() then + return false + end - if player_meta.sound_handler ~= nil then - if mcl_weather.rain.last_rp_count == 0 then - minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0) - player_meta.sound_handler = nil - end - elseif mcl_weather.rain.last_rp_count > 0 then - player_meta.sound_handler = mcl_weather.rain.sound_handler(player) - end + if player_meta.sound_handler ~= nil then + if mcl_weather.rain.last_rp_count == 0 then + minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0) + player_meta.sound_handler = nil + end + elseif mcl_weather.rain.last_rp_count > 0 then + player_meta.sound_handler = mcl_weather.rain.sound_handler(player) + end - player_meta.sound_updated = minetest.get_gametime() - end + player_meta.sound_updated = minetest.get_gametime() + end end -- rain sound removed from player. mcl_weather.rain.remove_sound = function(player) - local player_meta = mcl_weather.players[player:get_player_name()] - if player_meta ~= nil and player_meta.sound_handler ~= nil then - minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0) - player_meta.sound_handler = nil - player_meta.sound_updated = nil - end + local player_meta = mcl_weather.players[player:get_player_name()] + if player_meta ~= nil and player_meta.sound_handler ~= nil then + minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0) + player_meta.sound_handler = nil + player_meta.sound_updated = nil + end end -- callback function for removing rain mcl_weather.rain.clear = function() - mcl_weather.rain.raining = false - mcl_weather.rain.sky_last_update = -1 - mcl_weather.rain.init_done = false - mcl_weather.rain.set_particles_mode("rain") - mcl_weather.skycolor.remove_layer("weather-pack-rain-sky") - for _, player in ipairs(minetest.get_connected_players()) do - mcl_weather.rain.remove_sound(player) - mcl_weather.rain.remove_player(player) - end + mcl_weather.rain.raining = false + mcl_weather.rain.sky_last_update = -1 + mcl_weather.rain.init_done = false + mcl_weather.rain.set_particles_mode("rain") + mcl_weather.skycolor.remove_layer("weather-pack-rain-sky") + for _, player in ipairs(minetest.get_connected_players()) do + mcl_weather.rain.remove_sound(player) + mcl_weather.rain.remove_player(player) + end end minetest.register_globalstep(function(dtime) - if mcl_weather.state ~= "rain" then - return false - end + if mcl_weather.state ~= "rain" then + return false + end - mcl_weather.rain.make_weather() + mcl_weather.rain.make_weather() end) mcl_weather.rain.make_weather = function() - if mcl_weather.rain.init_done == false then - mcl_weather.rain.raining = true - mcl_weather.rain.set_sky_box() - mcl_weather.rain.set_particles_mode(mcl_weather.mode) - mcl_weather.rain.init_done = true - end + if mcl_weather.rain.init_done == false then + mcl_weather.rain.raining = true + mcl_weather.rain.set_sky_box() + mcl_weather.rain.set_particles_mode(mcl_weather.mode) + mcl_weather.rain.init_done = true + end - for _, player in ipairs(minetest.get_connected_players()) do - if (mcl_weather.is_underwater(player) or not mcl_worlds.has_weather(player:get_pos())) then - mcl_weather.rain.remove_sound(player) - return false - end - mcl_weather.rain.add_player(player) - mcl_weather.rain.add_rain_particles(player) - mcl_weather.rain.update_sound(player) - end + for _, player in ipairs(minetest.get_connected_players()) do + if (mcl_weather.is_underwater(player) or not mcl_worlds.has_weather(player:get_pos())) then + mcl_weather.rain.remove_sound(player) + return false + end + mcl_weather.rain.add_player(player) + mcl_weather.rain.add_rain_particles(player) + mcl_weather.rain.update_sound(player) + end end -- Switch the number of raindrops: "thunder" for many raindrops, otherwise for normal raindrops mcl_weather.rain.set_particles_mode = function(mode) - if mode == "thunder" then - mcl_weather.rain.particles_count = PARTICLES_COUNT_THUNDER - else - mcl_weather.rain.particles_count = PARTICLES_COUNT_RAIN - end + if mode == "thunder" then + mcl_weather.rain.particles_count = PARTICLES_COUNT_THUNDER + else + mcl_weather.rain.particles_count = PARTICLES_COUNT_RAIN + end end if mcl_weather.allow_abm then @@ -266,16 +266,16 @@ if mcl_weather.allow_abm then end if mcl_weather.reg_weathers.rain == nil then - mcl_weather.reg_weathers.rain = { - clear = mcl_weather.rain.clear, - light_factor = 0.6, - -- 10min - 20min - min_duration = 600, - max_duration = 1200, - transitions = { - [65] = "none", - [70] = "snow", - [100] = "thunder", - } - } + mcl_weather.reg_weathers.rain = { + clear = mcl_weather.rain.clear, + light_factor = 0.6, + -- 10min - 20min + min_duration = 600, + max_duration = 1200, + transitions = { + [65] = "none", + [70] = "snow", + [100] = "thunder", + } + } end diff --git a/mods/ENVIRONMENT/mcl_weather/snow.lua b/mods/ENVIRONMENT/mcl_weather/snow.lua index 7adfd0d54..986d38d43 100644 --- a/mods/ENVIRONMENT/mcl_weather/snow.lua +++ b/mods/ENVIRONMENT/mcl_weather/snow.lua @@ -81,17 +81,16 @@ end) -- register snow weather if mcl_weather.reg_weathers.snow == nil then - mcl_weather.reg_weathers.snow = { - clear = mcl_weather.snow.clear, - light_factor = 0.6, - -- 10min - 20min - min_duration = 600, - max_duration = 1200, - transitions = { - [65] = "none", - [80] = "rain", - [100] = "thunder", - } -} + mcl_weather.reg_weathers.snow = { + clear = mcl_weather.snow.clear, + light_factor = 0.6, + -- 10min - 20min + min_duration = 600, + max_duration = 1200, + transitions = { + [65] = "none", + [80] = "rain", + [100] = "thunder", + } + } end - diff --git a/mods/ENVIRONMENT/mcl_weather/weather_core.lua b/mods/ENVIRONMENT/mcl_weather/weather_core.lua index fb90aa740..365f6e549 100644 --- a/mods/ENVIRONMENT/mcl_weather/weather_core.lua +++ b/mods/ENVIRONMENT/mcl_weather/weather_core.lua @@ -6,8 +6,8 @@ mcl_weather.state = "none" -- player list for saving player meta info mcl_weather.players = {} --- default weather recalculation interval -mcl_weather.check_interval = 300 +-- default weather check interval for global step +mcl_weather.check_interval = 5 -- weather min duration mcl_weather.min_duration = 600 @@ -25,14 +25,14 @@ mcl_weather.reg_weathers = {} mcl_weather.allow_abm = true mcl_weather.reg_weathers["none"] = { - min_duration = mcl_weather.min_duration, - max_duration = mcl_weather.max_duration, - light_factor = nil, - transitions = { - [50] = "rain", - [100] = "snow", - }, - clear = function() end, + min_duration = mcl_weather.min_duration, + max_duration = mcl_weather.max_duration, + light_factor = nil, + transitions = { + [50] = "rain", + [100] = "snow", + }, + clear = function() end, } local storage = minetest.get_mod_storage() @@ -45,211 +45,230 @@ end minetest.register_on_shutdown(save_weather) mcl_weather.get_rand_end_time = function(min_duration, max_duration) - local r - if min_duration ~= nil and max_duration ~= nil then - r = math.random(min_duration, max_duration); - else - r = math.random(mcl_weather.min_duration, mcl_weather.max_duration); - end - return minetest.get_gametime() + r + local r + if min_duration ~= nil and max_duration ~= nil then + r = math.random(min_duration, max_duration) + else + r = math.random(mcl_weather.min_duration, mcl_weather.max_duration) + end + return minetest.get_gametime() + r end mcl_weather.get_current_light_factor = function() - if mcl_weather.state == "none" then - return nil - else - return mcl_weather.reg_weathers[mcl_weather.state].light_factor - end + if mcl_weather.state == "none" then + return nil + else + return mcl_weather.reg_weathers[mcl_weather.state].light_factor + end end -- Returns true if pos is outdoor. -- Outdoor is defined as any node in the Overworld under open sky. -- FIXME: Nodes below glass also count as “outdoor”, this should not be the case. mcl_weather.is_outdoor = function(pos) - local cpos = {x=pos.x, y=pos.y+1, z=pos.z} - local dim = mcl_worlds.pos_to_dimension(cpos) - if minetest.get_node_light(cpos, 0.5) == 15 and dim == "overworld" then - return true - end - return false + local cpos = {x=pos.x, y=pos.y+1, z=pos.z} + local dim = mcl_worlds.pos_to_dimension(cpos) + if minetest.get_node_light(cpos, 0.5) == 15 and dim == "overworld" then + return true + end + return false end -- checks if player is undewater. This is needed in order to -- turn off weather particles generation. mcl_weather.is_underwater = function(player) - local ppos = player:get_pos() - local offset = player:get_eye_offset() - local player_eye_pos = {x = ppos.x + offset.x, - y = ppos.y + offset.y + 1.5, - z = ppos.z + offset.z} - local node_level = minetest.get_node_level(player_eye_pos) - if node_level == 8 or node_level == 7 then - return true - end - return false + local ppos = player:get_pos() + local offset = player:get_eye_offset() + local player_eye_pos = {x = ppos.x + offset.x, + y = ppos.y + offset.y + 1.5, + z = ppos.z + offset.z} + local node_level = minetest.get_node_level(player_eye_pos) + if node_level == 8 or node_level == 7 then + return true + end + return false end -- trying to locate position for particles by player look direction for performance reason. -- it is costly to generate many particles around player so goal is focus mainly on front view. mcl_weather.get_random_pos_by_player_look_dir = function(player) - local look_dir = player:get_look_dir() - local player_pos = player:get_pos() + local look_dir = player:get_look_dir() + local player_pos = player:get_pos() - local random_pos_x = 0 - local random_pos_y = 0 - local random_pos_z = 0 + local random_pos_x = 0 + local random_pos_y = 0 + local random_pos_z = 0 - if look_dir.x > 0 then - if look_dir.z > 0 then - random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 5) - random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 5) - else - random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 5) - random_pos_z = math.random() + math.random(player_pos.z - 5, player_pos.z + 2.5) - end - else - if look_dir.z > 0 then - random_pos_x = math.random() + math.random(player_pos.x - 5, player_pos.x + 2.5) - random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 5) - else - random_pos_x = math.random() + math.random(player_pos.x - 5, player_pos.x + 2.5) - random_pos_z = math.random() + math.random(player_pos.z - 5, player_pos.z + 2.5) - end - end + if look_dir.x > 0 then + if look_dir.z > 0 then + random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 5) + random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 5) + else + random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 5) + random_pos_z = math.random() + math.random(player_pos.z - 5, player_pos.z + 2.5) + end + else + if look_dir.z > 0 then + random_pos_x = math.random() + math.random(player_pos.x - 5, player_pos.x + 2.5) + random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 5) + else + random_pos_x = math.random() + math.random(player_pos.x - 5, player_pos.x + 2.5) + random_pos_z = math.random() + math.random(player_pos.z - 5, player_pos.z + 2.5) + end + end - random_pos_y = math.random() + math.random(player_pos.y + 10, player_pos.y + 15) - return random_pos_x, random_pos_y, random_pos_z + random_pos_y = math.random() + math.random(player_pos.y + 10, player_pos.y + 15) + return random_pos_x, random_pos_y, random_pos_z end +local t, wci = 0, mcl_weather.check_interval minetest.register_globalstep(function(dtime) - if mcl_weather.end_time == nil then - mcl_weather.end_time = mcl_weather.get_rand_end_time() - end - -- recalculate weather - if mcl_weather.end_time <= minetest.get_gametime() then - local changeWeather = minetest.settings:get_bool("mcl_doWeatherCycle") - if changeWeather == nil then - changeWeather = true - end - if changeWeather then - mcl_weather.set_random_weather(mcl_weather.state, mcl_weather.reg_weathers[mcl_weather.state]) - else - mcl_weather.end_time = mcl_weather.get_rand_end_time() - end - end + t = t + dtime + if t < wci then return end + t = 0 + + if mcl_weather.end_time == nil then + mcl_weather.end_time = mcl_weather.get_rand_end_time() + end + -- recalculate weather + if mcl_weather.end_time <= minetest.get_gametime() then + local changeWeather = minetest.settings:get_bool("mcl_doWeatherCycle") + if changeWeather == nil then + changeWeather = true + end + if changeWeather then + mcl_weather.set_random_weather(mcl_weather.state, mcl_weather.reg_weathers[mcl_weather.state]) + else + mcl_weather.end_time = mcl_weather.get_rand_end_time() + end + end end) -- Sets random weather (which could be 'none' (no weather)). mcl_weather.set_random_weather = function(weather_name, weather_meta) - if (weather_meta ~= nil) then - local transitions = weather_meta.transitions - local random_roll = math.random(0,100) - local new_weather - for v, weather in pairs(transitions) do - if random_roll < v then - new_weather = weather - break - end - end - if new_weather then - mcl_weather.change_weather(new_weather) - end - end + if weather_meta == nil then return end + local transitions = weather_meta.transitions + local random_roll = math.random(0,100) + local new_weather + for v, weather in pairs(transitions) do + if random_roll < v then + new_weather = weather + break + end + end + if new_weather then + mcl_weather.change_weather(new_weather) + end end -- Change weather to new_weather. -- * explicit_end_time is OPTIONAL. If specified, explicitly set the -- gametime (minetest.get_gametime) in which the weather ends. -mcl_weather.change_weather = function(new_weather, explicit_end_time) - if (mcl_weather.reg_weathers ~= nil and mcl_weather.reg_weathers[new_weather] ~= nil) then - if (mcl_weather.state ~= nil and mcl_weather.reg_weathers[mcl_weather.state] ~= nil) then - mcl_weather.reg_weathers[mcl_weather.state].clear() - end - mcl_weather.state = new_weather - local weather_meta = mcl_weather.reg_weathers[mcl_weather.state] - if explicit_end_time then - mcl_weather.end_time = explicit_end_time - else - mcl_weather.end_time = mcl_weather.get_rand_end_time(weather_meta.min_duration, weather_meta.max_duration) - end - mcl_weather.skycolor.update_sky_color() - save_weather() - return true - end - return false +-- * changer is OPTIONAL, for logging purposes. +mcl_weather.change_weather = function(new_weather, explicit_end_time, changer_name) + local changer_name = changer_name or debug.getinfo(2).name.."()" + + if (mcl_weather.reg_weathers ~= nil and mcl_weather.reg_weathers[new_weather] ~= nil) then + if (mcl_weather.state ~= nil and mcl_weather.reg_weathers[mcl_weather.state] ~= nil) then + mcl_weather.reg_weathers[mcl_weather.state].clear() + end + + local old_weather = mcl_weather.state + + mcl_weather.state = new_weather + + if old_weather == "none" then + old_weather = "clear" + end + if new_weather == "none" then + new_weather = "clear" + end + minetest.log("action", "[mcl_weather] " .. changer_name .. " changed the weather from " .. old_weather .. " to " .. new_weather) + + local weather_meta = mcl_weather.reg_weathers[mcl_weather.state] + if explicit_end_time then + mcl_weather.end_time = explicit_end_time + else + mcl_weather.end_time = mcl_weather.get_rand_end_time(weather_meta.min_duration, weather_meta.max_duration) + end + mcl_weather.skycolor.update_sky_color() + save_weather() + return true + end + return false end mcl_weather.get_weather = function() - return mcl_weather.state + return mcl_weather.state end minetest.register_privilege("weather_manager", { - description = S("Gives ability to control weather"), - give_to_singleplayer = false + description = S("Gives ability to control weather"), + give_to_singleplayer = false }) -- Weather command definition. Set minetest.register_chatcommand("weather", { - params = "(clear | rain | snow | thunder) []", - description = S("Changes the weather to the specified parameter."), - privs = {weather_manager = true}, - func = function(name, param) - if (param == "") then - return false, S("Error: No weather specified.") - end - local new_weather, end_time - local parse1, parse2 = string.match(param, "(%w+) ?(%d*)") - if parse1 then - if parse1 == "clear" then - new_weather = "none" - else - new_weather = parse1 - end - else - return false, S("Error: Invalid parameters.") - end - if parse2 then - if type(tonumber(parse2)) == "number" then - local duration = tonumber(parse2) - if duration < 1 then - return false, S("Error: Duration can't be less than 1 second.") - end - end_time = minetest.get_gametime() + duration - end - end + params = "(clear | rain | snow | thunder) []", + description = S("Changes the weather to the specified parameter."), + privs = {weather_manager = true}, + func = function(name, param) + if (param == "") then + return false, S("Error: No weather specified.") + end + local new_weather, end_time + local parse1, parse2 = string.match(param, "(%w+) ?(%d*)") + if parse1 then + if parse1 == "clear" then + new_weather = "none" + else + new_weather = parse1 + end + else + return false, S("Error: Invalid parameters.") + end + if parse2 then + if type(tonumber(parse2)) == "number" then + local duration = tonumber(parse2) + if duration < 1 then + return false, S("Error: Duration can't be less than 1 second.") + end + end_time = minetest.get_gametime() + duration + end + end - local success = mcl_weather.change_weather(new_weather, end_time) - if success then - return true - else - return false, S("Error: Invalid weather specified. Use “clear”, “rain”, “snow” or “thunder”.") - end - end + local success = mcl_weather.change_weather(new_weather, end_time, name) + if success then + return true + else + return false, S("Error: Invalid weather specified. Use “clear”, “rain”, “snow” or “thunder”.") + end + end }) minetest.register_chatcommand("toggledownfall", { - params = "", - description = S("Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)"), - privs = {weather_manager = true}, - func = function(name, param) - -- Currently rain/thunder/snow: Set weather to clear - if mcl_weather.state ~= "none" then - return mcl_weather.change_weather("none") + params = "", + description = S("Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)"), + privs = {weather_manager = true}, + func = function(name, param) + -- Currently rain/thunder/snow: Set weather to clear + if mcl_weather.state ~= "none" then + return mcl_weather.change_weather("none", nil, name) - -- Currently clear: Set weather randomly to rain/thunder/snow - else - local new = { "rain", "thunder", "snow" } - local r = math.random(1, #new) - return mcl_weather.change_weather(new[r]) - end - end + -- Currently clear: Set weather randomly to rain/thunder/snow + else + local new = { "rain", "thunder", "snow" } + local r = math.random(1, #new) + return mcl_weather.change_weather(new[r], nil, name) + end + end }) -- Configuration setting which allows user to disable ABM for weathers (if they use it). -- Weather mods expected to be use this flag before registering ABM. local weather_allow_abm = minetest.settings:get_bool("weather_allow_abm") if weather_allow_abm ~= nil and weather_allow_abm == false then - mcl_weather.allow_abm = false + mcl_weather.allow_abm = false end diff --git a/mods/HELP/mcl_craftguide/init.lua b/mods/HELP/mcl_craftguide/init.lua index 86bd742a9..eb98bcce0 100644 --- a/mods/HELP/mcl_craftguide/init.lua +++ b/mods/HELP/mcl_craftguide/init.lua @@ -1109,7 +1109,13 @@ if progressive_mode then local name = player:get_player_name() local data = player_data[name] - meta:set_string("inv_items", serialize(data.inv_items)) + if not data then + return + end + + local inv_items = data.inv_items or {} + + meta:set_string("inv_items", serialize(inv_items)) end M.register_on_leaveplayer(function(player) diff --git a/mods/HUD/mcl_experience/init.lua b/mods/HUD/mcl_experience/init.lua index 048bd43c6..ff5647616 100644 --- a/mods/HUD/mcl_experience/init.lua +++ b/mods/HUD/mcl_experience/init.lua @@ -77,6 +77,8 @@ hud_manager.add_hud = function(player,hud_name,def) size = def.size, offset = def.offset, z_index = def.z_index, + alignment = def.alignment, + scale = def.scale, }) -- create new 3d array here -- depends.txt is not needed @@ -148,7 +150,7 @@ function mcl_experience.set_player_xp_level(player,level) return end pool[name].level = level - pool[name].xp, pool[name].bar_step, pool[name].next_level = mcl_experience.bar_to_xp(pool[name].bar, level) + pool[name].xp, pool[name].bar_step, pool[name].xp_next_level = mcl_experience.bar_to_xp(pool[name].bar, level) hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(level)}) -- we may don't update the bar end @@ -161,27 +163,27 @@ minetest.register_on_joinplayer(function(player) name = player:get_player_name() temp_pool = pool[name] - + hud_manager.add_hud(player,"experience_bar", { - hud_elem_type = "statbar", position = {x=0.5, y=1}, - name = "experience bar", - text = "experience_bar.png", - text2 = "experience_bar_background.png", - number = temp_pool.bar, item = 36, - direction = 0, - offset = {x = (-8 * 28) - 29, y = -(48 + 24 + 16)}, - size = { x=28, y=28 }, z_index = 11, + hud_elem_type = "image", + name = "experience bar", + text = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270", + position = {x=0.5, y=1}, + offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)}, + scale = {x = 2.8, y = 3.0}, + alignment = { x = 1, y = 1 }, + z_index = 11, }) - + hud_manager.add_hud(player,"xp_level", { hud_elem_type = "text", position = {x=0.5, y=1}, name = "xp_level", text = tostring(temp_pool.level), - number = 0xFFFFFF, + number = 0x80FF20, offset = {x = 0, y = -(48 + 24 + 24)}, z_index = 12, - }) + }) end) function mcl_experience.xp_to_level(xp) @@ -247,7 +249,7 @@ function mcl_experience.add_experience(player, experience) end if old_bar ~= temp_pool.bar then - hud_manager.change_hud({player = player, hud_name = "experience_bar", element = "number", data = math.floor(temp_pool.bar)}) + hud_manager.change_hud({player = player, hud_name = "experience_bar", element = "text", data = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",}) end if experience > 0 and minetest.get_us_time()/1000000 - temp_pool.last_time > 0.01 then @@ -277,13 +279,13 @@ minetest.register_on_dieplayer(function(player) name = player:get_player_name() temp_pool = pool[name] xp_amount = temp_pool.xp - + temp_pool.xp = 0 temp_pool.level = 0 temp_pool.bar, temp_pool.bar_step, temp_pool.xp_next_level = mcl_experience.xp_to_bar(temp_pool.xp, temp_pool.level) hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(temp_pool.level)}) - hud_manager.change_hud({player = player, hud_name = "experience_bar", element = "number", data = math.floor(temp_pool.bar)}) + hud_manager.change_hud({player = player, hud_name = "experience_bar", element = "text", data = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",}) mcl_experience.throw_experience(player:get_pos(), xp_amount) end) @@ -304,17 +306,17 @@ local function xp_step(self, dtime) return end collector = minetest.get_player_by_name(self.collector) - if collector and collector:get_hp() > 0 and vector.distance(self.object:get_pos(),collector:get_pos()) < 5 then + if collector and collector:get_hp() > 0 and vector.distance(self.object:get_pos(),collector:get_pos()) < 7.25 then self.object:set_acceleration(vector.new(0,0,0)) self.disable_physics(self) --get the variables pos = self.object:get_pos() pos2 = collector:get_pos() - - player_velocity = collector:get_player_velocity() - + + player_velocity = collector:get_velocity() or collector:get_player_velocity() + pos2.y = pos2.y + 0.8 - + direction = vector.direction(pos,pos2) distance = vector.distance(pos2,pos) multiplier = distance @@ -330,8 +332,79 @@ local function xp_step(self, dtime) goal = velocity acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z) self.object:add_velocity(vector.add(acceleration,player_velocity)) - elseif distance < 0.4 then - mcl_experience.add_experience(collector, self._xp) + elseif distance < 0.8 then + local xp = self._xp + local inv = collector:get_inventory() + local candidates = { + {list = "main", index = collector:get_wield_index()}, + {list = "armor", index = 2}, + {list = "armor", index = 3}, + {list = "armor", index = 4}, + {list = "armor", index = 5}, + } + local final_candidates = {} + for _, can in ipairs(candidates) do + local stack = inv:get_stack(can.list, can.index) + local wear = stack:get_wear() + if mcl_enchanting.has_enchantment(stack, "mending") and wear > 0 then + can.stack = stack + can.wear = wear + table.insert(final_candidates, can) + end + end + if #final_candidates > 0 then + local can = final_candidates[math.random(#final_candidates)] + local stack, list, index, wear = can.stack, can.list, can.index, can.wear + local unbreaking_level = mcl_enchanting.get_enchantment(stack, "unbreaking") + local uses + local armor_uses = minetest.get_item_group(stack:get_name(), "mcl_armor_uses") + if armor_uses > 0 then + uses = armor_uses + if unbreaking_level > 0 then + uses = uses / (0.6 + 0.4 / (unbreaking_level + 1)) + end + else + local def = stack:get_definition() + if def then + local fixed_uses = def._mcl_uses + if fixed_uses then + uses = fixed_uses + if unbreaking_level > 0 then + uses = uses * (unbreaking_level + 1) + end + end + end + if not uses then + local toolcaps = stack:get_tool_capabilities() + local groupcaps = toolcaps.groupcaps + for _, v in pairs(groupcaps) do + uses = v.uses + break + end + end + end + uses = uses or 0 + local multiplier = 2 * 65535 / uses + local repair = xp * multiplier + local new_wear = wear - repair + if new_wear < 0 then + xp = math.floor(-new_wear / multiplier + 0.5) + new_wear = 0 + else + xp = 0 + end + stack:set_wear(math.floor(new_wear)) + inv:set_stack(list, index, stack) + if can.list == "armor" then + local armor_inv = minetest.get_inventory({type = "detached", name = collector:get_player_name() .. "_armor"}) + armor_inv:set_stack(list, index, stack) + end + end + if xp > 0 then + mcl_experience.add_experience(collector, xp) + else + minetest.sound_play("experience",{gain=0.1,to_player = name,pitch=math.random(75,99)/100}) + end self.object:remove() end return @@ -341,7 +414,7 @@ local function xp_step(self, dtime) end end - + self.age = self.age + dtime if self.age > max_orb_age then self.object:remove() @@ -526,11 +599,55 @@ function mcl_experience.throw_experience(pos, amount) return false end obj:set_velocity({ - x=math.random(-2,2)*math.random(), - y=math.random(2,5), + x=math.random(-2,2)*math.random(), + y=math.random(2,5), z=math.random(-2,2)*math.random() }) i = i + xp j = j + 1 end end + +minetest.register_entity("mcl_experience:bottle",{ + textures = {"mcl_experience_bottle.png"}, + hp_max = 1, + visual_size = {x = 0.35, y = 0.35}, + collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1}, + pointable = false, + on_step = function(self, dtime) + local pos = self.object:get_pos() + local node = minetest.get_node(pos) + local n = node.name + if n ~= "air" and n ~= "mcl_portals:portal" and n ~= "mcl_portals:portal_end" and minetest.get_node_group(n, "liquid") == 0 then + minetest.sound_play("mcl_potions_breaking_glass", {pos = pos, max_hear_distance = 16, gain = 1}) + mcl_experience.throw_experience(pos, math.random(3, 11)) + self.object:remove() + end + end, +}) + +local function throw_xp_bottle(pos, dir, velocity) + minetest.sound_play("mcl_throwing_throw", {pos = pos, gain = 0.4, max_hear_distance = 16}, true) + local obj = minetest.add_entity(pos, "mcl_experience:bottle") + obj:set_velocity(vector.multiply(dir, velocity)) + local acceleration = vector.multiply(dir, -3) + acceleration.y = -9.81 + obj:set_acceleration(acceleration) +end + +minetest.register_craftitem("mcl_experience:bottle", { + description = "Bottle o' Enchanting", + inventory_image = "mcl_experience_bottle.png", + wield_image = "mcl_experience_bottle.png", + stack_max = 64, + on_use = function(itemstack, placer, pointed_thing) + throw_xp_bottle(vector.add(placer:get_pos(), vector.new(0, 1.5, 0)), placer:get_look_dir(), 10) + if not minetest.is_creative_enabled(placer:get_player_name()) then + itemstack:take_item() + end + return itemstack + end, + _on_dispense = function(_, pos, _, _, dir) + throw_xp_bottle(vector.add(pos, vector.multiply(dir, 0.51)), dir, 10) + end +}) diff --git a/mods/HUD/mcl_experience/locale/mcl_experience.de.tr b/mods/HUD/mcl_experience/locale/mcl_experience.de.tr new file mode 100644 index 000000000..351cf1911 --- /dev/null +++ b/mods/HUD/mcl_experience/locale/mcl_experience.de.tr @@ -0,0 +1,7 @@ +# textdomain: mcl_experience +[[] ]=[[] ] +Gives a player some XP=Gibt einen Spieler ein paar EP +Error: Too many parameters!=Fehler: Zu viele Parameter! +Error: Incorrect value of XP=Fehler: Ungültiger EP-Wert +Error: Player not found=Fehler: Spieler nicht gefunden +Added @1 XP to @2, total: @3, experience level: @4=@1 EP an @2 gegeben, gesamt: @3, Erfahrungsstufe: @4 diff --git a/mods/HUD/mcl_experience/locale/mcl_experience.ru.tr b/mods/HUD/mcl_experience/locale/mcl_experience.ru.tr index 42cc9ec43..a87840aff 100644 --- a/mods/HUD/mcl_experience/locale/mcl_experience.ru.tr +++ b/mods/HUD/mcl_experience/locale/mcl_experience.ru.tr @@ -1,3 +1,4 @@ +# textdomain: mcl_experience [[] ]=[[<игрок>] ] Gives a player some XP=Даёт игроку XP Error: Too many parameters!=Ошибка: слишком много параметров! diff --git a/mods/HUD/mcl_experience/locale/mlc_experience.fr.tr b/mods/HUD/mcl_experience/locale/mlc_experience.fr.tr index a186b549b..0644e2596 100644 --- a/mods/HUD/mcl_experience/locale/mlc_experience.fr.tr +++ b/mods/HUD/mcl_experience/locale/mlc_experience.fr.tr @@ -1,3 +1,4 @@ +# textdomain: mcl_experience [[] ]=[[] ] Gives a player some XP=Donne de l'XP à un joueur Error: Too many parameters!=Erreur: Trop de paramètres! diff --git a/mods/HUD/mcl_experience/locale/template.txt b/mods/HUD/mcl_experience/locale/template.txt index 8494504e9..a355cbbac 100644 --- a/mods/HUD/mcl_experience/locale/template.txt +++ b/mods/HUD/mcl_experience/locale/template.txt @@ -1,7 +1,7 @@ +# textdomain: mcl_experience [[] ]= Gives a player some XP= Error: Too many parameters!= Error: Incorrect value of XP= Error: Player not found= Added @1 XP to @2, total: @3, experience level: @4= -XP are disabled!= diff --git a/mods/HUD/mcl_experience/textures/experience_bar.png b/mods/HUD/mcl_experience/textures/experience_bar.png index d43a03d64..19a2c029d 100644 Binary files a/mods/HUD/mcl_experience/textures/experience_bar.png and b/mods/HUD/mcl_experience/textures/experience_bar.png differ diff --git a/mods/HUD/mcl_experience/textures/experience_bar_background.png b/mods/HUD/mcl_experience/textures/experience_bar_background.png index 0e6b59763..f28a0e0dd 100644 Binary files a/mods/HUD/mcl_experience/textures/experience_bar_background.png and b/mods/HUD/mcl_experience/textures/experience_bar_background.png differ diff --git a/mods/HUD/mcl_experience/textures/mcl_experience_bottle.png b/mods/HUD/mcl_experience/textures/mcl_experience_bottle.png new file mode 100644 index 000000000..51b6e3406 Binary files /dev/null and b/mods/HUD/mcl_experience/textures/mcl_experience_bottle.png differ diff --git a/mods/HUD/mcl_inventory/creative.lua b/mods/HUD/mcl_inventory/creative.lua index a63c85548..f762b9d66 100644 --- a/mods/HUD/mcl_inventory/creative.lua +++ b/mods/HUD/mcl_inventory/creative.lua @@ -16,6 +16,18 @@ for _, f in pairs(builtin_filter_ids) do inventory_lists[f] = {} end +local function replace_enchanted_books(tbl) + for k, item in ipairs(tbl) do + if item:find("mcl_enchanting:book_enchanted") == 1 then + local _, enchantment, level = item:match("(%a+) ([_%w]+) (%d+)") + level = level and tonumber(level) + if enchantment and level then + tbl[k] = mcl_enchanting.enchant(ItemStack("mcl_enchanting:book_enchanted"), enchantment, level) + end + end + end +end + --[[ Populate all the item tables. We only do this once. Note this mod must be loaded after _mcl_autogroup for this to work, because it required certain groups to be set. ]] @@ -82,11 +94,33 @@ do end end + for ench, def in pairs(mcl_enchanting.enchantments) do + local str = "mcl_enchanting:book_enchanted " .. ench .. " " .. def.max_level + if def.inv_tool_tab then + table.insert(inventory_lists["tools"], str) + end + if def.inv_combat_tab then + table.insert(inventory_lists["combat"], str) + end + table.insert(inventory_lists["all"], str) + end + for _, to_sort in pairs(inventory_lists) do table.sort(to_sort) + replace_enchanted_books(to_sort) end end +local function filter_item(name, description, lang, filter) + local desc + if not lang then + desc = string.lower(description) + else + desc = string.lower(minetest.get_translated_string(lang, description)) + end + return string.find(name, filter) or string.find(desc, filter) +end + local function set_inv_search(filter, player) local playername = player:get_player_name() local inv = minetest.get_inventory({type="detached", name="creative_"..playername}) @@ -94,19 +128,21 @@ local function set_inv_search(filter, player) local lang = minetest.get_player_information(playername).lang_code for name,def in pairs(minetest.registered_items) do if (not def.groups.not_in_creative_inventory or def.groups.not_in_creative_inventory == 0) and def.description and def.description ~= "" then - local name = string.lower(def.name) - local desc - if not lang then - desc = string.lower(def.description) - else - desc = string.lower(minetest.get_translated_string(lang, def.description)) - end - if string.find(name, filter) or string.find(desc, filter) then + if filter_item(string.lower(def.name), def.description, lang, filter) then table.insert(creative_list, name) end end end + for ench, def in pairs(mcl_enchanting.enchantments) do + for i = 1, def.max_level do + local stack = mcl_enchanting.enchant(ItemStack("mcl_enchanting:book_enchanted"), ench, i) + if filter_item("mcl_enchanting:book_enchanted", minetest.strip_colors(stack:get_description()), lang, filter) then + table.insert(creative_list, "mcl_enchanting:book_enchanted " .. ench .. " " .. i) + end + end + end table.sort(creative_list) + replace_enchanted_books(creative_list) inv:set_size("main", #creative_list) inv:set_list("main", creative_list) @@ -589,7 +625,9 @@ if minetest.is_creative_enabled("") then function minetest.handle_node_drops(pos, drops, digger) if not digger or not digger:is_player() then - return + for _,item in ipairs(drops) do + minetest.add_item(pos, item) + end end local inv = digger:get_inventory() if inv then diff --git a/mods/HUD/mcl_inventory/init.lua b/mods/HUD/mcl_inventory/init.lua index a4bd0e167..c6f97185d 100644 --- a/mods/HUD/mcl_inventory/init.lua +++ b/mods/HUD/mcl_inventory/init.lua @@ -141,7 +141,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) return_fields(player,"craft") return_fields(player,"enchanting_lapis") return_fields(player,"enchanting_item") - mcl_enchanting.reload_inventory(player) if not minetest.is_creative_enabled(player:get_player_name()) and (formname == "" or formname == "main") then set_inventory(player) end diff --git a/mods/HUD/mcl_tmp_message/init.lua b/mods/HUD/mcl_tmp_message/init.lua new file mode 100644 index 000000000..1456cd592 --- /dev/null +++ b/mods/HUD/mcl_tmp_message/init.lua @@ -0,0 +1,44 @@ +mcl_tmp_message = {} + +local huds = {} +local hud_hide_timeouts = {} + +function mcl_tmp_message.message(player, message) + local name = player:get_player_name() + player:hud_change(huds[name], "text", message) + hud_hide_timeouts[name] = 3 +end + +minetest.register_on_joinplayer(function(player) + huds[player:get_player_name()] = player:hud_add({ + hud_elem_type = "text", + position = {x=0.5, y=1}, + offset = {x = 0, y = -210}, + alignment = {x=0, y=0}, + number = 0xFFFFFF , + text = "", + z_index = 100, + }) +end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + huds[name] = nil + hud_hide_timeouts[name] = nil +end) + +minetest.register_globalstep(function(dtime) + local new_timeouts = {} + for name, timeout in pairs(hud_hide_timeouts) do + timeout = timeout - dtime + if timeout <= 0 then + local player = minetest.get_player_by_name(name) + if player then + player:hud_change(huds[name], "text", "") + end + else + new_timeouts[name] = timeout + end + end + hud_hide_timeouts = new_timeouts +end) diff --git a/mods/ITEMS/REDSTONE/mcl_observers/init.lua b/mods/ITEMS/REDSTONE/mcl_observers/init.lua index 841ab98b4..932f4f643 100644 --- a/mods/ITEMS/REDSTONE/mcl_observers/init.lua +++ b/mods/ITEMS/REDSTONE/mcl_observers/init.lua @@ -24,7 +24,7 @@ local rules_up = {{ x = 0, y = -1, z = 0, spread = true }} function mcl_observers.observer_activate(pos) minetest.after(mcl_vars.redstone_tick, function(pos) - node = minetest.get_node(pos) + local node = minetest.get_node(pos) if not node then return end @@ -284,6 +284,7 @@ if realtime then mcl_observers.set_node = minetest.set_node mcl_observers.swap_node = minetest.swap_node mcl_observers.remove_node = minetest.remove_node + mcl_observers.bulk_set_node = minetest.bulk_set_node minetest.add_node=function(pos,node) mcl_observers.add_node(pos,node) @@ -393,6 +394,35 @@ if realtime then mcl_observers.observer_activate({x=pos.x,y=pos.y+1,z=pos.z}) end end + minetest.bulk_set_node=function(lst, node) + mcl_observers.bulk_set_node(lst, node) + for _, pos in pairs(lst) do + local n=minetest.get_node({x=pos.x+1,y=pos.y,z=pos.z}) + if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_o" and minetest.facedir_to_dir(n.param2).x==-1 then + mcl_observers.observer_activate({x=pos.x+1,y=pos.y,z=pos.z}) + end + n=minetest.get_node({x=pos.x-1,y=pos.y,z=pos.z}) + if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_o" and minetest.facedir_to_dir(n.param2).x==1 then + mcl_observers.observer_activate({x=pos.x-1,y=pos.y,z=pos.z}) + end + n=minetest.get_node({x=pos.x,y=pos.y,z=pos.z+1}) + if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_o" and minetest.facedir_to_dir(n.param2).z==-1 then + mcl_observers.observer_activate({x=pos.x,y=pos.y,z=pos.z+1}) + end + n=minetest.get_node({x=pos.x,y=pos.y,z=pos.z-1}) + if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_o" and minetest.facedir_to_dir(n.param2).z==1 then + mcl_observers.observer_activate({x=pos.x,y=pos.y,z=pos.z-1}) + end + n=minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}) + if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_u" then + mcl_observers.observer_activate({x=pos.x,y=pos.y-1,z=pos.z}) + end + n=minetest.get_node({x=pos.x,y=pos.y+1,z=pos.z}) + if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_d" then + mcl_observers.observer_activate({x=pos.x,y=pos.y+1,z=pos.z}) + end + end + end else -- if realtime then ^^^ else: minetest.register_abm({ diff --git a/mods/ITEMS/REDSTONE/mesecons_mvps/init.lua b/mods/ITEMS/REDSTONE/mesecons_mvps/init.lua index a9a11bce5..669edbcbb 100644 --- a/mods/ITEMS/REDSTONE/mesecons_mvps/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_mvps/init.lua @@ -145,7 +145,7 @@ function mesecon.mvps_get_stack(pos, dir, maximum, piston_pos) if not node_replaceable(nn.name) then if #nodes >= maximum then return nil, false end - table.insert(nodes, {node = nn, pos = np}) + table.insert(nodes, {node = nn, pos = {x=np.x, y=np.y, z=np.z}}) -- add connected nodes to frontiers, connected is a vector list -- the vectors must be absolute positions @@ -195,10 +195,9 @@ function mesecon.mvps_set_owner(pos, placer) end end -local function are_protected(positions, player_name) - local name = player_name - for _, pos in pairs(positions) do - if is_protected(pos, name) then +local function are_protected(nodes, player_name) + for _, node in pairs(nodes) do + if minetest.is_protected(node.pos, player_name) then return true end end diff --git a/mods/ITEMS/REDSTONE/mesecons_pistons/init.lua b/mods/ITEMS/REDSTONE/mesecons_pistons/init.lua index fba702ede..3230d9804 100644 --- a/mods/ITEMS/REDSTONE/mesecons_pistons/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_pistons/init.lua @@ -97,7 +97,7 @@ local piston_on = function (pos, node) local meta = minetest.get_meta(pos) local success, stack, oldstack = mesecon.mvps_push(np, dir, PISTON_MAXIMUM_PUSH, meta:get_string("owner"), pos) if success then - minetest.set_node(pos, {param2 = node.param2, name = pistonspec.onname}) + minetest.swap_node(pos, {param2 = node.param2, name = pistonspec.onname}) minetest.set_node(np, {param2 = node.param2, name = pistonspec.pusher}) local below = minetest.get_node({x=np.x,y=np.y-1,z=np.z}) if below.name == "mcl_farming:soil" or below.name == "mcl_farming:soil_wet" then @@ -115,7 +115,7 @@ end local piston_off = function (pos, node) local pistonspec = minetest.registered_nodes[node.name].mesecons_piston - minetest.add_node(pos, {param2 = node.param2, name = pistonspec.offname}) + minetest.swap_node(pos, {param2 = node.param2, name = pistonspec.offname}) piston_remove_pusher (pos, node) if not pistonspec.sticky then return diff --git a/mods/ITEMS/mcl_armor/armor.lua b/mods/ITEMS/mcl_armor/armor.lua index 69801d010..6151a2812 100644 --- a/mods/ITEMS/mcl_armor/armor.lua +++ b/mods/ITEMS/mcl_armor/armor.lua @@ -540,7 +540,7 @@ minetest.register_on_player_hpchange(function(player, hp_change, reason) epf = epf + blast_protection_level * 2 end local fire_protection_level = enchantments.fire_protection or 0 - if fire_protection_level > 0 and (damage_type == "fireball" or reason.type == "node_damage" and + if fire_protection_level > 0 and (damage_type == "burning" or damage_type == "fireball" or reason.type == "node_damage" and (reason.node == "mcl_fire:fire" or reason.node == "mcl_core:lava_source" or reason.node == "mcl_core:lava_flowing")) then epf = epf + fire_protection_level * 2 end diff --git a/mods/ITEMS/mcl_armor_stand/init.lua b/mods/ITEMS/mcl_armor_stand/init.lua index 88b46a346..c451b6de1 100644 --- a/mods/ITEMS/mcl_armor_stand/init.lua +++ b/mods/ITEMS/mcl_armor_stand/init.lua @@ -223,7 +223,7 @@ minetest.register_node("mcl_armor_stand:armor_stand", { after_destruct = function(pos) update_entity(pos) end, - on_blast = function(pos) + on_blast = function(pos, _, do_drop) local object = get_stand_object(pos) if object then object:remove() @@ -231,6 +231,10 @@ minetest.register_node("mcl_armor_stand:armor_stand", { minetest.after(1, function(pos) update_entity(pos) end, pos) + minetest.remove_node(pos) + if do_drop then + minetest.add_item(pos, "mcl_armor_stand:armor_stand") + end end, on_rotate = function(pos, node, user, mode) if mode == screwdriver.ROTATE_FACE then diff --git a/mods/ITEMS/mcl_beds/api.lua b/mods/ITEMS/mcl_beds/api.lua index aadb716b5..7bc86d175 100644 --- a/mods/ITEMS/mcl_beds/api.lua +++ b/mods/ITEMS/mcl_beds/api.lua @@ -1,23 +1,24 @@ local S = minetest.get_translator("mcl_beds") -local reverse = true - -local function destruct_bed(pos, is_top) - local node = minetest.get_node(pos) - local other +local function destruct_bed(pos, oldnode) + local node = oldnode or minetest.get_node(pos) + if not node then return end local dir = minetest.facedir_to_dir(node.param2) - if is_top then - other = vector.subtract(pos, dir) - else - other = vector.add(pos, dir) - end - - if reverse then - reverse = not reverse - minetest.remove_node(other) - minetest.check_for_falling(other) - else - reverse = not reverse + local pos2, node2 + if string.sub(node.name, -4) == "_top" then + pos2 = vector.subtract(pos, dir) + node2 = minetest.get_node(pos2) + if node2 and string.sub(node2.name, -7) == "_bottom" then + minetest.remove_node(pos2) + end + minetest.check_for_falling(pos) + elseif string.sub(node.name, -7) == "_bottom" then + minetest.add_item(pos, node.name) + pos2 = vector.add(pos, dir) + node2 = minetest.get_node(pos2) + if node2 and string.sub(node2.name, -4) == "_top" then + minetest.remove_node(pos2) + end end end @@ -86,6 +87,7 @@ function mcl_beds.register_bed(name, def) node_box = node_box_bottom, selection_box = selection_box_bottom, collision_box = collision_box_bottom, + drop = "", on_place = function(itemstack, placer, pointed_thing) local under = pointed_thing.under @@ -139,10 +141,9 @@ function mcl_beds.register_bed(name, def) return itemstack end, - on_destruct = function(pos) - destruct_bed(pos, false) - kick_player_after_destruct(pos) - end, + after_destruct = destruct_bed, + + on_destruct = kick_player_after_destruct, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) mcl_beds.on_rightclick(pos, clicker, false) @@ -205,7 +206,7 @@ function mcl_beds.register_bed(name, def) _mcl_hardness = 0.2, _mcl_blast_resistance = 1, sounds = def.sounds or default_sounds, - drop = name .. "_bottom", + drop = "", node_box = node_box_top, selection_box = selection_box_top, collision_box = collision_box_top, @@ -214,10 +215,7 @@ function mcl_beds.register_bed(name, def) return itemstack end, on_rotate = false, - on_destruct = function(pos) - destruct_bed(pos, true) - kick_player_after_destruct(pos) - end, + after_destruct = destruct_bed, }) minetest.register_alias(name, name .. "_bottom") diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index df1a2317b..8b1b82dfc 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -75,16 +75,19 @@ local function lay_down(player, pos, bed_pos, state, skip) bed_pos2 = {x = bed_pos.x - dir.x, y = bed_pos.y, z = bed_pos.z - dir.z} bed_center = {x = bed_pos.x - dir.x/2, y = bed_pos.y + 0.1, z = bed_pos.z - dir.z/2} + -- save respawn position when entering bed + if minetest.get_modpath("mcl_spawn") and mcl_spawn.set_spawn_pos(player, bed_pos, false) then + minetest.chat_send_player(name, S("New respawn position set!")) + end + -- No sleeping if too far away if vector.distance(bed_pos, pos) > 2 and vector.distance(bed_pos2, pos) > 2 then - minetest.chat_send_player(name, S("You can't sleep, the bed's too far away!")) - return false + return false, S("You can't sleep, the bed's too far away!") 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, S("This bed is already occupied!")) - return false + return false, S("This bed is already occupied!") end end @@ -92,9 +95,8 @@ local function lay_down(player, pos, bed_pos, state, skip) -- FIXME: Velocity threshold should be 0.01 but Minetest 5.3.0 -- sometimes reports incorrect Y speed. A velocity threshold -- of 0.125 still seems good enough. - if vector.length(player:get_player_velocity()) > 0.125 then - minetest.chat_send_player(name, S("You have to stop moving before going to bed!")) - return false + if vector.length(player:get_velocity() or player:get_player_velocity()) > 0.125 then + return false, S("You have to stop moving before going to bed!") end -- No sleeping if monsters nearby. @@ -109,9 +111,8 @@ local function lay_down(player, pos, bed_pos, state, skip) -- Approximation of monster detection range if def._cmi_is_mob and ((mobname ~= "mobs_mc:pigman" and def.type == "monster" and not monster_exceptions[mobname]) or (mobname == "mobs_mc:pigman" and ent.state == "attack")) then if math.abs(bed_pos.y - obj:get_pos().y) <= 5 then - minetest.chat_send_player(name, S("You can't sleep now, monsters are nearby!")) + return false, S("You can't sleep now, monsters are nearby!") end - return false end end end @@ -154,32 +155,16 @@ local function lay_down(player, pos, bed_pos, state, skip) local def1 = minetest.registered_nodes[n1.name] local def2 = minetest.registered_nodes[n2.name] if def1.walkable or def2.walkable then - minetest.chat_send_player(name, S("You can't sleep, the bed is obstructed!")) - return false + return false, S("You can't sleep, the bed is obstructed!") 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, S("It's too dangerous to sleep here!")) - return false - end - - local spawn_changed = false - if minetest.get_modpath("mcl_spawn") then - -- save respawn position when entering bed - spawn_changed = mcl_spawn.set_spawn_pos(player, bed_pos, false) + return false, S("It's too dangerous to sleep here!") 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 - if spawn_changed then - minetest.chat_send_player(name, S("New respawn position set! But you can only sleep at night or during a thunderstorm.")) - else - minetest.chat_send_player(name, S("You can only sleep at night or during a thunderstorm.")) - end - return false - end - if spawn_changed then - minetest.chat_send_player(name, S("New respawn position set!")) + return false, S("You can only sleep at night or during a thunderstorm.") end mcl_beds.player[name] = 1 @@ -329,13 +314,17 @@ function mcl_beds.on_rightclick(pos, player, is_top) -- move to bed if not mcl_beds.player[name] then + local success, message if is_top then - lay_down(player, ppos, pos) + success, message = 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) + success, message = lay_down(player, ppos, other) + end + if message then + mcl_tmp_message.message(player, message) end else lay_down(player, nil, nil, false) @@ -362,15 +351,15 @@ minetest.register_on_joinplayer(function(player) -- Make player awake on joining server meta:set_string("mcl_beds:sleeping", "false") 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) - local name = player:get_player_name() - lay_down(player, nil, nil, false, true) local players = minetest.get_connected_players() + local name = player:get_player_name() for n, player in ipairs(players) do if player:get_player_name() == name then players[n] = nil diff --git a/mods/ITEMS/mcl_bows/arrow.lua b/mods/ITEMS/mcl_bows/arrow.lua index bc95fdb4f..f69cfc09e 100644 --- a/mods/ITEMS/mcl_bows/arrow.lua +++ b/mods/ITEMS/mcl_bows/arrow.lua @@ -26,7 +26,7 @@ S("An arrow fired from a bow has a regular damage of 1-9. At full charge, there' S("Arrows might get stuck on solid blocks and can be retrieved again. They are also capable of pushing wooden buttons."), _doc_items_usagehelp = S("To use arrows as ammunition for a bow, just put them anywhere in your inventory, they will be used up automatically. To use arrows as ammunition for a dispenser, place them in the dispenser's inventory. To retrieve an arrow that sticks in a block, simply walk close to it."), inventory_image = "mcl_bows_arrow_inv.png", - groups = { ammo=1, ammo_bow=1 }, + groups = { ammo=1, ammo_bow=1, ammo_bow_regular=1 }, _on_dispense = function(itemstack, dispenserpos, droppos, dropnode, dropdir) -- Shoot arrow local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51)) @@ -83,6 +83,7 @@ local ARROW_ENTITY={ textures = {"mcl_bows:arrow_box"}, collisionbox = {-0.19, -0.125, -0.19, 0.19, 0.125, 0.19}, collide_with_objects = false, + _fire_damage_resistant = true, _lastpos={}, _startpos=nil, @@ -105,6 +106,7 @@ local spawn_item = function(self, pos) item:set_velocity({x=0, y=0, z=0}) item:set_yaw(self.object:get_yaw()) end + mcl_burning.extinguish(self.object) self.object:remove() end @@ -131,6 +133,8 @@ local damage_particles = function(pos, is_critical) end ARROW_ENTITY.on_step = function(self, dtime) + mcl_burning.tick(self.object, dtime) + local pos = self.object:get_pos() local dpos = table.copy(pos) -- digital pos dpos = vector.round(dpos) @@ -140,6 +144,7 @@ ARROW_ENTITY.on_step = function(self, dtime) self._stucktimer = self._stucktimer + dtime self._stuckrechecktimer = self._stuckrechecktimer + dtime if self._stucktimer > ARROW_TIMEOUT then + mcl_burning.extinguish(self.object) self.object:remove() return end @@ -161,7 +166,7 @@ ARROW_ENTITY.on_step = function(self, dtime) local objects = minetest.get_objects_inside_radius(pos, 1) for _,obj in ipairs(objects) do if obj:is_player() then - if not minetest.is_creative_enabled(obj:get_player_name()) then + if self._collectable and not minetest.is_creative_enabled(obj:get_player_name()) then if obj:get_inventory():room_for_item("main", "mcl_bows:arrow") then obj:get_inventory():add_item("main", "mcl_bows:arrow") minetest.sound_play("item_drop_pickup", { @@ -171,6 +176,7 @@ ARROW_ENTITY.on_step = function(self, dtime) }, true) end end + mcl_burning.extinguish(self.object) self.object:remove() return end @@ -232,6 +238,7 @@ ARROW_ENTITY.on_step = function(self, dtime) local def = minetest.registered_nodes[nn] if (not def) or def.walkable then -- There's a node in the way. Delete arrow without damage + mcl_burning.extinguish(self.object) self.object:remove() return end @@ -244,6 +251,9 @@ ARROW_ENTITY.on_step = function(self, dtime) armor.last_damage_types[obj:get_player_name()] = "projectile" end damage_particles(self.object:get_pos(), self._is_critical) + if mcl_burning.is_burning(self.object) then + mcl_burning.set_on_fire(obj, 4) + end obj:punch(self.object, 1.0, { full_punch_interval=1.0, damage_groups={fleshy=self._damage}, @@ -271,6 +281,7 @@ ARROW_ENTITY.on_step = function(self, dtime) end minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true) end + mcl_burning.extinguish(self.object) self.object:remove() return end @@ -403,6 +414,7 @@ ARROW_ENTITY.on_activate = function(self, staticdata, dtime_s) -- If yes, delete it. self._stucktimer = minetest.get_gametime() - data.stuckstarttime if self._stucktimer > ARROW_TIMEOUT then + mcl_burning.extinguish(self.object) self.object:remove() return end diff --git a/mods/ITEMS/mcl_bows/bow.lua b/mods/ITEMS/mcl_bows/bow.lua index d4c5fb081..87820071d 100644 --- a/mods/ITEMS/mcl_bows/bow.lua +++ b/mods/ITEMS/mcl_bows/bow.lua @@ -33,7 +33,7 @@ local bow_load = {} -- Another player table, this one stores the wield index of the bow being charged local bow_index = {} -mcl_bows.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, bow_stack) +mcl_bows.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, bow_stack, collectable) local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, arrow_item.."_entity") if power == nil then power = BOW_MAX_SPEED --19 @@ -50,6 +50,9 @@ mcl_bows.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damag if enchantments.punch then knockback = enchantments.punch * 3 end + if enchantments.flame then + mcl_burning.set_on_fire(obj, math.huge) + end end obj:set_velocity({x=dir.x*power, y=dir.y*power, z=dir.z*power}) obj:set_acceleration({x=0, y=-GRAVITY, z=0}) @@ -60,6 +63,7 @@ mcl_bows.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damag le._is_critical = is_critical le._startpos = pos le._knockback = knockback + le._collectable = collectable minetest.sound_play("mcl_bows_bow_shoot", {pos=pos, max_hear_distance=16}, true) if shooter ~= nil and shooter:is_player() then if obj:get_luaentity().player == "" then @@ -88,6 +92,7 @@ local player_shoot_arrow = function(itemstack, player, power, damage, is_critica local arrow_stack, arrow_stack_id = get_arrow(player) local arrow_itemstring local has_infinity_enchantment = mcl_enchanting.has_enchantment(player:get_wielded_item(), "infinity") + local infinity_used = false if minetest.is_creative_enabled(player:get_player_name()) then if arrow_stack then @@ -100,7 +105,9 @@ local player_shoot_arrow = function(itemstack, player, power, damage, is_critica return false end arrow_itemstring = arrow_stack:get_name() - if not has_infinity_enchantment then + if has_infinity_enchantment and minetest.get_item_group(arrow_itemstring, "ammo_bow_regular") > 0 then + infinity_used = true + else arrow_stack:take_item() end local inv = player:get_inventory() @@ -113,7 +120,7 @@ local player_shoot_arrow = function(itemstack, player, power, damage, is_critica local dir = player:get_look_dir() local yaw = player:get_look_horizontal() - mcl_bows.shoot_arrow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage, is_critical, player:get_wielded_item()) + mcl_bows.shoot_arrow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage, is_critical, player:get_wielded_item(), not infinity_used) return true end @@ -131,7 +138,26 @@ S("The speed and damage of the arrow increases the longer you charge. The regula range = 4, -- Trick to disable digging as well on_use = function() return end, + on_place = function(itemstack, player, pointed_thing) + if pointed_thing and pointed_thing.type == "node" then + -- Call on_rightclick if the pointed node defines it + local node = minetest.get_node(pointed_thing.under) + if player and not player:get_player_control().sneak then + if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then + return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, player, itemstack) or itemstack + end + end + end + + itemstack:get_meta():set_string("active", "true") + return itemstack + end, + on_secondary_use = function(itemstack) + itemstack:get_meta():set_string("active", "true") + return itemstack + end, groups = {weapon=1,weapon_ranged=1,bow=1,enchantability=1}, + _mcl_uses = 385, }) -- Iterates through player inventory and resets all the bows in "charging" state back to their original stage @@ -139,11 +165,15 @@ local reset_bows = function(player) local inv = player:get_inventory() local list = inv:get_list("main") for place, stack in pairs(list) do - if stack:get_name()=="mcl_bows:bow_0" or stack:get_name()=="mcl_bows:bow_1" or stack:get_name()=="mcl_bows:bow_2" then + if stack:get_name() == "mcl_bows:bow" or stack:get_name() == "mcl_bows:bow_enchanted" then + stack:get_meta():set_string("active", "") + elseif stack:get_name()=="mcl_bows:bow_0" or stack:get_name()=="mcl_bows:bow_1" or stack:get_name()=="mcl_bows:bow_2" then stack:set_name("mcl_bows:bow") + stack:get_meta():set_string("active", "") list[place] = stack elseif stack:get_name()=="mcl_bows:bow_0_enchanted" or stack:get_name()=="mcl_bows:bow_1_enchanted" or stack:get_name()=="mcl_bows:bow_2_enchanted" then stack:set_name("mcl_bows:bow_enchanted") + stack:get_meta():set_string("active", "") list[place] = stack end end @@ -176,6 +206,7 @@ for level=0, 2 do on_use = function() return end, on_drop = function(itemstack, dropper, pos) reset_bow_state(dropper) + itemstack:get_meta():set_string("active", "") if mcl_enchanting.is_enchanted(itemstack:get_name()) then itemstack:set_name("mcl_bows:bow_enchanted") else @@ -189,6 +220,7 @@ for level=0, 2 do on_place = function(itemstack) return itemstack end, + _mcl_uses = 385, }) end @@ -200,7 +232,7 @@ controls.register_on_release(function(player, key, time) if (wielditem:get_name()=="mcl_bows:bow_0" or wielditem:get_name()=="mcl_bows:bow_1" or wielditem:get_name()=="mcl_bows:bow_2" or wielditem:get_name()=="mcl_bows:bow_0_enchanted" or wielditem:get_name()=="mcl_bows:bow_1_enchanted" or wielditem:get_name()=="mcl_bows:bow_2_enchanted") then local has_shot = false - + local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name()) local speed, damage local p_load = bow_load[player:get_player_name()] @@ -240,13 +272,13 @@ controls.register_on_release(function(player, key, time) end has_shot = player_shoot_arrow(wielditem, player, speed, damage, is_critical) - + if enchanted then wielditem:set_name("mcl_bows:bow_enchanted") else wielditem:set_name("mcl_bows:bow") end - + if has_shot and not minetest.is_creative_enabled(player:get_player_name()) then local durability = BOW_DURABILITY local unbreaking = mcl_enchanting.get_enchantment(wielditem, "unbreaking") @@ -268,7 +300,7 @@ controls.register_on_hold(function(player, key, time) end local inv = minetest.get_inventory({type="player", name=name}) local wielditem = player:get_wielded_item() - if bow_load[name] == nil and (wielditem:get_name()=="mcl_bows:bow" or wielditem:get_name()=="mcl_bows:bow_enchanted") and (creative or get_arrow(player)) then + if bow_load[name] == nil and (wielditem:get_name()=="mcl_bows:bow" or wielditem:get_name()=="mcl_bows:bow_enchanted") and wielditem:get_meta():get("active") and (creative or get_arrow(player)) then local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name()) if enchanted then wielditem:set_name("mcl_bows:bow_0_enchanted") diff --git a/mods/ITEMS/mcl_cauldrons/init.lua b/mods/ITEMS/mcl_cauldrons/init.lua index 7f0592b2a..a82a0b53a 100644 --- a/mods/ITEMS/mcl_cauldrons/init.lua +++ b/mods/ITEMS/mcl_cauldrons/init.lua @@ -82,7 +82,7 @@ local register_filled_cauldron = function(water_level, description, river_water) drawtype = "nodebox", paramtype = "light", is_ground_content = false, - groups = {pickaxey=1, not_in_creative_inventory=1, cauldron=(1+water_level), comparator_signal=water_level}, + groups = {pickaxey=1, not_in_creative_inventory=1, cauldron=(1+water_level), cauldron_filled=water_level, comparator_signal=water_level}, node_box = cauldron_nodeboxes[water_level], collision_box = cauldron_nodeboxes[0], selection_box = { type = "regular" }, @@ -122,3 +122,20 @@ minetest.register_craft({ { "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" }, } }) + +minetest.register_abm({ + label = "cauldrons", + nodenames = {"group:cauldron_filled"}, + interval = 0.5, + chance = 1, + action = function(pos, node) + for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 0.4)) do + if mcl_burning.is_burning(obj) then + mcl_burning.extinguish(obj) + local new_group = minetest.get_item_group(node.name, "cauldron_filled") - 1 + minetest.swap_node(pos, {name = "mcl_cauldrons:cauldron" .. (new_group == 0 and "" or "_" .. new_group)}) + break + end + end + end +}) diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index 3d5fbbca3..0f1e44c10 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -1,10 +1,162 @@ local S = minetest.get_translator("mcl_chests") local mod_doc = minetest.get_modpath("doc") +-- Chest Entity +local animate_chests = (minetest.settings:get_bool("animated_chests") ~= false) +local entity_animations = { + shulker = { + speed = 50, + open = {x = 45, y = 95}, + close = {x = 95, y = 145}, + }, + chest = { + speed = 25, + open = {x = 0, y = 10}, + open_partly = {x = 0, y = 7}, + close = {x = 10, y = 20}, + close_partly = {x = 13, y = 20}, + } +} + +minetest.register_entity("mcl_chests:chest", { + initial_properties = { + visual = "mesh", + visual_size = {x = 3, y = 3}, + pointable = false, + physical = false, + static_save = false, + }, + + set_animation = function(self, animname) + local anim_table = entity_animations[self.animation_type] + local anim = anim_table[animname] + if not anim then return end + self.object:set_animation(anim, anim_table.speed, 0, false) + end, + + open = function(self, playername, partly) + self.players[playername] = true + if not self.is_open then + self:set_animation(partly and "open_partly" or "open") + minetest.sound_play(self.sound_prefix .. "_open", { + pos = self.node_pos, + }) + self.is_open = true + self.opened_partly = partly + end + end, + + close = function(self, playername) + local playerlist = self.players + playerlist[playername] = nil + if self.is_open then + for _ in pairs(playerlist) do + return + end + self:set_animation(self.opened_partly and "close_partly" or "close") + minetest.sound_play(self.sound_prefix .. "_close", { + pos = self.node_pos, + }) + self.is_open = false + self.opened_partly = false + end + end, + + initialize = function(self, node_pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type) + self.node_pos = node_pos + self.node_name = node_name + self.sound_prefix = sound_prefix + self.animation_type = animation_type + local obj = self.object + obj:set_properties({ + textures = textures, + mesh = mesh_prefix .. (double and "_double" or "") .. ".b3d", + }) + self:set_yaw(dir) + end, + + reinitialize = function(self, node_name) + self.node_name = node_name + end, + + set_yaw = function(self, dir) + self.object:set_yaw(minetest.dir_to_yaw(dir)) + end, + + check = function(self) + local node_pos, node_name = self.node_pos, self.node_name + if not node_pos or not node_name then + return false + end + local node = minetest.get_node(node_pos) + if node.name ~= node_name then + return false + end + return true + end, + + on_activate = function(self) + self.object:set_armor_groups({immortal = 1}) + self.players = {} + end, + + on_step = function(self, dtime) + if not self:check() then + self.object:remove() + end + end +}) + +local function get_entity_pos(pos, dir, double) + pos = vector.new(pos) + pos.y = pos.y - 0.49 + if double then + local add, mul, vec, cross = vector.add, vector.multiply, vector.new, vector.cross + pos = add(pos, mul(cross(dir, vec(0, 1, 0)), -0.5)) + end + return pos +end + +local function find_entity(pos) + for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 0)) do + local luaentity = obj:get_luaentity() + if luaentity and luaentity.name == "mcl_chests:chest" then + return luaentity + end + end +end + +local function get_entity_info(pos, param2, double, dir, entity_pos) + dir = dir or minetest.facedir_to_dir(param2) + return dir, get_entity_pos(pos, dir, double) +end + +local function create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos) + dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) + local obj = minetest.add_entity(entity_pos, "mcl_chests:chest") + local luaentity = obj:get_luaentity() + luaentity:initialize(pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type) + return luaentity +end + +local function find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos) + dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos) + return find_entity(entity_pos) or create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos) +end + local no_rotate, simple_rotate if minetest.get_modpath("screwdriver") then no_rotate = screwdriver.disallow - simple_rotate = screwdriver.rotate_simple + simple_rotate = function(pos, node, user, mode, new_param2) + if screwdriver.rotate_simple(pos, node, user, mode, new_param2) ~= false then + local nodename = node.name + local nodedef = minetest.registered_nodes[nodename] + local dir = minetest.facedir_to_dir(new_param2) + find_or_create_entity(pos, nodename, nodedef._chest_entity_textures, new_param2, false, nodedef._chest_entity_sound, nodedef._chest_entity_mesh, nodedef._chest_entity_animation_type, dir):set_yaw(dir) + else + return false + end + end end --[[ List of open chests. @@ -13,9 +165,23 @@ Value: If player is using a chest: { pos = } Otherwise: nil ]] local open_chests = {} + +local function back_is_blocked(pos, dir) + pos = vector.add(pos, dir) + local def = minetest.registered_nodes[minetest.get_node(pos).name] + pos.y = pos.y + 1 + local def2 = minetest.registered_nodes[minetest.get_node(pos).name] + return not def or def.groups.opaque == 1 or not def2 or def2.groups.opaque == 1 +end -- To be called if a player opened a chest -local player_chest_open = function(player, pos) - open_chests[player:get_player_name()] = { pos = pos } +local player_chest_open = function(player, pos, node_name, textures, param2, double, sound, mesh, shulker) + local name = player:get_player_name() + open_chests[name] = {pos = pos, node_name = node_name, textures = textures, param2 = param2, double = double, sound = sound, mesh = mesh, shulker = shulker} + if animate_chests then + local dir = minetest.facedir_to_dir(param2) + local blocked = not shulker and (back_is_blocked(pos, dir) or double and back_is_blocked(mcl_util.get_double_container_neighbor_pos(pos, param2, node_name:sub(-4)), dir)) + find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh, shulker and "shulker" or "chest", dir):open(name, blocked) + end end -- Simple protection checking functions @@ -44,11 +210,13 @@ local trapped_chest_mesecons_rules = mesecon.rules.pplate local chest_update_after_close = function(pos) local node = minetest.get_node(pos) - if node.name == "mcl_chests:trapped_chest_on" then - minetest.swap_node(pos, {name="mcl_chests:trapped_chest", param2 = node.param2}) + if node.name == "mcl_chests:trapped_chest_on_small" then + minetest.swap_node(pos, {name="mcl_chests:trapped_chest_small", param2 = node.param2}) + find_or_create_entity(pos, "mcl_chests:trapped_chest_small", {"mcl_chests_trapped.png"}, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small") mesecon.receptor_off(pos, trapped_chest_mesecons_rules) elseif node.name == "mcl_chests:trapped_chest_on_left" then minetest.swap_node(pos, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) + find_or_create_entity(pos, "mcl_chests:trapped_chest_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") mesecon.receptor_off(pos, trapped_chest_mesecons_rules) local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") @@ -60,6 +228,7 @@ local chest_update_after_close = function(pos) local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) + find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) end end @@ -67,11 +236,14 @@ end -- To be called if a player closed a chest local player_chest_close = function(player) local name = player:get_player_name() - if open_chests[name] == nil then + local open_chest = open_chests[name] + if open_chest == nil then return end - local pos = open_chests[name].pos - chest_update_after_close(pos) + if animate_chests then + find_or_create_entity(open_chest.pos, open_chest.node_name, open_chest.textures, open_chest.param2, open_chest.double, open_chest.sound, open_chest.mesh, open_chest.shulker and "shulker" or "chest"):close(name) + end + chest_update_after_close(open_chest.pos) open_chests[name] = nil end @@ -146,18 +318,82 @@ local on_chest_blast = function(pos) minetest.remove_node(pos) end +local function limit_put_list(stack, list) + for _, other in ipairs(list) do + stack = other:add_item(stack) + if stack:is_empty() then + break + end + end + return stack +end + +local function limit_put(stack, inv1, inv2) + local leftover = ItemStack(stack) + leftover = limit_put_list(leftover, inv1:get_list("main")) + leftover = limit_put_list(leftover, inv2:get_list("main")) + return stack:get_count() - leftover:get_count() +end + +local small_name = "mcl_chests:"..basename.."_small" +local small_textures = tiles_table.small +local left_name = "mcl_chests:"..basename.."_left" +local left_textures = tiles_table.double + minetest.register_node("mcl_chests:"..basename, { description = desc, _tt_help = tt_help, _doc_items_longdesc = longdesc, _doc_items_usagehelp = usagehelp, _doc_items_hidden = hidden, - tiles = tiles_table.small, + drawtype = "mesh", + mesh = "mcl_chests_chest.obj", + tiles = small_textures, + paramtype = "light", + paramtype2 = "facedir", + stack_max = 64, + sounds = mcl_sounds.node_sound_wood_defaults(), + groups = {deco_block=1}, + on_construct = function(pos, node) + local node = minetest.get_node(pos) + node.name = small_name + minetest.set_node(pos, node) + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) + end, +}) + +local function close_forms(canonical_basename, pos) + local players = minetest.get_connected_players() + for p=1, #players do + if vector.distance(players[p]:get_pos(), pos) <= 30 then + minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z) + end + end +end + +minetest.register_node(small_name, { + description = desc, + _tt_help = tt_help, + _doc_items_longdesc = longdesc, + _doc_items_usagehelp = usagehelp, + _doc_items_hidden = hidden, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375}, + }, + tiles = {"mcl_chests_blank.png"}, + _chest_entity_textures = small_textures, + _chest_entity_sound = "default_chest", + _chest_entity_mesh = "mcl_chests_chest", + _chest_entity_animation_type = "chest", paramtype = "light", paramtype2 = "facedir", stack_max = 64, drop = drop, - groups = {handy=1,axey=1, container=2, deco_block=1, material_wood=1,flammable=-1}, + groups = {handy=1,axey=1, container=2, deco_block=1, material_wood=1,flammable=-1,chest_entity=1, not_in_creative_inventory=1}, is_ground_content = false, sounds = mcl_sounds.node_sound_wood_defaults(), on_construct = function(pos) @@ -185,16 +421,19 @@ minetest.register_node("mcl_chests:"..basename, { -- BEGIN OF LISTRING WORKAROUND inv:set_size("input", 1) -- END OF LISTRING WORKAROUND - if minetest.get_node(mcl_util.get_double_container_neighbor_pos(pos, param2, "right")).name == "mcl_chests:"..canonical_basename then + if minetest.get_node(mcl_util.get_double_container_neighbor_pos(pos, param2, "right")).name == "mcl_chests:"..canonical_basename.."_small" then minetest.swap_node(pos, {name="mcl_chests:"..canonical_basename.."_right",param2=param2}) local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") minetest.swap_node(p, { name = "mcl_chests:"..canonical_basename.."_left", param2 = param2 }) - elseif minetest.get_node(mcl_util.get_double_container_neighbor_pos(pos, param2, "left")).name == "mcl_chests:"..canonical_basename then + create_entity(p, "mcl_chests:"..canonical_basename.."_left", left_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") + elseif minetest.get_node(mcl_util.get_double_container_neighbor_pos(pos, param2, "left")).name == "mcl_chests:"..canonical_basename.."_small" then minetest.swap_node(pos, {name="mcl_chests:"..canonical_basename.."_left",param2=param2}) + create_entity(pos, "mcl_chests:"..canonical_basename.."_left", left_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") minetest.swap_node(p, { name = "mcl_chests:"..canonical_basename.."_right", param2 = param2 }) else - minetest.swap_node(pos, { name = "mcl_chests:"..canonical_basename, param2 = param2 }) + minetest.swap_node(pos, { name = "mcl_chests:"..canonical_basename.."_small", param2 = param2 }) + create_entity(pos, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") end end, after_place_node = function(pos, placer, itemstack, pointed_thing) @@ -253,23 +492,31 @@ minetest.register_node("mcl_chests:"..basename, { if on_rightclick_addendum then on_rightclick_addendum(pos, node, clicker) end + + player_chest_open(clicker, pos, small_name, small_textures, node.param2, false, "default_chest", "mcl_chests_chest") end, on_destruct = function(pos) - local players = minetest.get_connected_players() - for p=1, #players do - minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z) - end + close_forms(canonical_basename, pos) end, mesecons = mesecons, on_rotate = simple_rotate, }) -minetest.register_node("mcl_chests:"..basename.."_left", { - tiles = tiles_table.left, +minetest.register_node(left_name, { + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-0.4375, -0.5, -0.4375, 0.5, 0.375, 0.4375}, + }, + tiles = {"mcl_chests_blank.png"}, + _chest_entity_textures = left_textures, + _chest_entity_sound = "default_chest", + _chest_entity_mesh = "mcl_chests_chest", + _chest_entity_animation_type = "chest", paramtype = "light", paramtype2 = "facedir", - groups = {handy=1,axey=1, container=5,not_in_creative_inventory=1, material_wood=1,flammable=-1}, + groups = {handy=1,axey=1, container=5,not_in_creative_inventory=1, material_wood=1,flammable=-1,chest_entity=1,double_chest=1}, drop = drop, is_ground_content = false, sounds = mcl_sounds.node_sound_wood_defaults(), @@ -278,33 +525,31 @@ minetest.register_node("mcl_chests:"..basename.."_left", { local param2 = n.param2 local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") if not p or minetest.get_node(p).name ~= "mcl_chests:"..canonical_basename.."_right" then - n.name = "mcl_chests:"..canonical_basename + n.name = "mcl_chests:"..canonical_basename.."_small" minetest.swap_node(pos, n) end + create_entity(pos, left_name, left_textures, param2, true, "default_chest", "mcl_chests_chest", "chest") end, after_place_node = function(pos, placer, itemstack, pointed_thing) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) end, on_destruct = function(pos) local n = minetest.get_node(pos) - if n.name == "mcl_chests:"..basename then + if n.name == small_name then return end - local players = minetest.get_connected_players() - for p=1, #players do - minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z) - end + close_forms(canonical_basename, pos) local param2 = n.param2 local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") if not p or minetest.get_node(p).name ~= "mcl_chests:"..basename.."_right" then return end - for pl=1, #players do - minetest.close_formspec(players[pl]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..p.x.."_"..p.y.."_"..p.z) - end - minetest.swap_node(p, { name = "mcl_chests:"..basename, param2 = param2 }) + close_forms(canonical_basename, p) + + minetest.swap_node(p, { name = small_name, param2 = param2 }) + create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") end, after_dig_node = drop_items_chest, on_blast = on_chest_blast, @@ -318,17 +563,19 @@ minetest.register_node("mcl_chests:"..basename.."_left", { -- BEGIN OF LISTRING WORKAROUND elseif listname == "input" then local inv = minetest.get_inventory({type="node", pos=pos}) - if inv:room_for_item("main", stack) then + local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") + local other_inv = minetest.get_inventory({type="node", pos=other_pos}) + return limit_put(stack, inv, other_inv) + --[[if inv:room_for_item("main", stack) then return -1 else - local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") - local other_inv = minetest.get_inventory({type="node", pos=other_pos}) + if other_inv:room_for_item("main", stack) then return -1 else return 0 end - end + end]]-- -- END OF LISTRING WORKAROUND else return stack:get_count() @@ -347,6 +594,8 @@ minetest.register_node("mcl_chests:"..basename.."_left", { local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "left") local other_inv = minetest.get_inventory({type="node", pos=other_pos}) + inv:set_stack("input", 1, nil) + double_chest_add_item(inv, other_inv, "main", stack) end -- END OF LISTRING WORKAROUND @@ -399,16 +648,23 @@ minetest.register_node("mcl_chests:"..basename.."_left", { if on_rightclick_addendum_left then on_rightclick_addendum_left(pos, node, clicker) end + + player_chest_open(clicker, pos, left_name, left_textures, node.param2, true, "default_chest", "mcl_chests_chest") end, mesecons = mesecons, on_rotate = no_rotate, }) minetest.register_node("mcl_chests:"..basename.."_right", { - tiles = tiles_table.right, + drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", - groups = {handy=1,axey=1, container=6,not_in_creative_inventory=1, material_wood=1,flammable=-1}, + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.4375, 0.4375, 0.375, 0.4375}, + }, + tiles = {"mcl_chests_blank.png"}, + groups = {handy=1,axey=1, container=6,not_in_creative_inventory=1, material_wood=1,flammable=-1,double_chest=2}, drop = drop, is_ground_content = false, sounds = mcl_sounds.node_sound_wood_defaults(), @@ -417,7 +673,7 @@ minetest.register_node("mcl_chests:"..basename.."_right", { local param2 = n.param2 local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") if not p or minetest.get_node(p).name ~= "mcl_chests:"..canonical_basename.."_left" then - n.name = "mcl_chests:"..canonical_basename + n.name = "mcl_chests:"..canonical_basename.."_small" minetest.swap_node(pos, n) end end, @@ -426,24 +682,21 @@ minetest.register_node("mcl_chests:"..basename.."_right", { end, on_destruct = function(pos) local n = minetest.get_node(pos) - if n.name == "mcl_chests:"..basename then + if n.name == small_name then return end - local players = minetest.get_connected_players() - for p=1, #players do - minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z) - end + close_forms(canonical_basename, pos) local param2 = n.param2 local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") if not p or minetest.get_node(p).name ~= "mcl_chests:"..basename.."_left" then return end - for pl=1, #players do - minetest.close_formspec(players[pl]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..p.x.."_"..p.y.."_"..p.z) - end - minetest.swap_node(p, { name = "mcl_chests:"..basename, param2 = param2 }) + close_forms(canonical_basename, p) + + minetest.swap_node(p, { name = small_name, param2 = param2 }) + create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest") local meta = minetest.get_meta(pos) end, after_dig_node = drop_items_chest, @@ -459,16 +712,17 @@ minetest.register_node("mcl_chests:"..basename.."_right", { elseif listname == "input" then local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") local other_inv = minetest.get_inventory({type="node", pos=other_pos}) - if other_inv:room_for_item("main", stack) then + local inv = minetest.get_inventory({type="node", pos=pos}) + --[[if other_inv:room_for_item("main", stack) then return -1 else - local inv = minetest.get_inventory({type="node", pos=pos}) if inv:room_for_item("main", stack) then return -1 else return 0 end - end + end--]] + return limit_put(stack, other_inv, inv) -- END OF LISTRING WORKAROUND else return stack:get_count() @@ -487,6 +741,8 @@ minetest.register_node("mcl_chests:"..basename.."_right", { local other_inv = minetest.get_inventory({type="node", pos=other_pos}) local inv = minetest.get_inventory({type="node", pos=pos}) + inv:set_stack("input", 1, nil) + double_chest_add_item(other_inv, inv, "main", stack) end -- END OF LISTRING WORKAROUND @@ -540,14 +796,16 @@ minetest.register_node("mcl_chests:"..basename.."_right", { if on_rightclick_addendum_right then on_rightclick_addendum_right(pos, node, clicker) end + + player_chest_open(clicker, pos_other, left_name, left_textures, node.param2, true, "default_chest", "mcl_chests_chest") end, mesecons = mesecons, on_rotate = no_rotate, }) if mod_doc then - doc.add_entry_alias("nodes", "mcl_chests:"..basename, "nodes", "mcl_chests:"..basename.."_left") - doc.add_entry_alias("nodes", "mcl_chests:"..basename, "nodes", "mcl_chests:"..basename.."_right") + doc.add_entry_alias("nodes", small_name, "nodes", "mcl_chests:"..basename.."_left") + doc.add_entry_alias("nodes", small_name, "nodes", "mcl_chests:"..basename.."_right") end -- END OF register_chest FUNCTION BODY @@ -561,29 +819,33 @@ register_chest("chest", chestusage, S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"), { - small = {"default_chest_top.png", "mcl_chests_chest_bottom.png", + small = {"mcl_chests_normal.png"}, + double = {"mcl_chests_normal_double.png"}, + inv = {"default_chest_top.png", "mcl_chests_chest_bottom.png", "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", "mcl_chests_chest_back.png", "default_chest_front.png"}, - left = {"default_chest_top_big.png", "default_chest_top_big.png", + --[[left = {"default_chest_top_big.png", "default_chest_top_big.png", "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", "default_chest_side_big.png^[transformFX", "default_chest_front_big.png"}, right = {"default_chest_top_big.png^[transformFX", "default_chest_top_big.png^[transformFX", "mcl_chests_chest_right.png", "mcl_chests_chest_left.png", - "default_chest_side_big.png", "default_chest_front_big.png^[transformFX"}, + "default_chest_side_big.png", "default_chest_front_big.png^[transformFX"},]]-- }, false ) local traptiles = { - small = {"mcl_chests_chest_trapped_top.png", "mcl_chests_chest_trapped_bottom.png", + small = {"mcl_chests_trapped.png"}, + double = {"mcl_chests_trapped_double.png"}, + inv = {"mcl_chests_chest_trapped_top.png", "mcl_chests_chest_trapped_bottom.png", "mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_left.png", "mcl_chests_chest_trapped_back.png", "mcl_chests_chest_trapped_front.png"}, - left = {"mcl_chests_chest_trapped_top_big.png", "mcl_chests_chest_trapped_top_big.png", + --[[left = {"mcl_chests_chest_trapped_top_big.png", "mcl_chests_chest_trapped_top_big.png", "mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_left.png", "mcl_chests_chest_trapped_side_big.png^[transformFX", "mcl_chests_chest_trapped_front_big.png"}, right = {"mcl_chests_chest_trapped_top_big.png^[transformFX", "mcl_chests_chest_trapped_top_big.png^[transformFX", "mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_left.png", - "mcl_chests_chest_trapped_side_big.png", "mcl_chests_chest_trapped_front_big.png^[transformFX"}, + "mcl_chests_chest_trapped_side_big.png", "mcl_chests_chest_trapped_front_big.png^[transformFX"},]]-- } register_chest("trapped_chest", @@ -598,22 +860,21 @@ register_chest("trapped_chest", rules = trapped_chest_mesecons_rules, }}, function(pos, node, clicker) - minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on", param2 = node.param2}) + minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on_small", param2 = node.param2}) + find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", {"mcl_chests_trapped.png"}, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_small") mesecon.receptor_on(pos, trapped_chest_mesecons_rules) - player_chest_open(clicker, pos) end, function(pos, node, clicker) local meta = minetest.get_meta(pos) meta:set_int("players", 1) minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on_left", param2 = node.param2}) + find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") mesecon.receptor_on(pos, trapped_chest_mesecons_rules) local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_on_right", param2 = node.param2}) mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) - - player_chest_open(clicker, pos) end, function(pos, node, clicker) local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") @@ -622,9 +883,8 @@ register_chest("trapped_chest", mesecon.receptor_on(pos, trapped_chest_mesecons_rules) minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_on_left", param2 = node.param2}) + find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_on_left") mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) - - player_chest_open(clicker, pos) end ) @@ -634,15 +894,7 @@ register_chest("trapped_chest_on", state = mesecon.state.on, rules = trapped_chest_mesecons_rules, }}, - function(pos, node, clicker) - player_chest_open(clicker, pos) - end, - function(pos, node, clicker) - player_chest_open(clicker, pos) - end, - function(pos, node, clicker) - player_chest_open(clicker, pos) - end, + nil, nil, nil, "trapped_chest", "trapped_chest" ) @@ -650,13 +902,15 @@ register_chest("trapped_chest_on", local function close_if_trapped_chest(pos, player) local node = minetest.get_node(pos) - if node.name == "mcl_chests:trapped_chest_on" then - minetest.swap_node(pos, {name="mcl_chests:trapped_chest", param2 = node.param2}) + if node.name == "mcl_chests:trapped_chest_on_small" then + minetest.swap_node(pos, {name="mcl_chests:trapped_chest_small", param2 = node.param2}) + find_or_create_entity(pos, "mcl_chests:trapped_chest_small", {"mcl_chests_trapped.png"}, node.param2, false, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small") mesecon.receptor_off(pos, trapped_chest_mesecons_rules) player_chest_close(player) elseif node.name == "mcl_chests:trapped_chest_on_left" then minetest.swap_node(pos, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) + find_or_create_entity(pos, "mcl_chests:trapped_chest_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") mesecon.receptor_off(pos, trapped_chest_mesecons_rules) local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") @@ -670,15 +924,16 @@ local function close_if_trapped_chest(pos, player) local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) + find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", {"mcl_chests_trapped_double.png"}, node.param2, true, "default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left") mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) player_chest_close(player) end end --- Disable trapped chest when it has been closed +-- Disable chest when it has been closed minetest.register_on_player_receive_fields(function(player, formname, fields) - if formname:find("mcl_chests:trapped_chest_") == 1 then + if formname:find("mcl_chests:") == 1 then if fields.quit then player_chest_close(player) end @@ -710,6 +965,26 @@ minetest.register_craft({ burntime = 15 }) +minetest.register_node("mcl_chests:ender_chest", { + description = S("Ender Chest"), + _tt_help = S("27 interdimensional inventory slots") .. "\n" .. S("Put items inside, retrieve them from any ender chest"), + _doc_items_longdesc = S("Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This inventory is the same no matter from which ender chest you access it from. If you put one item into one ender chest, you will find it in all other ender chests. Each player will only see their own items, but not the items of other players."), + _doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."), + drawtype = "mesh", + mesh = "mcl_chests_chest.obj", + tiles = {"mcl_chests_ender.png"}, + paramtype = "light", + paramtype2 = "facedir", + stack_max = 64, + groups = {deco_block=1}, + sounds = mcl_sounds.node_sound_stone_defaults(), + on_construct = function(pos, node) + local node = minetest.get_node(pos) + node.name = "mcl_chests:ender_chest_small" + minetest.set_node(pos, node) + end, +}) + local formspec_ender_chest = "size[9,8.75]".. "label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Ender Chest"))).."]".. "list[current_player;enderchest;0,0.5;9,3;]".. @@ -723,17 +998,27 @@ local formspec_ender_chest = "size[9,8.75]".. "listring[current_player;main]" -minetest.register_node("mcl_chests:ender_chest", { +minetest.register_node("mcl_chests:ender_chest_small", { description = S("Ender Chest"), _tt_help = S("27 interdimensional inventory slots") .. "\n" .. S("Put items inside, retrieve them from any ender chest"), _doc_items_longdesc = S("Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This inventory is the same no matter from which ender chest you access it from. If you put one item into one ender chest, you will find it in all other ender chests. Each player will only see their own items, but not the items of other players."), _doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."), - tiles = {"mcl_chests_ender_chest_top.png", "mcl_chests_ender_chest_bottom.png", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-0.4375, -0.5, -0.4375, 0.5, 0.375, 0.4375}, + }, + _chest_entity_textures = {"mcl_chests_ender.png"}, + _chest_entity_sound = "mcl_chests_enderchest", + _chest_entity_mesh = "mcl_chests_chest", + _chest_entity_animation_type = "chest", + tiles = {"mcl_chests_blank.png"}, + --[[{"mcl_chests_ender_chest_top.png", "mcl_chests_ender_chest_bottom.png", "mcl_chests_ender_chest_right.png", "mcl_chests_ender_chest_left.png", - "mcl_chests_ender_chest_back.png", "mcl_chests_ender_chest_front.png"}, + "mcl_chests_ender_chest_back.png", "mcl_chests_ender_chest_front.png"},]]-- -- Note: The “container” group is missing here because the ender chest does not -- have an inventory on its own - groups = {pickaxey=1, deco_block=1, material_stone=1}, + groups = {pickaxey=1, deco_block=1, material_stone=1, chest_entity=1, not_in_creative_inventory=1}, is_ground_content = false, paramtype = "light", light_source = 7, @@ -743,23 +1028,22 @@ minetest.register_node("mcl_chests:ender_chest", { on_construct = function(pos) local meta = minetest.get_meta(pos) meta:set_string("formspec", formspec_ender_chest) + create_entity(pos, "mcl_chests:ender_chest_small", {"mcl_chests_ender.png"}, minetest.get_node(pos).param2, false, "mcl_chests_enderchest", "mcl_chests_chest", "chest") + end, + on_rightclick = function(pos, node, clicker) + player_chest_open(clicker, pos, "mcl_chests:ender_chest_small", {"mcl_chests_ender.png"}, node.param2, false, "mcl_chests_enderchest", "mcl_chests_chest") + end, + on_receive_fields = function(pos, formname, fields, sender) + if fields.quit then + player_chest_close(sender) + end end, _mcl_blast_resistance = 3000, _mcl_hardness = 22.5, - _mcl_silk_touch_drop = true, + _mcl_silk_touch_drop = {"mcl_chests:ender_chest"}, on_rotate = simple_rotate, }) -minetest.register_lbm({ - label = "Update ender chest + shulker box formspecs (0.51.0)", - name = "mcl_chests:update_formspecs_0_51_0", - nodenames = { "mcl_chests:ender_chest", "group:shulker_box" }, - action = function(pos, node) - minetest.registered_nodes[node.name].on_construct(pos) - minetest.log("action", "[mcl_chests] Node formspec updated at "..minetest.pos_to_string(pos)) - end, -}) - minetest.register_on_joinplayer(function(player) local inv = player:get_inventory() inv:set_size("enderchest", 9*3) @@ -852,6 +1136,8 @@ for color, desc in pairs(boxtypes) do end end + local small_name = "mcl_chests:"..color.."_shulker_box_small" + minetest.register_node("mcl_chests:"..color.."_shulker_box", { description = desc, _tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"), @@ -859,43 +1145,26 @@ for color, desc in pairs(boxtypes) do _doc_items_entry_name = entry_name, _doc_items_longdesc = longdesc, _doc_items_usagehelp = usagehelp, - tiles = { - "mcl_chests_"..color.."_shulker_box_top.png", -- top + tiles = {mob_texture}, + drawtype = "mesh", + mesh = "mcl_chests_shulker.obj", + --[["mcl_chests_"..color.."_shulker_box_top.png", -- top "[combine:16x16:-32,-28="..mob_texture, -- bottom "[combine:16x16:0,-36="..mob_texture..":0,-16="..mob_texture, -- side "[combine:16x16:-32,-36="..mob_texture..":-32,-16="..mob_texture, -- side "[combine:16x16:-16,-36="..mob_texture..":-16,-16="..mob_texture, -- side - "[combine:16x16:-48,-36="..mob_texture..":-48,-16="..mob_texture, -- side - }, - groups = {handy=1,pickaxey=1, container=3, deco_block=1, dig_by_piston=1, shulker_box=1}, + "[combine:16x16:-48,-36="..mob_texture..":-48,-16="..mob_texture, -- side]]-- + groups = {handy=1,pickaxey=1, container=3, deco_block=1, dig_by_piston=1, shulker_box=1, old_shulker_box_node=1}, is_ground_content = false, sounds = mcl_sounds.node_sound_stone_defaults(), stack_max = 1, drop = "", paramtype = "light", paramtype2 = "facedir", --- TODO: Make shulker boxes rotatable --- This doesn't work, it just destroys the inventory: --- on_place = minetest.rotate_node, on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("formspec", formspec_shulker_box(nil)) - local inv = meta:get_inventory() - inv:set_size("main", 9*3) - end, - _on_dispense = function(stack, pos, droppos, dropnode, dropdir) - -- Place shulker box as node - if minetest.registered_nodes[dropnode.name].buildable_to then - minetest.set_node(droppos, {name = stack:get_name(), param2 = minetest.dir_to_facedir(dropdir)}) - local ninv = minetest.get_inventory({type="node", pos=droppos}) - local imetadata = stack:get_metadata() - local iinv_main = minetest.deserialize(imetadata) - ninv:set_list("main", iinv_main) - ninv:set_size("main", 9*3) - set_shulkerbox_meta(minetest.get_meta(droppos), stack:get_meta()) - stack:take_item() - end - return stack + local node = minetest.get_node(pos) + node.name = small_name + minetest.set_node(pos, node) end, after_place_node = function(pos, placer, itemstack, pointed_thing) local nmeta = minetest.get_meta(pos) @@ -916,6 +1185,79 @@ for color, desc in pairs(boxtypes) do return nil end end, + _on_dispense = function(stack, pos, droppos, dropnode, dropdir) + -- Place shulker box as node + if minetest.registered_nodes[dropnode.name].buildable_to then + minetest.set_node(droppos, {name = small_name, param2 = minetest.dir_to_facedir(dropdir)}) + local ninv = minetest.get_inventory({type="node", pos=droppos}) + local imetadata = stack:get_metadata() + local iinv_main = minetest.deserialize(imetadata) + ninv:set_list("main", iinv_main) + ninv:set_size("main", 9*3) + set_shulkerbox_meta(minetest.get_meta(droppos), stack:get_meta()) + stack:take_item() + end + return stack + end, + }) + + minetest.register_node(small_name, { + description = desc, + _tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"), + _doc_items_create_entry = create_entry, + _doc_items_entry_name = entry_name, + _doc_items_longdesc = longdesc, + _doc_items_usagehelp = usagehelp, + drawtype = "nodebox", + tiles = {"mcl_chests_blank.png"}, + _chest_entity_textures = {mob_texture}, + _chest_entity_sound = "mcl_chests_shulker", + _chest_entity_mesh = "mcl_chests_shulker", + _chest_entity_animation_type = "shulker", + groups = {handy=1,pickaxey=1, container=3, deco_block=1, dig_by_piston=1, shulker_box=1, chest_entity=1, not_in_creative_inventory=1}, + is_ground_content = false, + sounds = mcl_sounds.node_sound_stone_defaults(), + stack_max = 1, + drop = "", + paramtype = "light", + paramtype2 = "facedir", +-- TODO: Make shulker boxes rotatable +-- This doesn't work, it just destroys the inventory: +-- on_place = minetest.rotate_node, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", formspec_shulker_box(nil)) + local inv = meta:get_inventory() + inv:set_size("main", 9*3) + create_entity(pos, small_name, {mob_texture}, minetest.get_node(pos).param2, false, "mcl_chests_shulker", "mcl_chests_shulker", "shulker") + end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + local nmeta = minetest.get_meta(pos) + local imetadata = itemstack:get_metadata() + local iinv_main = minetest.deserialize(imetadata) + local ninv = nmeta:get_inventory() + ninv:set_list("main", iinv_main) + ninv:set_size("main", 9*3) + set_shulkerbox_meta(nmeta, itemstack:get_meta()) + + if minetest.is_creative_enabled(placer:get_player_name()) then + if not ninv:is_empty("main") then + return nil + else + return itemstack + end + else + return nil + end + end, + on_rightclick = function(pos, node, clicker) + player_chest_open(clicker, pos, small_name, {mob_texture}, node.param2, false, "mcl_chests_shulker", "mcl_chests_shulker", true) + end, + on_receive_fields = function(pos, formname, fields, sender) + if fields.quit then + player_chest_close(sender) + end + end, on_destruct = function(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() @@ -961,6 +1303,7 @@ for color, desc in pairs(boxtypes) do if mod_doc and not is_canonical then doc.add_entry_alias("nodes", "mcl_chests:"..canonical_shulker_color.."_shulker_box", "nodes", "mcl_chests:"..color.."_shulker_box") + doc.add_entry_alias("nodes", "mcl_chests:"..canonical_shulker_color.."_shulker_box_small", "nodes", "mcl_chests:"..color.."_shulker_box_small") end minetest.register_craft({ @@ -1001,6 +1344,40 @@ minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv end end) +local function select_and_spawn_entity(pos, node) + local node_name = node.name + local node_def = minetest.registered_nodes[node_name] + local double_chest = minetest.get_item_group(node_name, "double_chest") > 0 + create_entity(pos, node_name, node_def._chest_entity_textures, node.param2, double_chest, node_def._chest_entity_sound, node_def._chest_entity_mesh, node_def._chest_entity_animation_type) +end + +minetest.register_lbm({ + label = "Spawn Chest entities", + name = "mcl_chests:spawn_chest_entities", + nodenames = {"group:chest_entity"}, + run_at_every_load = true, + action = select_and_spawn_entity, +}) + +minetest.register_lbm({ + label = "Replace old chest nodes", + name = "mcl_chests:replace_old", + nodenames = {"mcl_chests:chest", "mcl_chests:trapped_chest", "mcl_chests:trapped_chest_on", "mcl_chests:ender_chest", "group:old_shulker_box_node"}, + run_at_every_load = true, + action = function(pos, node) + local node_name = node.name + node.name = node_name .. "_small" + minetest.swap_node(pos, node) + select_and_spawn_entity(pos, node) + if node_name == "mcl_chests:trapped_chest_on" then + minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " ..minetest.pos_to_string(pos)) + chest_update_after_close(pos) + elseif node_name == "mcl_chests:ender_chest" then + local meta = minetest.get_meta(pos) + meta:set_string("formspec", formspec_ender_chest) + end + end +}) minetest.register_lbm({ -- Disable active/open trapped chests when loaded because nobody could @@ -1008,7 +1385,7 @@ minetest.register_lbm({ -- Fixes redstone weirdness. label = "Disable active trapped chests", name = "mcl_chests:reset_trapped_chests", - nodenames = { "mcl_chests:trapped_chest_on", "mcl_chests:trapped_chest_on_left", "mcl_chests:trapped_chest_on_right" }, + nodenames = { "mcl_chests:trapped_chest_on_small", "mcl_chests:trapped_chest_on_left", "mcl_chests:trapped_chest_on_right" }, run_at_every_load = true, action = function(pos, node) minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " ..minetest.pos_to_string(pos)) @@ -1016,17 +1393,6 @@ minetest.register_lbm({ end, }) --- Legacy -minetest.register_lbm({ - label = "Update ender chest formspecs (0.60.0)", - name = "mcl_chests:update_ender_chest_formspecs_0_60_0", - nodenames = { "mcl_chests:ender_chest" }, - run_at_every_load = false, - action = function(pos, node) - local meta = minetest.get_meta(pos) - meta:set_string("formspec", formspec_ender_chest) - end, -}) minetest.register_lbm({ label = "Update shulker box formspecs (0.60.0)", name = "mcl_chests:update_shulker_box_formspecs_0_60_0", diff --git a/mods/ITEMS/mcl_chests/models/mcl_chests_chest.b3d b/mods/ITEMS/mcl_chests/models/mcl_chests_chest.b3d new file mode 100644 index 000000000..e82c7e363 Binary files /dev/null and b/mods/ITEMS/mcl_chests/models/mcl_chests_chest.b3d differ diff --git a/mods/ITEMS/mcl_chests/models/mcl_chests_chest.obj b/mods/ITEMS/mcl_chests/models/mcl_chests_chest.obj new file mode 100644 index 000000000..36268146f --- /dev/null +++ b/mods/ITEMS/mcl_chests/models/mcl_chests_chest.obj @@ -0,0 +1,91 @@ +# Blender v2.76 (sub 0) OBJ File: 'chest.small.facedir.blend' +# www.blender.org +mtllib chest.small.facedir.mtl +o chest_upper_upper +v 0.062513 -0.063134 -0.500468 +v 0.062513 0.186920 -0.500468 +v 0.062514 -0.063134 -0.437955 +v 0.062514 0.186920 -0.437955 +v -0.062514 -0.063134 -0.500468 +v -0.062514 0.186920 -0.500468 +v -0.062514 -0.063134 -0.437955 +v -0.062514 0.186920 -0.437955 +v 0.437907 0.061263 -0.438085 +v 0.437907 0.373830 -0.438085 +v 0.437907 0.061263 0.437729 +v 0.437907 0.373830 0.437729 +v -0.437907 0.061263 -0.438085 +v -0.437907 0.373830 -0.438085 +v -0.437907 0.061263 0.437729 +v -0.437907 0.373830 0.437729 +v 0.437595 -0.500754 -0.437772 +v 0.437595 0.124381 -0.437772 +v 0.437595 -0.500754 0.437417 +v 0.437595 0.124381 0.437417 +v -0.437595 -0.500754 -0.437772 +v -0.437595 0.124381 -0.437772 +v -0.437595 -0.500754 0.437417 +v -0.437595 0.124381 0.437417 +vt 0.015625 0.921875 +vt 0.015625 0.984375 +vt 0.000000 0.984375 +vt 0.000000 0.921875 +vt 0.093750 0.921875 +vt 0.093750 0.984375 +vt 0.062500 0.984375 +vt 0.062500 0.921875 +vt 0.046875 0.984375 +vt 0.046875 0.921875 +vt 0.078125 0.984375 +vt 0.078125 1.000000 +vt 0.046875 1.000000 +vt 0.015625 1.000000 +vt 0.218750 0.703125 +vt 0.218750 0.781250 +vt 0.000000 0.781250 +vt 0.000000 0.703125 +vt 0.875000 0.703125 +vt 0.875000 0.781250 +vt 0.656250 0.781250 +vt 0.656250 0.703125 +vt 0.437500 0.781250 +vt 0.437500 0.703125 +vt 0.656250 1.000000 +vt 0.437500 1.000000 +vt 0.218750 1.000000 +vt 0.218750 0.328125 +vt 0.218750 0.484375 +vt -0.000000 0.484375 +vt -0.000000 0.328125 +vt 0.875000 0.328125 +vt 0.875000 0.484375 +vt 0.656250 0.484375 +vt 0.656250 0.328125 +vt 0.437500 0.484375 +vt 0.437500 0.328125 +vn 1.000000 0.000000 -0.000000 +vn 0.000000 0.000000 1.000000 +vn -1.000000 0.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl None +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/8/3 8/7/3 6/9/3 5/10/3 +f 5/10/4 6/9/4 2/2/4 1/1/4 +f 3/9/5 7/11/5 5/12/5 1/13/5 +f 8/13/6 4/14/6 2/2/6 6/9/6 +f 9/15/1 10/16/1 12/17/1 11/18/1 +f 11/19/2 12/20/2 16/21/2 15/22/2 +f 15/22/3 16/21/3 14/23/3 13/24/3 +f 13/24/4 14/23/4 10/16/4 9/15/4 +f 11/25/5 15/26/5 13/23/5 9/21/5 +f 16/26/6 12/27/6 10/16/6 14/23/6 +f 17/28/1 18/29/1 20/30/1 19/31/1 +f 19/32/2 20/33/2 24/34/2 23/35/2 +f 23/35/3 24/34/3 22/36/3 21/37/3 +f 21/37/4 22/36/4 18/29/4 17/28/4 +f 19/22/5 23/24/5 21/36/5 17/34/5 +f 24/24/6 20/15/6 18/29/6 22/36/6 diff --git a/mods/ITEMS/mcl_chests/models/mcl_chests_chest_double.b3d b/mods/ITEMS/mcl_chests/models/mcl_chests_chest_double.b3d new file mode 100644 index 000000000..86c44bfeb Binary files /dev/null and b/mods/ITEMS/mcl_chests/models/mcl_chests_chest_double.b3d differ diff --git a/mods/ITEMS/mcl_chests/models/mcl_chests_shulker.b3d b/mods/ITEMS/mcl_chests/models/mcl_chests_shulker.b3d new file mode 100644 index 000000000..86dde145b Binary files /dev/null and b/mods/ITEMS/mcl_chests/models/mcl_chests_shulker.b3d differ diff --git a/mods/ITEMS/mcl_chests/models/mcl_chests_shulker.obj b/mods/ITEMS/mcl_chests/models/mcl_chests_shulker.obj new file mode 100644 index 000000000..ca12b682e --- /dev/null +++ b/mods/ITEMS/mcl_chests/models/mcl_chests_shulker.obj @@ -0,0 +1,159 @@ +# Blender v2.79 (sub 0) OBJ File: 'shulkerbox2.blend' +# www.blender.org +mtllib shulkerbox2.mtl +o low1_Cube.006 +v -0.500000 -0.500001 0.500000 +v -0.500000 0.062499 0.500000 +v -0.500000 -0.500001 -0.500000 +v -0.500000 0.062499 -0.500000 +v 0.500000 -0.500001 0.500000 +v 0.500000 0.062499 0.500000 +v 0.500000 -0.500001 -0.500000 +v 0.500000 0.062499 -0.500000 +vt 0.250000 0.187500 +vt -0.000000 0.187500 +vt -0.000000 0.312500 +vt 0.250000 0.312500 +vt 1.000000 0.187500 +vt 0.750000 0.187500 +vt 0.750000 0.312500 +vt 1.000000 0.312500 +vt 0.500000 0.187500 +vt 0.500000 0.312500 +vt 0.750000 0.562500 +vt 0.750000 0.312500 +vt 0.500000 0.312500 +vt 0.500000 0.562500 +vt 0.500000 0.562500 +vt 0.250000 0.562500 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 1.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +usemtl None +s off +f 1/1/1 3/2/1 4/3/1 2/4/1 +f 3/5/2 7/6/2 8/7/2 4/8/2 +f 7/6/3 5/9/3 6/10/3 8/7/3 +f 5/9/4 1/1/4 2/4/4 6/10/4 +f 3/11/5 1/12/5 5/13/5 7/14/5 +f 8/15/6 6/10/6 2/4/6 4/16/6 +o top1_Cube.005 +v -0.500313 -0.220552 0.500313 +v -0.500313 0.530073 0.500313 +v -0.500313 -0.220552 -0.500313 +v -0.500313 0.530073 -0.500313 +v 0.500313 -0.220552 0.500313 +v 0.500313 0.530073 0.500313 +v 0.500313 -0.220552 -0.500313 +v 0.500313 0.530073 -0.500313 +vt 0.250000 0.562500 +vt -0.000000 0.562500 +vt -0.000000 0.750000 +vt 0.250000 0.750000 +vt 1.000000 0.562500 +vt 0.750000 0.562500 +vt 0.750000 0.750000 +vt 1.000000 0.750000 +vt 0.500000 0.562500 +vt 0.500000 0.750000 +vt 0.750000 1.000000 +vt 0.750000 0.750000 +vt 0.500000 0.750000 +vt 0.500000 1.000000 +vt 0.500000 1.000000 +vt 0.250000 1.000000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 1.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +usemtl None +s off +f 9/17/7 11/18/7 12/19/7 10/20/7 +f 11/21/8 15/22/8 16/23/8 12/24/8 +f 15/22/9 13/25/9 14/26/9 16/23/9 +f 13/25/10 9/17/10 10/20/10 14/26/10 +f 11/27/11 9/28/11 13/29/11 15/30/11 +f 16/31/12 14/26/12 10/20/12 12/32/12 +o top2_Cube.002 +v -0.500247 -0.220392 0.500247 +v -0.500247 0.530234 0.500247 +v -0.500247 -0.220392 -0.500378 +v -0.500247 0.530234 -0.500378 +v 0.500378 -0.220392 0.500247 +v 0.500378 0.530234 0.500247 +v 0.500378 -0.220392 -0.500378 +v 0.500378 0.530234 -0.500378 +vt 0.250000 0.562500 +vt 0.250000 0.750000 +vt -0.000000 0.750000 +vt -0.000000 0.562500 +vt 1.000000 0.562500 +vt 1.000000 0.750000 +vt 0.750000 0.750000 +vt 0.750000 0.562500 +vt 0.500000 0.750000 +vt 0.500000 0.562500 +vt 0.750000 1.000000 +vt 0.500000 1.000000 +vt 0.500000 0.750000 +vt 0.750000 0.750000 +vt 0.500000 1.000000 +vt 0.250000 1.000000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl None +s off +f 17/33/13 18/34/13 20/35/13 19/36/13 +f 19/37/14 20/38/14 24/39/14 23/40/14 +f 23/40/15 24/39/15 22/41/15 21/42/15 +f 21/42/16 22/41/16 18/34/16 17/33/16 +f 19/43/17 23/44/17 21/45/17 17/46/17 +f 24/47/18 20/48/18 18/34/18 22/41/18 +o low2_Cube.001 +v -0.499935 -0.499936 0.499935 +v -0.499935 0.062565 0.499935 +v -0.499935 -0.499936 -0.500066 +v -0.499935 0.062565 -0.500066 +v 0.500066 -0.499936 0.499935 +v 0.500066 0.062565 0.499935 +v 0.500066 -0.499936 -0.500066 +v 0.500066 0.062565 -0.500066 +vt 0.250000 0.187500 +vt 0.250000 0.312500 +vt -0.000000 0.312500 +vt -0.000000 0.187500 +vt 1.000000 0.187500 +vt 1.000000 0.312500 +vt 0.750000 0.312500 +vt 0.750000 0.187500 +vt 0.500000 0.312500 +vt 0.500000 0.187500 +vt 0.750000 0.562500 +vt 0.500000 0.562500 +vt 0.500000 0.312500 +vt 0.750000 0.312500 +vt 0.500000 0.562500 +vt 0.250000 0.562500 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl None +s off +f 25/49/19 26/50/19 28/51/19 27/52/19 +f 27/53/20 28/54/20 32/55/20 31/56/20 +f 31/56/21 32/55/21 30/57/21 29/58/21 +f 29/58/22 30/57/22 26/50/22 25/49/22 +f 27/59/23 31/60/23 29/61/23 25/62/23 +f 32/63/24 28/64/24 26/50/24 30/57/24 diff --git a/mods/ITEMS/mcl_chests/sounds/attributions.txt b/mods/ITEMS/mcl_chests/sounds/attributions.txt new file mode 100644 index 000000000..2943aecc0 --- /dev/null +++ b/mods/ITEMS/mcl_chests/sounds/attributions.txt @@ -0,0 +1,2 @@ +default_chest_open and default_chest_close - Taken from minetest_game https://github.com/minetest/minetest_game +mcl_chests_enderchest_open and mcl_chests_enderchest_close - https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/resource-packs/1245112-snowsong-the-epic-sound-pack-sound-resource-pack diff --git a/mods/ITEMS/mcl_chests/sounds/default_chest_close.ogg b/mods/ITEMS/mcl_chests/sounds/default_chest_close.ogg new file mode 100644 index 000000000..068d9002f Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/default_chest_close.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/default_chest_open.ogg b/mods/ITEMS/mcl_chests/sounds/default_chest_open.ogg new file mode 100644 index 000000000..40b0b9341 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/default_chest_open.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_enderchest_close.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_enderchest_close.ogg new file mode 100644 index 000000000..7ecf4c98d Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_enderchest_close.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_enderchest_open.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_enderchest_open.ogg new file mode 100644 index 000000000..33bd84f8d Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_enderchest_open.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.0.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.0.ogg new file mode 100644 index 000000000..e97e22b36 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.0.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.1.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.1.ogg new file mode 100644 index 000000000..d61c81ccb Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.1.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.2.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.2.ogg new file mode 100644 index 000000000..865e81909 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.2.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.3.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.3.ogg new file mode 100644 index 000000000..20388a403 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.3.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.4.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.4.ogg new file mode 100644 index 000000000..4a3616448 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_close.4.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.0.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.0.ogg new file mode 100644 index 000000000..c63da1d10 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.0.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.1.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.1.ogg new file mode 100644 index 000000000..1dc602807 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.1.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.2.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.2.ogg new file mode 100644 index 000000000..e1af5da85 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.2.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.3.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.3.ogg new file mode 100644 index 000000000..d7ad1a3a9 Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.3.ogg differ diff --git a/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.4.ogg b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.4.ogg new file mode 100644 index 000000000..faf751c9a Binary files /dev/null and b/mods/ITEMS/mcl_chests/sounds/mcl_chests_shulker_open.4.ogg differ diff --git a/mods/ITEMS/mcl_chests/textures/mcl_chests_blank.png b/mods/ITEMS/mcl_chests/textures/mcl_chests_blank.png new file mode 100644 index 000000000..baee128d4 Binary files /dev/null and b/mods/ITEMS/mcl_chests/textures/mcl_chests_blank.png differ diff --git a/mods/ITEMS/mcl_chests/textures/mcl_chests_trapped.png b/mods/ITEMS/mcl_chests/textures/mcl_chests_trapped.png new file mode 100644 index 000000000..de21d8f2f Binary files /dev/null and b/mods/ITEMS/mcl_chests/textures/mcl_chests_trapped.png differ diff --git a/mods/ITEMS/mcl_chests/textures/mcl_chests_trapped_double.png b/mods/ITEMS/mcl_chests/textures/mcl_chests_trapped_double.png new file mode 100644 index 000000000..95f768f97 Binary files /dev/null and b/mods/ITEMS/mcl_chests/textures/mcl_chests_trapped_double.png differ diff --git a/mods/ITEMS/mcl_colorblocks/init.lua b/mods/ITEMS/mcl_colorblocks/init.lua index dad97f62f..4981b39b0 100644 --- a/mods/ITEMS/mcl_colorblocks/init.lua +++ b/mods/ITEMS/mcl_colorblocks/init.lua @@ -97,7 +97,7 @@ for _, row in ipairs(block.dyes) do _doc_items_create_entry = create_entry, _doc_items_entry_name = ename_cp, tiles = {"mcl_colorblocks_concrete_powder_"..name..".png"}, - groups = {handy=1,shovely=1, concrete_powder=1,building_block=1,falling_node=1, material_sand=1}, + groups = {handy=1,shovely=1, concrete_powder=1,building_block=1,falling_node=1, material_sand=1, float=1}, stack_max = 64, is_ground_content = false, sounds = mcl_sounds.node_sound_sand_defaults(), @@ -208,11 +208,20 @@ minetest.register_abm({ neighbors = {"group:water"}, action = function(pos, node) local harden_to = minetest.registered_nodes[node.name]._mcl_colorblocks_harden_to - -- It should be impossible for harden_to to be nil, but a Minetest bug might call - -- the ABM on the new concrete node, which isn't part of this ABM! - if harden_to then - node.name = harden_to - minetest.set_node(pos, node) - end + -- It should be impossible for harden_to to be nil, but a Minetest bug might call + -- the ABM on the new concrete node, which isn't part of this ABM! + if harden_to then + node.name = harden_to + --Fix "float" group not lowering concrete into the water by 1. + local water_pos = { x = pos.x, y = pos.y-1, z = pos.z } + local water_node = minetest.get_node(water_pos) + if minetest.get_item_group(water_node.name, "water") == 0 then + minetest.set_node(pos, node) + else + minetest.set_node(water_pos,node) + minetest.set_node(pos, {name = "air"}) + minetest.check_for_falling(pos) -- Update C. Powder that stacked above so they fall down after setting air. + end + end end, }) diff --git a/mods/ITEMS/mcl_compass/depends.txt b/mods/ITEMS/mcl_compass/depends.txt index 0389d3050..53261d53c 100644 --- a/mods/ITEMS/mcl_compass/depends.txt +++ b/mods/ITEMS/mcl_compass/depends.txt @@ -2,4 +2,3 @@ mcl_core mcl_worlds mesecons doc? -mcl_enchanting diff --git a/mods/ITEMS/mcl_compass/init.lua b/mods/ITEMS/mcl_compass/init.lua index 66553dfc0..b0cf5627e 100644 --- a/mods/ITEMS/mcl_compass/init.lua +++ b/mods/ITEMS/mcl_compass/init.lua @@ -57,9 +57,6 @@ minetest.register_globalstep(function(dtime) if minetest.get_item_group(stack:get_name(), "compass") ~= 0 and minetest.get_item_group(stack:get_name(), "compass")-1 ~= compass_image then local itemname = "mcl_compass:"..compass_image - if mcl_enchanting.is_enchanted(stack:get_name()) then - itemname = itemname .. "_enchanted" - end stack:set_name(itemname) player:get_inventory():set_stack("main", j, stack) end @@ -98,7 +95,7 @@ for i,img in ipairs(images) do inventory_image = img, wield_image = img, stack_max = 64, - groups = {not_in_creative_inventory=inv, compass=i, tool=1, disable_repair=1, enchantability=1 } + groups = {not_in_creative_inventory=inv, compass=i, tool=1, disable_repair=1 } }) -- Help aliases. Makes sure the lookup tool works correctly diff --git a/mods/ITEMS/mcl_core/craftitems.lua b/mods/ITEMS/mcl_core/craftitems.lua index 122445095..7cffcb785 100644 --- a/mods/ITEMS/mcl_core/craftitems.lua +++ b/mods/ITEMS/mcl_core/craftitems.lua @@ -144,17 +144,52 @@ minetest.register_craftitem("mcl_core:apple", { _mcl_saturation = 2.4, }) --- TODO: Status effects +local gapple_hunger_restore = minetest.item_eat(4) + +local function eat_gapple(itemstack, placer, pointed_thing) + if pointed_thing.type == "node" then + local node = minetest.get_node(pointed_thing.under) + if placer and not placer:get_player_control().sneak then + if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then + return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack + end + end + elseif pointed_thing.type == "object" then + return itemstack + end + + local regen_duration, absorbtion_factor = 5, 1 + if itemstack:get_name() == "mcl_core:apple_gold_enchanted" then + regen_duration, absorbtion_factor = 20, 4 + mcl_potions.fire_resistance_func(placer, 1, 300) + mcl_potions.leaping_func(placer, 1, 300) + end + mcl_potions.swiftness_func(placer, absorbtion_factor, 120) -- TODO: Absorbtion + mcl_potions.regeneration_func(placer, 2, regen_duration) + return gapple_hunger_restore(itemstack, placer, pointed_thing) +end + minetest.register_craftitem("mcl_core:apple_gold", { - -- TODO: Add special highlight color when this item is special + -- TODO: Add special highlight color description = S("Golden Apple"), _doc_items_longdesc = S("Golden apples are precious food items which can be eaten."), wield_image = "mcl_core_apple_golden.png", inventory_image = "mcl_core_apple_golden.png", stack_max = 64, - -- TODO: Reduce to 4 when it's ready - on_place = minetest.item_eat(20), - on_secondary_use = minetest.item_eat(20), - groups = { food = 2, eatable = 20, can_eat_when_full = 1 }, + on_place = eat_gapple, + on_secondary_use = eat_gapple, + groups = { food = 2, eatable = 4, can_eat_when_full = 1 }, + _mcl_saturation = 9.6, +}) + +minetest.register_craftitem("mcl_core:apple_gold_enchanted", { + description = S("Enchanted Golden Apple"), + _doc_items_longdesc = S("Golden apples are precious food items which can be eaten."), + wield_image = "mcl_core_apple_golden.png" .. mcl_enchanting.overlay, + inventory_image = "mcl_core_apple_golden.png" .. mcl_enchanting.overlay, + stack_max = 64, + on_place = eat_gapple, + on_secondary_use = eat_gapple, + groups = { food = 2, eatable = 4, can_eat_when_full = 1 }, _mcl_saturation = 9.6, }) diff --git a/mods/ITEMS/mcl_core/depends.txt b/mods/ITEMS/mcl_core/depends.txt index 60ece3906..fb9861814 100644 --- a/mods/ITEMS/mcl_core/depends.txt +++ b/mods/ITEMS/mcl_core/depends.txt @@ -5,3 +5,4 @@ mcl_util mcl_worlds doc_items doc? +mcl_enchanting diff --git a/mods/ITEMS/mcl_core/functions.lua b/mods/ITEMS/mcl_core/functions.lua index 0d9a1f601..3d47336f3 100644 --- a/mods/ITEMS/mcl_core/functions.lua +++ b/mods/ITEMS/mcl_core/functions.lua @@ -39,8 +39,8 @@ minetest.register_abm({ (water[w].y == pos.y and (water[w].x == pos.x or water[w].z == pos.z))) then minetest.set_node(pos, {name="mcl_core:obsidian"}) minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true) - -- Flowing water above flowing lava: Lava turns into cobblestone - elseif watertype == "flowing" and lavatype == "flowing" and water[w].y > pos.y and water[w].x == pos.x and water[w].z == pos.z then + -- water above flowing lava: Lava turns into cobblestone + elseif lavatype == "flowing" and water[w].y > pos.y and water[w].x == pos.x and water[w].z == pos.z then minetest.set_node(pos, {name="mcl_core:cobble"}) minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true) end @@ -899,98 +899,118 @@ local treelight = 9 local sapling_grow_action = function(tree_id, soil_needed, one_by_one, two_by_two, sapling) return function(pos) + local meta = minetest.get_meta(pos) + if meta:get("grown") then return end -- Checks if the sapling at pos has enough light and the correct soil - local sapling_is_growable = function(pos) - local light = minetest.get_node_light(pos) - local soilnode = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}) - local soiltype = minetest.get_item_group(soilnode.name, "soil_sapling") - return soiltype >= soil_needed and light and light >= treelight + local light = minetest.get_node_light(pos) + if not light then return end + local low_light = (light < treelight) + + local delta = 1 + local current_game_time = minetest.get_day_count() + minetest.get_timeofday() + + local last_game_time = tonumber(meta:get_string("last_gametime")) + meta:set_string("last_gametime", tostring(current_game_time)) + + if last_game_time then + delta = current_game_time - last_game_time + elseif low_light then + return end - if sapling_is_growable(pos) then - -- Increase and check growth stage - local meta = minetest.get_meta(pos) - local stage = meta:get_int("stage") - if stage == nil then stage = 0 end - stage = stage + 1 - if stage >= 3 then - -- This sapling grows in a special way when there are 4 saplings in a 2×2 pattern - if two_by_two then - -- Check 8 surrounding saplings and try to find a 2×2 pattern - local is_sapling = function(pos, sapling) - return minetest.get_node(pos).name == sapling - end - local p2 = {x=pos.x+1, y=pos.y, z=pos.z} - local p3 = {x=pos.x, y=pos.y, z=pos.z-1} - local p4 = {x=pos.x+1, y=pos.y, z=pos.z-1} - local p5 = {x=pos.x-1, y=pos.y, z=pos.z-1} - local p6 = {x=pos.x-1, y=pos.y, z=pos.z} - local p7 = {x=pos.x-1, y=pos.y, z=pos.z+1} - local p8 = {x=pos.x, y=pos.y, z=pos.z+1} - local p9 = {x=pos.x+1, y=pos.y, z=pos.z+1} - local s2 = is_sapling(p2, sapling) - local s3 = is_sapling(p3, sapling) - local s4 = is_sapling(p4, sapling) - local s5 = is_sapling(p5, sapling) - local s6 = is_sapling(p6, sapling) - local s7 = is_sapling(p7, sapling) - local s8 = is_sapling(p8, sapling) - local s9 = is_sapling(p9, sapling) - -- In a 9×9 field there are 4 possible 2×2 squares. We check them all. - if s2 and s3 and s4 and check_tree_growth(pos, tree_id, { two_by_two = true }) then - -- Success: Remove saplings and place tree - minetest.remove_node(pos) - minetest.remove_node(p2) - minetest.remove_node(p3) - minetest.remove_node(p4) - mcl_core.generate_tree(pos, tree_id, { two_by_two = true }) - return - elseif s3 and s5 and s6 and check_tree_growth(p6, tree_id, { two_by_two = true }) then - minetest.remove_node(pos) - minetest.remove_node(p3) - minetest.remove_node(p5) - minetest.remove_node(p6) - mcl_core.generate_tree(p6, tree_id, { two_by_two = true }) - return - elseif s6 and s7 and s8 and check_tree_growth(p7, tree_id, { two_by_two = true }) then - minetest.remove_node(pos) - minetest.remove_node(p6) - minetest.remove_node(p7) - minetest.remove_node(p8) - mcl_core.generate_tree(p7, tree_id, { two_by_two = true }) - return - elseif s2 and s8 and s9 and check_tree_growth(p8, tree_id, { two_by_two = true }) then - minetest.remove_node(pos) - minetest.remove_node(p2) - minetest.remove_node(p8) - minetest.remove_node(p9) - mcl_core.generate_tree(p8, tree_id, { two_by_two = true }) - return - end - end - if one_by_one and tree_id == OAK_TREE_ID then - -- There is a chance that this tree wants to grow as a balloon oak - if math.random(1, 12) == 1 then - -- Check if there is room for that - if check_tree_growth(pos, tree_id, { balloon = true }) then - minetest.set_node(pos, {name="air"}) - mcl_core.generate_tree(pos, tree_id, { balloon = true }) - return - end - end - end + if low_light then + if delta < 1.2 then return end + if minetest.get_node_light(pos, 0.5) < treelight then return end + end - -- If this sapling can grow alone - if one_by_one and check_tree_growth(pos, tree_id) then - -- Single sapling - minetest.set_node(pos, {name="air"}) - local r = math.random(1, 12) - mcl_core.generate_tree(pos, tree_id) + -- TODO: delta is [days] missed in inactive area. Currently we just add it to stage, which is far from a perfect calculation... + + local soilnode = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}) + local soiltype = minetest.get_item_group(soilnode.name, "soil_sapling") + if soiltype < soil_needed then return end + + -- Increase and check growth stage + local meta = minetest.get_meta(pos) + local stage = meta:get_int("stage") + if stage == nil then stage = 0 end + stage = stage + math.max(1, math.floor(delta)) + if stage >= 3 then + meta:set_string("grown", "true") + -- This sapling grows in a special way when there are 4 saplings in a 2×2 pattern + if two_by_two then + -- Check 8 surrounding saplings and try to find a 2×2 pattern + local is_sapling = function(pos, sapling) + return minetest.get_node(pos).name == sapling + end + local p2 = {x=pos.x+1, y=pos.y, z=pos.z} + local p3 = {x=pos.x, y=pos.y, z=pos.z-1} + local p4 = {x=pos.x+1, y=pos.y, z=pos.z-1} + local p5 = {x=pos.x-1, y=pos.y, z=pos.z-1} + local p6 = {x=pos.x-1, y=pos.y, z=pos.z} + local p7 = {x=pos.x-1, y=pos.y, z=pos.z+1} + local p8 = {x=pos.x, y=pos.y, z=pos.z+1} + local p9 = {x=pos.x+1, y=pos.y, z=pos.z+1} + local s2 = is_sapling(p2, sapling) + local s3 = is_sapling(p3, sapling) + local s4 = is_sapling(p4, sapling) + local s5 = is_sapling(p5, sapling) + local s6 = is_sapling(p6, sapling) + local s7 = is_sapling(p7, sapling) + local s8 = is_sapling(p8, sapling) + local s9 = is_sapling(p9, sapling) + -- In a 9×9 field there are 4 possible 2×2 squares. We check them all. + if s2 and s3 and s4 and check_tree_growth(pos, tree_id, { two_by_two = true }) then + -- Success: Remove saplings and place tree + minetest.remove_node(pos) + minetest.remove_node(p2) + minetest.remove_node(p3) + minetest.remove_node(p4) + mcl_core.generate_tree(pos, tree_id, { two_by_two = true }) + return + elseif s3 and s5 and s6 and check_tree_growth(p6, tree_id, { two_by_two = true }) then + minetest.remove_node(pos) + minetest.remove_node(p3) + minetest.remove_node(p5) + minetest.remove_node(p6) + mcl_core.generate_tree(p6, tree_id, { two_by_two = true }) + return + elseif s6 and s7 and s8 and check_tree_growth(p7, tree_id, { two_by_two = true }) then + minetest.remove_node(pos) + minetest.remove_node(p6) + minetest.remove_node(p7) + minetest.remove_node(p8) + mcl_core.generate_tree(p7, tree_id, { two_by_two = true }) + return + elseif s2 and s8 and s9 and check_tree_growth(p8, tree_id, { two_by_two = true }) then + minetest.remove_node(pos) + minetest.remove_node(p2) + minetest.remove_node(p8) + minetest.remove_node(p9) + mcl_core.generate_tree(p8, tree_id, { two_by_two = true }) return end - else - meta:set_int("stage", stage) end + if one_by_one and tree_id == OAK_TREE_ID then + -- There is a chance that this tree wants to grow as a balloon oak + if math.random(1, 12) == 1 then + -- Check if there is room for that + if check_tree_growth(pos, tree_id, { balloon = true }) then + minetest.set_node(pos, {name="air"}) + mcl_core.generate_tree(pos, tree_id, { balloon = true }) + return + end + end + end + -- If this sapling can grow alone + if one_by_one and check_tree_growth(pos, tree_id) then + -- Single sapling + minetest.set_node(pos, {name="air"}) + local r = math.random(1, 12) + mcl_core.generate_tree(pos, tree_id) + return + end + else + meta:set_int("stage", stage) end end end @@ -1039,7 +1059,14 @@ minetest.register_abm({ neighbors = {"group:soil_sapling"}, interval = 25, chance = 2, - action = grow_oak, + action = grow_oak +}) +minetest.register_lbm({ + label = "Add growth for unloaded oak tree", + name = "mcl_core:lbm_oak", + nodenames = {"mcl_core:sapling"}, + run_at_every_load = true, + action = grow_oak }) -- Dark oak tree @@ -1049,7 +1076,14 @@ minetest.register_abm({ neighbors = {"group:soil_sapling"}, interval = 25, chance = 2, - action = grow_dark_oak, + action = grow_dark_oak +}) +minetest.register_lbm({ + label = "Add growth for unloaded dark oak tree", + name = "mcl_core:lbm_dark_oak", + nodenames = {"mcl_core:darksapling"}, + run_at_every_load = true, + action = grow_dark_oak }) -- Jungle Tree @@ -1059,7 +1093,14 @@ minetest.register_abm({ neighbors = {"group:soil_sapling"}, interval = 25, chance = 2, - action = grow_jungle_tree, + action = grow_jungle_tree +}) +minetest.register_lbm({ + label = "Add growth for unloaded jungle tree", + name = "mcl_core:lbm_jungle_tree", + nodenames = {"mcl_core:junglesapling"}, + run_at_every_load = true, + action = grow_jungle_tree }) -- Spruce tree @@ -1071,6 +1112,13 @@ minetest.register_abm({ chance = 2, action = grow_spruce }) +minetest.register_lbm({ + label = "Add growth for unloaded spruce tree", + name = "mcl_core:lbm_spruce", + nodenames = {"mcl_core:sprucesapling"}, + run_at_every_load = true, + action = grow_spruce +}) -- Birch tree minetest.register_abm({ @@ -1079,7 +1127,14 @@ minetest.register_abm({ neighbors = {"group:soil_sapling"}, interval = 25, chance = 2, - action = grow_birch, + action = grow_birch +}) +minetest.register_lbm({ + label = "Add growth for unloaded birch tree", + name = "mcl_core:lbm_birch", + nodenames = {"mcl_core:birchsapling"}, + run_at_every_load = true, + action = grow_birch }) -- Acacia tree @@ -1089,7 +1144,14 @@ minetest.register_abm({ neighbors = {"group:soil_sapling"}, interval = 20, chance = 2, - action = grow_acacia, + action = grow_acacia +}) +minetest.register_lbm({ + label = "Add growth for unloaded acacia tree", + name = "mcl_core:lbm_acacia", + nodenames = {"mcl_core:acaciasapling"}, + run_at_every_load = true, + action = grow_acacia }) local function leafdecay_particles(pos, node) @@ -1450,10 +1512,11 @@ end -- * tiles: Optional custom tiles -- * sounds: Optional custom sounds -- * clear_colorization: Optional. If true, will clear all paramtype2="color" related node def. fields +-- * desc: Item description -- -- The snowable nodes also MUST have _mcl_snowed defined to contain the name -- of the snowed node. -mcl_core.register_snowed_node = function(itemstring_snowed, itemstring_clear, tiles, sounds, clear_colorization) +mcl_core.register_snowed_node = function(itemstring_snowed, itemstring_clear, tiles, sounds, clear_colorization, desc) local def = table.copy(minetest.registered_nodes[itemstring_clear]) local create_doc_alias if def.description then @@ -1462,7 +1525,7 @@ mcl_core.register_snowed_node = function(itemstring_snowed, itemstring_clear, ti create_doc_alias = false end -- Just some group clearing - def.description = nil + def.description = desc def._doc_items_longdesc = nil def._doc_items_usagehelp = nil def._doc_items_create_entry = false @@ -1503,6 +1566,8 @@ mcl_core.register_snowed_node = function(itemstring_snowed, itemstring_clear, ti def.sounds = sounds end + def._mcl_silk_touch_drop = {itemstring_clear} + -- Register stuff minetest.register_node(itemstring_snowed, def) diff --git a/mods/ITEMS/mcl_core/nodes_base.lua b/mods/ITEMS/mcl_core/nodes_base.lua index 82ce39e1a..03581dfa3 100644 --- a/mods/ITEMS/mcl_core/nodes_base.lua +++ b/mods/ITEMS/mcl_core/nodes_base.lua @@ -182,6 +182,7 @@ minetest.register_node("mcl_core:stone_with_lapis", { sounds = mcl_sounds.node_sound_stone_defaults(), _mcl_blast_resistance = 3, _mcl_hardness = 3, + _mcl_silk_touch_drop = true, _mcl_fortune_drop = mcl_core.fortune_drop_ore, }) @@ -380,7 +381,7 @@ minetest.register_node("mcl_core:dirt_with_grass", { _mcl_hardness = 0.6, _mcl_silk_touch_drop = true, }) -mcl_core.register_snowed_node("mcl_core:dirt_with_grass_snow", "mcl_core:dirt_with_grass", nil, nil, true) +mcl_core.register_snowed_node("mcl_core:dirt_with_grass_snow", "mcl_core:dirt_with_grass", nil, nil, true, S("Dirt with Snow")) minetest.register_node("mcl_core:grass_path", { tiles = {"mcl_core_grass_path_top.png", "default_dirt.png", "mcl_core_grass_path_side.png"}, @@ -424,7 +425,7 @@ minetest.register_node("mcl_core:mycelium", { _mcl_hardness = 0.6, _mcl_silk_touch_drop = true, }) -mcl_core.register_snowed_node("mcl_core:mycelium_snow", "mcl_core:mycelium") +mcl_core.register_snowed_node("mcl_core:mycelium_snow", "mcl_core:mycelium", nil, nil, false, S("Mycelium with Snow")) minetest.register_node("mcl_core:podzol", { description = S("Podzol"), @@ -441,7 +442,7 @@ minetest.register_node("mcl_core:podzol", { _mcl_hardness = 0.6, _mcl_silk_touch_drop = true, }) -mcl_core.register_snowed_node("mcl_core:podzol_snow", "mcl_core:podzol") +mcl_core.register_snowed_node("mcl_core:podzol_snow", "mcl_core:podzol", nil, nil, false, S("Podzol with Snow")) minetest.register_node("mcl_core:dirt", { description = S("Dirt"), @@ -503,7 +504,7 @@ minetest.register_node("mcl_core:gravel", { {items = {'mcl_core:gravel'}} } }, - [3] = "mcl_core:flint" + [3] = "mcl_core:flint", }, }) @@ -924,7 +925,7 @@ for i=1,8 do desc = S("Top Snow") tt_help = S("Stackable") longdesc = S("Top snow is a layer of snow. It melts near light sources other than the sun with a light level of 12 or higher.").."\n"..S("Top snow can be stacked and has one of 8 different height levels. At levels 2-8, top snow is collidable. Top snow drops 2-9 snowballs, depending on its height.") - usagehelp = S("This block can only be placed on full solid blocks and on another top snow (which increases its height).") + usagehelp = S("This block can only be placed on full solid blocks and on another top snow (which increases its height).") walkable = false else id = "mcl_core:snow_"..i @@ -1028,7 +1029,7 @@ for i=1,8 do drop = "mcl_throwing:snowball "..(i+1), _mcl_blast_resistance = 0.1, _mcl_hardness = 0.1, - _mcl_silk_touch_drop = true, + _mcl_silk_touch_drop = {"mcl_core:snow " .. (i+1)}, }) end diff --git a/mods/ITEMS/mcl_core/nodes_liquid.lua b/mods/ITEMS/mcl_core/nodes_liquid.lua index 57b3e588f..4769975b9 100644 --- a/mods/ITEMS/mcl_core/nodes_liquid.lua +++ b/mods/ITEMS/mcl_core/nodes_liquid.lua @@ -142,7 +142,7 @@ minetest.register_node("mcl_core:lava_flowing", { damage_per_second = 4*2, _mcl_node_death_message = lava_death_messages, post_effect_color = {a=245, r=208, g=73, b=10}, - groups = { lava=3, liquid=2, destroys_items=1, not_in_creative_inventory=1, dig_by_piston=1}, + groups = { lava=3, liquid=2, destroys_items=1, not_in_creative_inventory=1, dig_by_piston=1, set_on_fire=15}, _mcl_blast_resistance = 100, -- Hardness intentionally set to infinite instead of 100 (Minecraft value) to avoid problems in creative mode _mcl_hardness = -1, @@ -198,7 +198,7 @@ S("• When lava is directly above water, the water turns into stone."), _mcl_node_death_message = lava_death_messages, post_effect_color = {a=245, r=208, g=73, b=10}, stack_max = 64, - groups = { lava=3, lava_source=1, liquid=2, destroys_items=1, not_in_creative_inventory=1, dig_by_piston=1}, + groups = { lava=3, lava_source=1, liquid=2, destroys_items=1, not_in_creative_inventory=1, dig_by_piston=1, set_on_fire=15}, _mcl_blast_resistance = 100, -- Hardness intentionally set to infinite instead of 100 (Minecraft value) to avoid problems in creative mode _mcl_hardness = -1, diff --git a/mods/ITEMS/mcl_doors/api_doors.lua b/mods/ITEMS/mcl_doors/api_doors.lua index 15d83a75c..07a5623f3 100644 --- a/mods/ITEMS/mcl_doors/api_doors.lua +++ b/mods/ITEMS/mcl_doors/api_doors.lua @@ -152,7 +152,7 @@ function mcl_doors:register_door(name, def) elseif p2 == 3 then pt_left.z = pt_left.z-1 end - + local left_node = minetest.get_node(pt_left) -- Set door nodes @@ -182,14 +182,14 @@ function mcl_doors:register_door(name, def) meta1:set_int("is_open", 0) meta2:set_int("is_open", 0) - + if not minetest.is_creative_enabled(pn) then itemstack:take_item() end on_place_node(pt, minetest.get_node(pt), placer, nu, itemstack, pointed_thing) on_place_node(pt2, minetest.get_node(pt2), placer, minetest.get_node({x=ptu.x,y=ptu.y+1,z=ptu.z}), itemstack, pointed_thing) - + return itemstack end, }) @@ -275,7 +275,7 @@ function mcl_doors:register_door(name, def) paramtype2 = "facedir", sunlight_propagates = true, is_ground_content = false, - drop = name, + drop = "", drawtype = "nodebox", node_box = { type = "fixed", @@ -291,6 +291,7 @@ function mcl_doors:register_door(name, def) sounds = def.sounds, after_destruct = function(bottom, oldnode) + minetest.add_item(bottom, name) local top = { x = bottom.x, y = bottom.y + 1, z = bottom.z } if minetest.get_node(bottom).name ~= name.."_b_2" and minetest.get_node(top).name == name.."_t_1" then minetest.remove_node(top) @@ -394,7 +395,7 @@ function mcl_doors:register_door(name, def) paramtype2 = "facedir", sunlight_propagates = true, is_ground_content = false, - drop = name, + drop = "", drawtype = "nodebox", node_box = { type = "fixed", @@ -410,6 +411,7 @@ function mcl_doors:register_door(name, def) sounds = def.sounds, after_destruct = function(bottom, oldnode) + minetest.add_item(bottom, name) local top = { x = bottom.x, y = bottom.y + 1, z = bottom.z } if minetest.get_node(bottom).name ~= name.."_b_1" and minetest.get_node(top).name == name.."_t_2" then minetest.remove_node(top) diff --git a/mods/ITEMS/mcl_enchanting/LICENSE b/mods/ITEMS/mcl_enchanting/LICENSE deleted file mode 100644 index 9cecc1d46..000000000 --- a/mods/ITEMS/mcl_enchanting/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/mods/ITEMS/mcl_enchanting/enchantments.lua b/mods/ITEMS/mcl_enchanting/enchantments.lua index c3bfa05df..75969f516 100644 --- a/mods/ITEMS/mcl_enchanting/enchantments.lua +++ b/mods/ITEMS/mcl_enchanting/enchantments.lua @@ -25,6 +25,8 @@ end requires_tool = false, treasure = false, power_range_table = {{1, 41}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- implemented via on_enchant and additions in mobs_mc; Slowness IV part unimplemented @@ -34,7 +36,7 @@ mcl_enchanting.enchantments.bane_of_arthropods = { primary = {sword = true}, secondary = {axe = true}, disallow = {}, - incompatible = {smite = true, shaprness = true}, + incompatible = {smite = true, sharpness = true}, weight = 5, description = S("Increases damage and applies Slowness IV to arthropod mobs (spiders, cave spiders, silverfish and endermites)."), curse = false, @@ -42,6 +44,8 @@ mcl_enchanting.enchantments.bane_of_arthropods = { requires_tool = false, treasure = false, power_range_table = {{5, 25}, {13, 33}, {21, 41}, {29, 49}, {37, 57}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- implemented in mcl_armor @@ -59,6 +63,8 @@ mcl_enchanting.enchantments.blast_protection = { requires_tool = false, treasure = false, power_range_table = {{5, 13}, {13, 21}, {21, 29}, {29, 37}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- requires missing MineClone2 feature @@ -76,6 +82,8 @@ mcl_enchanting.enchantments.blast_protection = { requires_tool = false, treasure = false, power_range_table = {{25, 50}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- implemented in mcl_armor @@ -93,6 +101,8 @@ mcl_enchanting.enchantments.curse_of_binding = { requires_tool = false, treasure = true, power_range_table = {{25, 50}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- implemented in mcl_death_drop @@ -110,6 +120,8 @@ mcl_enchanting.enchantments.curse_of_vanishing = { requires_tool = false, treasure = true, power_range_table = {{25, 50}}, + inv_combat_tab = true, + inv_tool_tab = true, } -- unimplemented @@ -127,6 +139,8 @@ mcl_enchanting.enchantments.curse_of_vanishing = { requires_tool = false, treasure = false, power_range_table = {{10, 25}, {20, 35}, {30, 45}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- implemented via on_enchant @@ -154,6 +168,8 @@ mcl_enchanting.enchantments.efficiency = { requires_tool = false, treasure = false, power_range_table = {{1, 61}, {11, 71}, {21, 81}, {31, 91}, {41, 101}}, + inv_combat_tab = false, + inv_tool_tab = true, } -- implemented in mcl_armor @@ -170,10 +186,12 @@ mcl_enchanting.enchantments.feather_falling = { requires_tool = false, treasure = false, power_range_table = {{5, 11}, {11, 17}, {17, 23}, {23, 29}}, + inv_combat_tab = true, + inv_tool_tab = false, } --- requires missing MineClone2 feature ---[[mcl_enchanting.enchantments.fire_aspect = { +-- implemented in mcl_mobs and via register_on_punchplayer callback +mcl_enchanting.enchantments.fire_aspect = { name = S("Fire Aspect"), max_level = 2, primary = {sword = true}, @@ -187,7 +205,21 @@ mcl_enchanting.enchantments.feather_falling = { requires_tool = false, treasure = false, power_range_table = {{10, 61}, {30, 71}}, -}]]-- + inv_combat_tab = true, + inv_tool_tab = false, +} + +minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage) + if hitter and hitter:is_player() then + local wielditem = hitter:get_wielded_item() + if wielditem then + local fire_aspect_level = mcl_enchanting.get_enchantment(wielditem, "fire_aspect") + if fire_aspect_level > 0 then + mcl_burning.set_on_fire(player, fire_aspect_level * 4 - 1, 1, hitter:get_player_name()) + end + end + end +end) -- implemented in mcl_armor mcl_enchanting.enchantments.fire_protection = { @@ -204,10 +236,11 @@ mcl_enchanting.enchantments.fire_protection = { requires_tool = false, treasure = false, power_range_table = {{10, 18}, {18, 26}, {26, 34}, {34, 42}}, + inv_combat_tab = true, + inv_tool_tab = false, } --- requires missing MineClone2 feature ---[[mcl_enchanting.enchantments.flame = { +mcl_enchanting.enchantments.flame = { name = S("Flame"), max_level = 1, primary = {bow = true}, @@ -221,7 +254,9 @@ mcl_enchanting.enchantments.fire_protection = { requires_tool = false, treasure = false, power_range_table = {{20, 50}}, -}]]-- + inv_combat_tab = true, + inv_tool_tab = false, +} -- implemented in mcl_item_entity mcl_enchanting.enchantments.fortune = { @@ -238,6 +273,8 @@ mcl_enchanting.enchantments.fortune = { requires_tool = false, treasure = false, power_range_table = {{15, 61}, {24, 71}, {33, 81}}, + inv_combat_tab = false, + inv_tool_tab = true, } -- implemented via walkover.register_global @@ -255,6 +292,8 @@ mcl_enchanting.enchantments.frost_walker = { requires_tool = false, treasure = true, power_range_table = {{10, 25}, {20, 35}}, + inv_combat_tab = true, + inv_tool_tab = false, } walkover.register_global(function(pos, _, player) @@ -289,6 +328,8 @@ end) requires_tool = false, treasure = false, power_range_table = {{1, 21}, {9, 29}, {17, 37}, {25, 45}, {33, 53}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- implemented in mcl_bows @@ -306,6 +347,8 @@ mcl_enchanting.enchantments.infinity = { requires_tool = false, treasure = false, power_range_table = {{20, 50}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- implemented via minetest.calculate_knockback @@ -323,6 +366,8 @@ mcl_enchanting.enchantments.knockback = { requires_tool = false, treasure = false, power_range_table = {{5, 61}, {25, 71}}, + inv_combat_tab = true, + inv_tool_tab = false, } local old_calculate_knockback = minetest.calculate_knockback @@ -341,8 +386,8 @@ function minetest.calculate_knockback(player, hitter, time_from_last_punch, tool return knockback end --- unimplemented ---[[mcl_enchanting.enchantments.looting = { +-- implemented in mcl_mobs and mobs_mc +mcl_enchanting.enchantments.looting = { name = S("Looting"), max_level = 3, primary = {sword = true}, @@ -356,7 +401,9 @@ end requires_tool = false, treasure = false, power_range_table = {{15, 61}, {24, 71}, {33, 81}}, -}]]-- + inv_combat_tab = true, + inv_tool_tab = false, +} -- requires missing MineClone2 feature --[[mcl_enchanting.enchantments.loyalty = { @@ -373,10 +420,12 @@ end requires_tool = false, treasure = false, power_range_table = {{12, 50}, {19, 50}, {26, 50}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- --- unimplemented ---[[mcl_enchanting.enchantments.luck_of_the_sea = { +-- implemented in mcl_fishing +mcl_enchanting.enchantments.luck_of_the_sea = { name = S("Luck of the Sea"), max_level = 3, primary = {fishing_rod = true}, @@ -390,7 +439,9 @@ end requires_tool = false, treasure = false, power_range_table = {{15, 61}, {24, 71}, {33, 81}}, -}]]-- + inv_combat_tab = false, + inv_tool_tab = true, +} -- implemented in mcl_fishing mcl_enchanting.enchantments.lure = { @@ -407,10 +458,12 @@ mcl_enchanting.enchantments.lure = { requires_tool = false, treasure = false, power_range_table = {{15, 61}, {24, 71}, {33, 81}}, + inv_combat_tab = false, + inv_tool_tab = true, } --- unimplemented ---[[mcl_enchanting.enchantments.mending = { +-- implemented in mcl_experience +mcl_enchanting.enchantments.mending = { name = S("Mending"), max_level = 1, primary = {}, @@ -424,7 +477,9 @@ mcl_enchanting.enchantments.lure = { requires_tool = true, treasure = true, power_range_table = {{25, 75}}, -}]]-- + inv_combat_tab = true, + inv_tool_tab = true, +} -- requires missing MineClone2 feature --[[mcl_enchanting.enchantments.multishot = { @@ -441,6 +496,8 @@ mcl_enchanting.enchantments.lure = { requires_tool = false, treasure = false, power_range_table = {{20, 50}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- requires missing MineClone2 feature @@ -458,6 +515,8 @@ mcl_enchanting.enchantments.lure = { requires_tool = false, treasure = false, power_range_table = {{1, 50}, {11, 50}, {21, 50}, {31, 50}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- implemented in mcl_bows @@ -475,6 +534,8 @@ mcl_enchanting.enchantments.power = { requires_tool = false, treasure = false, power_range_table = {{1, 16}, {11, 26}, {21, 36}, {31, 46}, {41, 56}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- implemented in mcl_armor @@ -492,6 +553,8 @@ mcl_enchanting.enchantments.projectile_protection = { requires_tool = false, treasure = false, power_range_table = {{1, 16}, {11, 26}, {21, 36}, {31, 46}, {41, 56}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- implemented in mcl_armor @@ -509,6 +572,8 @@ mcl_enchanting.enchantments.protection = { requires_tool = false, treasure = false, power_range_table = {{1, 12}, {12, 23}, {23, 34}, {34, 45}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- implemented via minetest.calculate_knockback (together with the Knockback enchantment) and mcl_bows @@ -526,6 +591,8 @@ mcl_enchanting.enchantments.punch = { requires_tool = false, treasure = false, power_range_table = {{12, 37}, {32, 57}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- requires missing MineClone2 feature @@ -543,6 +610,8 @@ mcl_enchanting.enchantments.punch = { requires_tool = false, treasure = false, power_range_table = {{12, 50}, {32, 50}, {52, 50}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- unimplemented @@ -560,6 +629,8 @@ mcl_enchanting.enchantments.punch = { requires_tool = false, treasure = false, power_range_table = {{10, 40}, {20, 50}, {30, 60}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- requires missing MineClone2 feature @@ -577,6 +648,8 @@ mcl_enchanting.enchantments.punch = { requires_tool = false, treasure = false, power_range_table = {{17, 50}, {24, 50}, {31, 50}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- implemented via on_enchant @@ -594,6 +667,8 @@ mcl_enchanting.enchantments.sharpness = { requires_tool = false, treasure = false, power_range_table = {{1, 21}, {12, 32}, {23, 43}, {34, 54}, {45, 65}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- implemented in mcl_item_entity @@ -611,6 +686,8 @@ mcl_enchanting.enchantments.silk_touch = { requires_tool = false, treasure = false, power_range_table = {{15, 61}}, + inv_combat_tab = false, + inv_tool_tab = true, } -- implemented via on_enchant and additions in mobs_mc @@ -628,6 +705,8 @@ mcl_enchanting.enchantments.smite = { requires_tool = false, treasure = false, power_range_table = {{5, 25}, {13, 33}, {21, 41}, {29, 49}, {37, 57}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- implemented in mcl_playerplus @@ -645,6 +724,8 @@ mcl_enchanting.enchantments.soul_speed = { requires_tool = false, treasure = true, power_range_table = {{10, 25}, {20, 35}, {30, 45}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- requires missing MineClone2 feature @@ -662,6 +743,8 @@ mcl_enchanting.enchantments.soul_speed = { requires_tool = false, treasure = false, power_range_table = {{5, 20}, {14, 29}, {23, 38}}, + inv_combat_tab = true, + inv_tool_tab = false, }]]-- -- implemented in mcl_armor @@ -671,7 +754,7 @@ mcl_enchanting.enchantments.thorns = { primary = {armor_head = true}, secondary = {armor_torso = true, armor_legs = true, armor_feet = true}, disallow = {non_combat_armor = true}, - incompatible = {blast_protection = true, fire_protection = true, projectile_protection = true}, + incompatible = {}, weight = 1, description = S("Reflects some of the damage taken when hit, at the cost of reducing durability with each proc."), curse = false, @@ -679,6 +762,8 @@ mcl_enchanting.enchantments.thorns = { requires_tool = false, treasure = false, power_range_table = {{10, 61}, {30, 71}, {50, 81}}, + inv_combat_tab = true, + inv_tool_tab = false, } -- for tools & weapons implemented via on_enchant; for bows implemented in mcl_bows; for armor implemented in mcl_armor and mcl_tt; for fishing rods implemented in mcl_fishing @@ -692,7 +777,7 @@ mcl_enchanting.enchantments.unbreaking = { weight = 5, description = S("Increases item durability."), curse = false, - on_enchant = function(itemstack, level) + on_enchant = function(itemstack, level) local tool_capabilities = itemstack:get_tool_capabilities() for group, capability in pairs(tool_capabilities.groupcaps) do capability.uses = capability.uses * (1 + level) @@ -703,4 +788,6 @@ mcl_enchanting.enchantments.unbreaking = { requires_tool = true, treasure = false, power_range_table = {{5, 61}, {13, 71}, {21, 81}}, + inv_combat_tab = true, + inv_tool_tab = true, } diff --git a/mods/ITEMS/mcl_enchanting/engine.lua b/mods/ITEMS/mcl_enchanting/engine.lua index ac12857b9..5bb5884ce 100644 --- a/mods/ITEMS/mcl_enchanting/engine.lua +++ b/mods/ITEMS/mcl_enchanting/engine.lua @@ -2,7 +2,7 @@ local S = minetest.get_translator("mcl_enchanting") local F = minetest.formspec_escape function mcl_enchanting.is_book(itemname) - return itemname == "mcl_books:book" or itemname == "mcl_enchanting:book_enchanted" + return itemname == "mcl_books:book" or itemname == "mcl_enchanting:book_enchanted" or itemname == "mcl_books:book_enchanted" end function mcl_enchanting.get_enchantments(itemstack) @@ -147,7 +147,7 @@ function mcl_enchanting.combine(itemstack, combine_with) local itemname = itemstack:get_name() local combine_name = combine_with:get_name() local enchanted_itemname = mcl_enchanting.get_enchanted_itemstring(itemname) - if enchanted_itemname ~= mcl_enchanting.get_enchanted_itemstring(combine_name) and not mcl_enchanting.is_book(itemname) then + if enchanted_itemname ~= mcl_enchanting.get_enchanted_itemstring(combine_name) and not mcl_enchanting.is_book(combine_name) then return false end local enchantments = mcl_enchanting.get_enchantments(itemstack) @@ -211,7 +211,7 @@ function mcl_enchanting.initialize() local register_tool_list = {} local register_item_list = {} for itemname, itemdef in pairs(minetest.registered_items) do - if mcl_enchanting.can_enchant_freshly(itemname) then + if mcl_enchanting.can_enchant_freshly(itemname) and not mcl_enchanting.is_book(itemname) then local new_name = itemname .. "_enchanted" minetest.override_item(itemname, {_mcl_enchanting_enchanted_tool = new_name}) local new_def = table.copy(itemdef) @@ -220,6 +220,7 @@ function mcl_enchanting.initialize() new_def.wield_image = new_def.wield_image .. mcl_enchanting.overlay end new_def.groups.not_in_creative_inventory = 1 + new_def.groups.not_in_craft_guide = 1 new_def.groups.enchanted = 1 new_def.texture = itemdef.texture or itemname:gsub("%:", "_") new_def._mcl_enchanting_enchanted_tool = new_name @@ -251,9 +252,9 @@ function mcl_enchanting.get_possible_enchantments(itemstack, enchantment_level, return possible_enchantments, weights, accum_weight end -function mcl_enchanting.generate_random_enchantments(itemstack, enchantment_level, treasure, no_reduced_bonus_chance) +function mcl_enchanting.generate_random_enchantments(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted) local itemname = itemstack:get_name() - if not mcl_enchanting.can_enchant_freshly(itemname) then + if not mcl_enchanting.can_enchant_freshly(itemname) and not ignore_already_enchanted then return end itemstack = ItemStack(itemstack) @@ -278,7 +279,7 @@ function mcl_enchanting.generate_random_enchantments(itemstack, enchantment_leve if weights[enchantment] >= r then selected_enchantment = enchantment break - end + end end local enchantment_def = mcl_enchanting.enchantments[selected_enchantment] local power_range_table = enchantment_def.power_range_table @@ -306,23 +307,45 @@ function mcl_enchanting.generate_random_enchantments(itemstack, enchantment_leve return enchantments, description end -function mcl_enchanting.enchant_randomly(itemstack, enchantment_level, treasure, no_reduced_bonus_chance) - local enchantments = mcl_enchanting.generate_random_enchantments(itemstack, enchantment_level, treasure, no_reduced_bonus_chance) - if enchantments then - mcl_enchanting.set_enchanted_itemstring(itemstack) - mcl_enchanting.set_enchantments(itemstack, enchantments) - end +function mcl_enchanting.generate_random_enchantments_reliable(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted) + local enchantments + repeat + enchantments = mcl_enchanting.generate_random_enchantments(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted) + until enchantments + return enchantments +end + +function mcl_enchanting.enchant_randomly(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted) + mcl_enchanting.set_enchanted_itemstring(itemstack) + mcl_enchanting.set_enchantments(itemstack, mcl_enchanting.generate_random_enchantments_reliable(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted)) return itemstack end function mcl_enchanting.get_randomly_enchanted_book(enchantment_level, treasure, no_reduced_bonus_chance) - return mcl_enchanting.enchant_randomly(enchantment_level, treasure, no_reduced_bonus_chance) + return mcl_enchanting.enchant_randomly(ItemStack("mcl_books:book"), enchantment_level, treasure, no_reduced_bonus_chance, true) +end + +function mcl_enchanting.get_uniform_randomly_enchanted_book(except) + except = except or except + local stack = ItemStack("mcl_enchanting:book_enchanted") + local list = {} + for enchantment in pairs(mcl_enchanting.enchantments) do + if table.indexof(except, enchantment) == -1 then + table.insert(list, enchantment) + end + end + local index = math.random(#list) + local enchantment = list[index] + local enchantment_def = mcl_enchanting.enchantments[enchantment] + local level = math.random(enchantment_def.max_level) + mcl_enchanting.enchant(stack, enchantment, level) + return stack end function mcl_enchanting.get_random_glyph_row() local glyphs = "" local x = 1.3 - for i = 1, 9 do + for i = 1, 9 do glyphs = glyphs .. "image[".. x .. ",0.1;0.5,0.5;mcl_enchanting_glyph_" .. math.random(18) .. ".png^[colorize:#675D49:255]" x = x + 0.6 end @@ -393,15 +416,19 @@ function mcl_enchanting.show_enchanting_formspec(player) .. "formspec_version[3]" .. "label[0,0;" .. C("#313131") .. F(table_name) .. "]" .. mcl_formspec.get_itemslot_bg(0.2, 2.4, 1, 1) - .. "list[detached:" .. name .. "_enchanting;enchanting;0.2,2.4;1,1;1]" + .. "list[current_player;enchanting_item;0.2,2.4;1,1]" .. mcl_formspec.get_itemslot_bg(1.1, 2.4, 1, 1) .. "image[1.1,2.4;1,1;mcl_enchanting_lapis_background.png]" - .. "list[detached:" .. name .. "_enchanting;enchanting;1.1,2.4;1,1;2]" + .. "list[current_player;enchanting_lapis;1.1,2.4;1,1]" .. "label[0,4;" .. C("#313131") .. F(S("Inventory")).."]" .. mcl_formspec.get_itemslot_bg(0, 4.5, 9, 3) .. mcl_formspec.get_itemslot_bg(0, 7.74, 9, 1) .. "list[current_player;main;0,4.5;9,3;9]" - .. "listring[detached:" .. name .. "_enchanting;enchanting]" + .. "listring[current_player;enchanting_item]" + .. "listring[current_player;main]" + .. "listring[current_player;enchanting]" + .. "listring[current_player;main]" + .. "listring[current_player;enchanting_lapis]" .. "listring[current_player;main]" .. "list[current_player;main;0,7.74;9,1;]" .. "real_coordinates[true]" @@ -420,7 +447,7 @@ function mcl_enchanting.show_enchanting_formspec(player) local hover_ending = (can_enchant and "_hovered" or "_off") formspec = formspec .. "container[3.2," .. y .. "]" - .. (slot and "tooltip[button_" .. i .. ";" .. C("#818181") .. F(slot.description) .. " " .. C("#FFFFFF") .. " . . . ?\n\n" .. (enough_levels and C(enough_lapis and "#818181" or "#FC5454") .. F(S("@1 × Lapis Lazuli", i)) .. "\n" .. C("#818181") .. F(S("Enchantment levels: @1", i)) or C("#FC5454") .. F(S("Level requirement: @1", slot.level_requirement))) .. "]" or "") + .. (slot and "tooltip[button_" .. i .. ";" .. C("#818181") .. F(slot.description) .. " " .. C("#FFFFFF") .. " . . . ?\n\n" .. (enough_levels and C(enough_lapis and "#818181" or "#FC5454") .. F(S("@1 Lapis Lazuli", i)) .. "\n" .. C("#818181") .. F(S("@1 Enchantment Levels", i)) or C("#FC5454") .. F(S("Level requirement: @1", slot.level_requirement))) .. "]" or "") .. "style[button_" .. i .. ";bgimg=mcl_enchanting_button" .. ending .. ".png;bgimg_hovered=mcl_enchanting_button" .. hover_ending .. ".png;bgimg_pressed=mcl_enchanting_button" .. hover_ending .. ".png]" .. "button[0,0;7.5,1.3;button_" .. i .. ";]" .. (slot and "image[0,0;1.3,1.3;mcl_enchanting_number_" .. i .. ending .. ".png]" or "") @@ -468,80 +495,67 @@ function mcl_enchanting.handle_formspec_fields(player, formname, fields) inv:set_stack("enchanting_item", 1, itemstack) minetest.sound_play("mcl_enchanting_enchant", {to_player = name, gain = 5.0}) mcl_enchanting.reset_table_slots(player) - mcl_enchanting.reload_inventory(player) mcl_enchanting.show_enchanting_formspec(player) end end function mcl_enchanting.initialize_player(player) - local player_inv = player:get_inventory() - player_inv:set_size("enchanting_lapis", 1) - player_inv:set_size("enchanting_item", 1) - local name = player:get_player_name() - local detached_inv = minetest.create_detached_inventory(name .. "_enchanting", { - allow_put = function(inv, listname, index, stack, player) - if player:get_player_name() ~= name then - return 0 - end - if stack:get_name() == "mcl_dye:blue" and index ~= 2 then - return math.min(inv:get_stack(listname, 3):get_free_space(), stack:get_count()) - elseif index ~= 3 and inv:get_stack(listname, 2):get_count() == 0 then + local inv = player:get_inventory() + inv:set_size("enchanting", 1) + inv:set_size("enchanting_item", 1) + inv:set_size("enchanting_lapis", 1) +end + +function mcl_enchanting.is_enchanting_inventory_action(action, inventory, inventory_info) + if inventory:get_location().type == "player" then + local enchanting_lists = mcl_enchanting.enchanting_lists + if action == "move" then + local is_from = table.indexof(enchanting_lists, inventory_info.from_list) ~= -1 + local is_to = table.indexof(enchanting_lists, inventory_info.to_list) ~= -1 + return is_from or is_to, is_to + elseif (action == "put" or action == "take") and table.indexof(enchanting_lists, inventory_info.listname) ~= -1 then + return true + end + else + return false + end +end + +function mcl_enchanting.allow_inventory_action(player, action, inventory, inventory_info) + local is_enchanting_action, do_limit = mcl_enchanting.is_enchanting_inventory_action(action, inventory, inventory_info) + if is_enchanting_action and do_limit then + if action == "move" then + local listname = inventory_info.to_list + local stack = inventory:get_stack(inventory_info.from_list, inventory_info.from_index) + if stack:get_name() == "mcl_dye:blue" and listname ~= "enchanting_item" then + return math.min(inventory:get_stack("enchanting_lapis", 1):get_free_space(), stack:get_count()) + elseif inventory:get_stack("enchanting_item", 1):get_count() == 0 and listname ~= "enchanting_lapis" then return 1 else return 0 end - end, - allow_take = function(inv, listname, index, stack, player) - if player:get_player_name() ~= name or index == 1 then - return 0 - end - return stack:get_count() - end, - allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + else return 0 - end, - on_put = function(inv, listname, index, stack, player) - local result_list - if index == 1 then - if stack:get_name() == "mcl_dye:blue" then - result_list = "lapis" - stack:add_item(inv:get_stack(listname, 3)) - inv:set_stack(listname, 1, nil) - inv:set_stack(listname, 3, stack) - else - result_list = "item" - inv:set_stack(listname, 1, nil) - inv:set_stack(listname, 2, stack) - end - elseif index == 2 then - result_list = "item" - elseif index == 3 then - result_list = "lapis" - stack = inv:get_stack(listname, 3) - end - player:get_inventory():set_stack("enchanting_" .. result_list, 1, stack) - mcl_enchanting.show_enchanting_formspec(player) - end, - on_take = function(inv, listname, index, stack, player) - local result_list - if index == 2 then - result_list = "item" - elseif index == 3 then - result_list = "lapis" - end - player:get_inventory():set_stack("enchanting_" .. result_list, 1, nil) - mcl_enchanting.show_enchanting_formspec(player) end - }, name) - detached_inv:set_size("enchanting", 3) - mcl_enchanting.reload_inventory(player) + end end -function mcl_enchanting.reload_inventory(player) - local player_inv = player:get_inventory() - local detached_inv = minetest.get_inventory({type = "detached", name = player:get_player_name() .. "_enchanting"}) - detached_inv:set_stack("enchanting", 2, player_inv:get_stack("enchanting_item", 1)) - detached_inv:set_stack("enchanting", 3, player_inv:get_stack("enchanting_lapis", 1)) +function mcl_enchanting.on_inventory_action(player, action, inventory, inventory_info) + if mcl_enchanting.is_enchanting_inventory_action(action, inventory, inventory_info) then + if action == "move" and inventory_info.to_list == "enchanting" then + local stack = inventory:get_stack("enchanting", 1) + local result_list + if stack:get_name() == "mcl_dye:blue" then + result_list = "enchanting_lapis" + stack:add_item(inventory:get_stack("enchanting_lapis", 1)) + else + result_list = "enchanting_item" + end + inventory:set_stack(result_list, 1, stack) + inventory:set_stack("enchanting", 1, nil) + end + mcl_enchanting.show_enchanting_formspec(player) + end end function mcl_enchanting.schedule_book_animation(self, anim) @@ -551,7 +565,7 @@ end function mcl_enchanting.set_book_animation(self, anim) local anim_index = mcl_enchanting.book_animations[anim] local start, stop = mcl_enchanting.book_animation_steps[anim_index], mcl_enchanting.book_animation_steps[anim_index + 1] - self.object:set_animation({x = start, y = stop}, mcl_enchanting.book_animation_speed) + self.object:set_animation({x = start, y = stop}, mcl_enchanting.book_animation_speed, 0, mcl_enchanting.book_animation_loop[anim] or false) self.scheduled_anim = nil self.anim_length = (stop - start) / 40 end diff --git a/mods/ITEMS/mcl_enchanting/init.lua b/mods/ITEMS/mcl_enchanting/init.lua index 315cadc89..ab3dddbd3 100644 --- a/mods/ITEMS/mcl_enchanting/init.lua +++ b/mods/ITEMS/mcl_enchanting/init.lua @@ -5,10 +5,13 @@ mcl_enchanting = { book_offset = vector.new(0, 0.75, 0), book_animations = {["close"] = 1, ["opening"] = 2, ["open"] = 3, ["closing"] = 4}, book_animation_steps = {0, 640, 680, 700, 740}, + book_animation_loop = {["open"] = true, ["close"] = true}, book_animation_speed = 40, roman_numerals = dofile(modpath .. "/roman_numerals.lua"), -- https://exercism.io/tracks/lua/exercises/roman-numerals/solutions/73c2fb7521e347209312d115f872fa49 enchantments = {}, - overlay = "^[colorize:white:50^[colorize:purple:50", + overlay = "^[colorize:purple:50", + --overlay = "^[invert:rgb^[multiply:#4df44d:50^[invert:rgb", + enchanting_lists = {"enchanting", "enchanting_item", "enchanting_lapis"}, bookshelf_positions = { {x = -2, y = 0, z = -2}, {x = -2, y = 1, z = -2}, {x = -1, y = 0, z = -2}, {x = -1, y = 1, z = -2}, @@ -24,8 +27,8 @@ mcl_enchanting = { {x = -2, y = 0, z = -1}, {x = -2, y = 1, z = -1}, {x = -2, y = 0, z = 0}, {x = -2, y = 1, z = 0}, {x = -2, y = 0, z = 1}, {x = -2, y = 1, z = 1}, - {x = -2, y = 0, z = 2}, {x = -2, y = 1, z = 2}, - {x = 2, y = 0, z = -2}, {x = 2, y = 1, z = -2}, + -- {x = -2, y = 0, z = 2}, {x = -2, y = 1, z = 2}, + -- {x = 2, y = 0, z = -2}, {x = 2, y = 1, z = -2}, {x = 2, y = 0, z = -1}, {x = 2, y = 1, z = -1}, {x = 2, y = 0, z = 0}, {x = 2, y = 1, z = 0}, {x = 2, y = 0, z = 1}, {x = 2, y = 1, z = 1}, @@ -46,8 +49,8 @@ mcl_enchanting = { {x = -1, y = 0, z = -1}, {x = -1, y = 1, z = -1}, {x = -1, y = 0, z = 0}, {x = -1, y = 1, z = 0}, {x = -1, y = 0, z = 1}, {x = -1, y = 1, z = 1}, - {x = -1, y = 0, z = 1}, {x = -1, y = 1, z = 1}, - {x = 1, y = 0, z = -1}, {x = 1, y = 1, z = -1}, + -- {x = -1, y = 0, z = 1}, {x = -1, y = 1, z = 1}, + -- {x = 1, y = 0, z = -1}, {x = 1, y = 1, z = -1}, {x = 1, y = 0, z = -1}, {x = 1, y = 1, z = -1}, {x = 1, y = 0, z = 0}, {x = 1, y = 1, z = 0}, {x = 1, y = 0, z = 1}, {x = 1, y = 1, z = 1}, @@ -142,6 +145,8 @@ minetest.register_craftitem("mcl_enchanting:book_enchanted", { stack_max = 1, }) +minetest.register_alias("mcl_books:book_enchanted", "mcl_enchanting:book_enchanted") + local spawn_book_entity = function(pos, respawn) if respawn then -- Check if we already have a book @@ -214,6 +219,16 @@ end minetest.register_node("mcl_enchanting:table", { description = S("Enchanting Table"), + _tt_help = S("Spend experience, and lapis to enchant various items."), + _doc_items_longdesc = S("Enchanting Tables will let you enchant armors, tools, weapons, and books with various abilities. But, at the cost of some experience, and lapis lazuli."), + _doc_items_usagehelp = + S("Rightclick the Enchanting Table to open the enchanting menu.").."\n".. + S("Place a tool, armor, weapon or book into the top left slot, and then place 1-3 Lapis Lazuli in the slot to the right.").."\n".."\n".. + S("After placing your items in the slots, the enchanting options will be shown. Hover over the options to read what is available to you.").."\n".. + S("These options are randomized, and dependent on experience level; but the enchantment strength can be increased.").."\n".."\n".. + S("To increase the enchantment strength, place bookshelves around the enchanting table. However, you will need to keep 1 air node between the table, & the bookshelves to empower the enchanting table.").."\n".."\n".. + S("After finally selecting your enchantment; left-click on the selection, and you will see both the lapis lazuli and your experience levels consumed. And, an enchanted item left in its place."), + _doc_items_hidden = false, drawtype = "nodebox", tiles = {"mcl_enchanting_table_top.png", "mcl_enchanting_table_bottom.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png"}, node_box = { @@ -231,7 +246,8 @@ minetest.register_node("mcl_enchanting:table", { if table_name == "" then table_name = S("Enchant") end - player_meta:set_int("mcl_enchanting:num_bookshelves", num_bookshelves) + local bookshelves = mcl_enchanting.get_bookshelves(pos) + player_meta:set_int("mcl_enchanting:num_bookshelves", math.min(15, #bookshelves)) player_meta:set_string("mcl_enchanting:table_name", table_name) mcl_enchanting.show_enchanting_formspec(clicker) -- Respawn book entity just in case it got lost @@ -273,7 +289,7 @@ minetest.register_node("mcl_enchanting:table", { drop = "", _mcl_blast_resistance = 1200, _mcl_hardness = 5, -}) +}) minetest.register_craft({ output = "mcl_enchanting:table", @@ -290,23 +306,42 @@ minetest.register_abm({ chance = 1, nodenames = "mcl_enchanting:table", action = function(pos) - local absolute, relative = mcl_enchanting.get_bookshelves(pos) - for i, ap in ipairs(absolute) do - if math.random(10) == 1 then - local rp = relative[i] - minetest.add_particle({ - pos = ap, - velocity = vector.subtract(vector.new(0, 5, 0), rp), - acceleration = {x = 0, y = -9.81, z = 0}, - expirationtime = 2, - size = 2, - texture = "mcl_enchanting_glyph_" .. math.random(18) .. ".png" - }) + local playernames = {} + for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 15)) do + if obj:is_player() then + table.insert(playernames, obj:get_player_name()) + end + end + if #playernames < 1 then + return + end + local absolute, relative = mcl_enchanting.get_bookshelves(pos) + for i, ap in ipairs(absolute) do + if math.random(5) == 1 then + local rp = relative[i] + local t = math.random()+1 --time + local d = {x = rp.x, y=rp.y-0.7, z=rp.z} --distance + local v = {x = -math.random()*d.x, y = math.random(), z = -math.random()*d.z} --velocity + local a = {x = 2*(-v.x*t - d.x)/t/t, y = 2*(-v.y*t - d.y)/t/t, z = 2*(-v.z*t - d.z)/t/t} --acceleration + local s = math.random()+0.9 --size + t = t - 0.1 --slightly decrease time to avoid texture overlappings + local tx = "mcl_enchanting_glyph_" .. math.random(18) .. ".png" + for _, name in pairs(playernames) do + minetest.add_particle({ + pos = ap, + velocity = v, + acceleration = a, + expirationtime = t, + size = s, + texture = tx, + collisiondetection = false, + playername = name + }) + end end end - minetest.get_meta(pos):set_int("mcl_enchanting:num_bookshelves", math.min(15, #absolute)) end -}) +}) minetest.register_lbm({ label = "(Re-)spawn book entity above enchanting table", @@ -322,4 +357,6 @@ minetest.register_lbm({ minetest.register_on_mods_loaded(mcl_enchanting.initialize) minetest.register_on_joinplayer(mcl_enchanting.initialize_player) minetest.register_on_player_receive_fields(mcl_enchanting.handle_formspec_fields) -table.insert(tt.registered_snippets, 1, mcl_enchanting.enchantments_snippet) +minetest.register_allow_player_inventory_action(mcl_enchanting.allow_inventory_action) +minetest.register_on_player_inventory_action(mcl_enchanting.on_inventory_action) +table.insert(tt.registered_snippets, 1, mcl_enchanting.enchantments_snippet) diff --git a/mods/ITEMS/mcl_enchanting/locale/mcl_enchanting.de.tr b/mods/ITEMS/mcl_enchanting/locale/mcl_enchanting.de.tr new file mode 100644 index 000000000..68077578c --- /dev/null +++ b/mods/ITEMS/mcl_enchanting/locale/mcl_enchanting.de.tr @@ -0,0 +1,100 @@ +# textdomain: mcl_enchanting +Aqua Affinity=Aquaaffinität +Increases underwater mining speed.=Erhöht Unterwassergrabegeschwindigkeit. +Bane of Arthropods=Schrecken der Gliederfüßler +Increases damage and applies Slowness IV to arthropod mobs (spiders, cave spiders, silverfish and endermites).=Erhöht Schaden und gibt Langsamkeit IV an Gliederfüßlern (Spinnen, Höhlenspinenn, Silberfischchen und Endermilben). +Blast Protection=Explosionsschutz +Reduces explosion damage and knockback.=Reduziert Explosionsschaden und -rückschlag. +Channeling=Kanalisierung +Channels a bolt of lightning toward a target. Works only during thunderstorms and if target is unobstructed with opaque blocks.=Kanalisiert einen Blitz zu einem Ziel. Funktioniert nur während Gewitterstürmen und solange kein undurchsichtiger Block im Weg ist. +Curse of Binding=Fluch der Bindung +Item cannot be removed from armor slots except due to death, breaking or in Creative Mode.=Gegenstand kann nicht von den Rüstungsplätzen entfernt werden, außer beim Tod, Zerbrechen oder im Kreativmodus. +Curse of Vanishing=Fluch des Verschwindens +Item destroyed on death.=Gegenstand wird bei Tod zerstört. +Depth Strider=Tiefenstreicher +Increases underwater movement speed.=Erhöht Bewegungsgeschwindigkeit im Wasser. +Efficiency=Effizienz +Increases mining speed.=Erhöht Grabegeschwindigkeit. +Feather Falling=Federfall +Reduces fall damage.=Reduziert Fallschaden. +Fire Aspect=Feieraspekt +Sets target on fire.=Zündes das Ziel an. +Fire Protection=Feuerschutz +Reduces fire damage.=Reduziert Feuerschaden +Flame=Flamme +Arrows set target on fire.=Pfeile zünden Ziel an. +Fortune=Glück +Increases certain block drops.=Erhöht bestimmte Blockabwürfe. +Frost Walker=Frostläufer +Turns water beneath the player into frosted ice and prevents the damage from magma blocks.=Verwandelt Wasser unter dem Spieler zu brüchigem Eis und verhindert Schaden von Magmablöcken. +Impaling=Aufspießen +Trident deals additional damage to ocean mobs.=Dreizack richtet Zusatzschaden an Ozeanmobs an. +Infinity=Unendlichkeit +Shooting consumes no regular arrows.=Schüsse verbrauchen keine regulären Pfeile. +Knockback=Rückschlag. +Increases knockback.=Verstärkt Rückschlag. +Looting=Plünderer +Increases mob loot.=Erhöht Abwürfe von Mobs. +Loyalty=Loyalität +Trident returns after being thrown. Higher levels reduce return time.=Dreizack kehrt nach Wurf zurück. Höhere Stufen reduzieren die Rückkehrzeit. +Luck of the Sea=Glück des Meeres +Increases rate of good loot (enchanting books, etc.)=Erhöht die Rate von guten Abwürfen (verzauberte Bücher, usw.) +Lure=Köder +Decreases time until rod catches something.=Reduziert die Zeit, bis die Angel etwas fängt. +Mending=Ausbessern +Repair the item while gaining XP orbs.=Gegenstand reparieren, während man Erfahrungskugeln erhält. +Multishot=Mehrschuss +Shoot 3 arrows at the cost of one.=3 Pfeile zum Preis von 1 schießen. +Piercing=Durchbohren +Arrows passes through multiple objects.=Pfeile durchdringen mehrere Objekte. +Power=Stärke +Increases arrow damage.=Erhöht Pfeilschaden. +Projectile Protection=Projektilprojektion +Reduces projectile damage.=Reduziert Projektilschaden. +Protection=Schutz +Reduces most types of damage by 4% for each level.=Reduziert die meisten Schadensarten um 4% je Stufe. +Punch=Schlag +Increases arrow knockback.=Erhöht Pfeilrückschlag. +Quick Charge=Schnellladen +Decreases crossbow charging time.=Reduziert Armbrustladezeit. +Respiration=Atmung +Extends underwater breathing time.=Erhöht Unterwasseratemzeit. +Riptide=Strömung +Trident launches player with itself when thrown. Works only in water or rain.=Dreizack wirft den Spieler mit. Funktioniert nur im Wasser oder Regen. +Sharpness=Schärfe +Increases damage.=Erhöht Schaden. +Silk Touch=Sorgfalt +Mined blocks drop themselves.=Abgebaute Blöcke werfen sich selbst ab. +Smite=Qual +Increases damage to undead mobs.=Erhöht Schaden für untote Mobs. +Soul Speed=Schnelle Seele +Increases walking speed on soul sand.=Erhöht Gehgeschwindigkeit auf Seelensand. +Sweeping Edge=Schwungklinge +Increases sweeping attack damage.=Erhöht Schwungangriffsschaden. +Thorns=Dornen +Reflects some of the damage taken when hit, at the cost of reducing durability with each proc.=Reflektiert etwas des Schadens beim Erleiden eines Treffers, auf Kosten der Haltbarkeit. +Unbreaking=Haltbarkeit +Increases item durability.=Erhöht Haldbarkeit des Gegenstands. +Inventory=Inventar +@1 Lapis Lazuli=@1 Lapislazuli +@1 Enchantment Levels=@1 Verzauberungsstufen +Level requirement: @1=Level benötigt: @1 +Enchant an item=Gegenstand verzaubern + []= [] +Usage: /enchant []=Verwendung: /enchant [] +Player '@1' cannot be found.=Spieler „@1“ kann nicht gefunden werden. +There is no such enchantment '@1'.=Es gibt keine Verzauberung namens „@1“. +The target doesn't hold an item.=Das Ziel hält keinen Gegenstand. +The selected enchantment can't be added to the target item.=Die gewählte Verzauberug kann nicht dem Ziel gegeben werden. +'@1' is not a valid number='@1' ist keine gültige Zahl +The number you have entered (@1) is too big, it must be at most @2.=Die eingegebene Zahl (@1) ist zu groß, maximal @1 erlaubt. +The number you have entered (@1) is too small, it must be at least @2.=Die eingegebene Zahl (@1) ist zu klein, minimal @1 erlaubt. +@1 can't be combined with @2.=@1 kann nicht mit @2 kombiniert werden. +Enchanting succeded.=Verzauberug erfolgreich. +Forcefully enchant an item=Einen Gegenstand zwangsweise verzaubern +Usage: /forceenchant []=Verwendung: /forceenchant [] +The target item is not enchantable.=Der Zielgegenstand ist nicht verzauberbar. +'@1' is not a valid number.='@1' ist keine gültige Zahl. +Enchanted Book=Verzaubertes Buch +Enchanting Table=Zaubertisch +Enchant=Verzaubern diff --git a/mods/ITEMS/mcl_enchanting/locale/template.txt b/mods/ITEMS/mcl_enchanting/locale/template.txt new file mode 100644 index 000000000..f186ef37b --- /dev/null +++ b/mods/ITEMS/mcl_enchanting/locale/template.txt @@ -0,0 +1,100 @@ +# textdomain: mcl_enchanting +Aqua Affinity= +Increases underwater mining speed.= +Bane of Arthropods= +Increases damage and applies Slowness IV to arthropod mobs (spiders, cave spiders, silverfish and endermites).= +Blast Protection= +Reduces explosion damage and knockback.= +Channeling= +Channels a bolt of lightning toward a target. Works only during thunderstorms and if target is unobstructed with opaque blocks.= +Curse of Binding= +Item cannot be removed from armor slots except due to death, breaking or in Creative Mode.= +Curse of Vanishing= +Item destroyed on death.= +Depth Strider= +Increases underwater movement speed.= +Efficiency= +Increases mining speed.= +Feather Falling= +Reduces fall damage.= +Fire Aspect= +Sets target on fire.= +Fire Protection= +Reduces fire damage.= +Flame= +Arrows set target on fire.= +Fortune= +Increases certain block drops.= +Frost Walker= +Turns water beneath the player into frosted ice and prevents the damage from magma blocks.= +Impaling= +Trident deals additional damage to ocean mobs.= +Infinity= +Shooting consumes no regular arrows.= +Knockback= +Increases knockback.= +Looting= +Increases mob loot.= +Loyalty= +Trident returns after being thrown. Higher levels reduce return time.= +Luck of the Sea= +Increases rate of good loot (enchanting books, etc.)= +Lure= +Decreases time until rod catches something.= +Mending= +Repair the item while gaining XP orbs.= +Multishot= +Shoot 3 arrows at the cost of one.= +Piercing= +Arrows passes through multiple objects.= +Power= +Increases arrow damage.= +Projectile Protection= +Reduces projectile damage.= +Protection= +Reduces most types of damage by 4% for each level.= +Punch= +Increases arrow knockback.= +Quick Charge= +Decreases crossbow charging time.= +Respiration= +Extends underwater breathing time.= +Riptide= +Trident launches player with itself when thrown. Works only in water or rain.= +Sharpness= +Increases damage.= +Silk Touch= +Mined blocks drop themselves.= +Smite= +Increases damage to undead mobs.= +Soul Speed= +Increases walking speed on soul sand.= +Sweeping Edge= +Increases sweeping attack damage.= +Thorns= +Reflects some of the damage taken when hit, at the cost of reducing durability with each proc.= +Unbreaking= +Increases item durability.= +Inventory= +@1 × Lapis Lazuli= +Enchantment levels: @1= +Level requirement: @1= +Enchant an item= + []= +Usage: /enchant []= +Player '@1' cannot be found.= +There is no such enchantment '@1'.= +The target doesn't hold an item.= +The selected enchantment can't be added to the target item.= +'@1' is not a valid number= +The number you have entered (@1) is too big, it must be at most @2.= +The number you have entered (@1) is too small, it must be at least @2.= +@1 can't be combined with @2.= +Enchanting succeded.= +Forcefully enchant an item= +Usage: /forceenchant []= +The target item is not enchantable.= +'@1' is not a valid number.= +Enchanted Book= +Enchanting Table= +Enchant= diff --git a/mods/ITEMS/mcl_enchanting/sounds/attributions.txt b/mods/ITEMS/mcl_enchanting/sounds/attributions.txt new file mode 100644 index 000000000..261a4ad99 --- /dev/null +++ b/mods/ITEMS/mcl_enchanting/sounds/attributions.txt @@ -0,0 +1,7 @@ +Alecia Shepherd (CC BY-SA 4.0): + + mcl_enchanting_enchant.0.ogg + mcl_enchanting_enchant.1.ogg + mcl_enchanting_enchant.2.ogg + +Source: SnowSong sound and music pack diff --git a/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.0.ogg b/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.0.ogg new file mode 100644 index 000000000..a7f357f03 Binary files /dev/null and b/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.0.ogg differ diff --git a/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.1.ogg b/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.1.ogg new file mode 100644 index 000000000..246fde346 Binary files /dev/null and b/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.1.ogg differ diff --git a/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.2.ogg b/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.2.ogg new file mode 100644 index 000000000..4ad66d9a2 Binary files /dev/null and b/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.2.ogg differ diff --git a/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.ogg b/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.ogg deleted file mode 100644 index 882e9ee23..000000000 Binary files a/mods/ITEMS/mcl_enchanting/sounds/mcl_enchanting_enchant.ogg and /dev/null differ diff --git a/mods/ITEMS/mcl_farming/pumpkin.lua b/mods/ITEMS/mcl_farming/pumpkin.lua index 990da549d..72b4e5412 100644 --- a/mods/ITEMS/mcl_farming/pumpkin.lua +++ b/mods/ITEMS/mcl_farming/pumpkin.lua @@ -112,7 +112,6 @@ pumpkin_face_base_def._doc_items_longdesc = S("A pumpkin can be worn as a helmet pumpkin_face_base_def._doc_items_usagehelp = nil pumpkin_face_base_def.tiles = {"farming_pumpkin_top.png", "farming_pumpkin_top.png", "farming_pumpkin_side.png", "farming_pumpkin_side.png", "farming_pumpkin_side.png", "farming_pumpkin_face.png"} pumpkin_face_base_def.groups.armor_head=1 -pumpkin_face_base_def.groups.enchantability=1 pumpkin_face_base_def._mcl_armor_mob_range_factor = 0 pumpkin_face_base_def._mcl_armor_mob_range_mob = "mobs_mc:enderman" pumpkin_face_base_def.groups.non_combat_armor=1 diff --git a/mods/ITEMS/mcl_farming/textures/mcl_farming_pumpkin_face_preview.png b/mods/ITEMS/mcl_farming/textures/mcl_farming_pumpkin_face_preview.png index f3d91c44d..a151fcab6 100644 Binary files a/mods/ITEMS/mcl_farming/textures/mcl_farming_pumpkin_face_preview.png and b/mods/ITEMS/mcl_farming/textures/mcl_farming_pumpkin_face_preview.png differ diff --git a/mods/ITEMS/mcl_fire/flint_and_steel.lua b/mods/ITEMS/mcl_fire/flint_and_steel.lua index 7b585f348..54c2f1fac 100644 --- a/mods/ITEMS/mcl_fire/flint_and_steel.lua +++ b/mods/ITEMS/mcl_fire/flint_and_steel.lua @@ -9,7 +9,7 @@ minetest.register_tool("mcl_fire:flint_and_steel", { inventory_image = "mcl_fire_flint_and_steel.png", liquids_pointable = false, stack_max = 1, - groups = { tool = 1, enchantability = 1 }, + groups = { tool = 1, }, on_place = function(itemstack, user, pointed_thing) -- Use pointed node's on_rightclick function first, if present local node = minetest.get_node(pointed_thing.under) @@ -70,6 +70,7 @@ minetest.register_tool("mcl_fire:flint_and_steel", { return stack end, sound = { breaks = "default_tool_breaks" }, + _mcl_uses = 65, }) minetest.register_craft({ diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 2daaf1e5d..50303e3b2 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -117,7 +117,7 @@ minetest.register_node("mcl_fire:fire", { sunlight_propagates = true, damage_per_second = 1, _mcl_node_death_message = fire_death_messages, - groups = {fire = 1, dig_immediate = 3, not_in_creative_inventory = 1, dig_by_piston=1, destroys_items=1 }, + groups = {fire = 1, dig_immediate = 3, not_in_creative_inventory = 1, dig_by_piston=1, destroys_items=1, set_on_fire=8}, floodable = true, on_flood = function(pos, oldnode, newnode) if minetest.get_item_group(newnode.name, "water") ~= 0 then @@ -248,7 +248,7 @@ minetest.register_node("mcl_fire:eternal_fire", { sunlight_propagates = true, damage_per_second = 1, _mcl_node_death_message = fire_death_messages, - groups = {fire = 1, dig_immediate = 3, not_in_creative_inventory = 1, dig_by_piston = 1, destroys_items = 1}, + groups = {fire = 1, dig_immediate = 3, not_in_creative_inventory = 1, dig_by_piston = 1, destroys_items = 1, set_on_fire=8}, floodable = true, on_flood = function(pos, oldnode, newnode) if minetest.get_item_group(newnode.name, "water") ~= 0 then diff --git a/mods/ITEMS/mcl_fire/textures/mcl_burning_entity_flame_animated.png b/mods/ITEMS/mcl_fire/textures/mcl_burning_entity_flame_animated.png new file mode 100644 index 000000000..434bdffdd Binary files /dev/null and b/mods/ITEMS/mcl_fire/textures/mcl_burning_entity_flame_animated.png differ diff --git a/mods/ITEMS/mcl_fire/textures/mcl_burning_hud_flame_animated.png b/mods/ITEMS/mcl_fire/textures/mcl_burning_hud_flame_animated.png new file mode 100644 index 000000000..434bdffdd Binary files /dev/null and b/mods/ITEMS/mcl_fire/textures/mcl_burning_hud_flame_animated.png differ diff --git a/mods/ITEMS/mcl_fishing/init.lua b/mods/ITEMS/mcl_fishing/init.lua index 3b7ba37c0..df76efcbd 100644 --- a/mods/ITEMS/mcl_fishing/init.lua +++ b/mods/ITEMS/mcl_fishing/init.lua @@ -25,21 +25,31 @@ local bobber_ENTITY={ objtype="fishing", } -local fish = function(itemstack, player) +local fish = function(itemstack, player, pointed_thing) + if pointed_thing and pointed_thing.type == "node" then + -- Call on_rightclick if the pointed node defines it + local node = minetest.get_node(pointed_thing.under) + if player and not player:get_player_control().sneak then + if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then + return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, player, itemstack) or itemstack + end + end + end + local pos = player:get_pos() local objs = minetest.get_objects_inside_radius(pos, 125) local num = 0 local ent = nil local noent = true - - + + local durability = 65 local unbreaking = mcl_enchanting.get_enchantment(itemstack, "unbreaking") if unbreaking > 0 then durability = durability * (unbreaking + 1) end - + --Check for bobber if so handle. for n = 1, #objs do ent = objs[n]:get_luaentity() @@ -52,10 +62,15 @@ local fish = function(itemstack, player) local items local itemcount = 1 local itemwear = 0 - -- FIXME: Maybe use a better seeding local pr = PseudoRandom(os.time() * math.random(1, 100)) local r = pr:next(1, 100) - if r <= 85 then + local fish_values = {85, 84.8, 84.7, 84.5} + local junk_values = {10, 8.1, 6.1, 4.2} + local luck_of_the_sea = math.min(mcl_enchanting.get_enchantment(itemstack, "luck_of_the_sea"), 3) + local index = luck_of_the_sea + 1 + local fish_value = fish_values[index] + local junk_value = junk_values[index] + fish_value + if r <= fish_value then -- Fish items = mcl_loot.get_loot({ items = { @@ -65,7 +80,7 @@ local fish = function(itemstack, player) { itemstring = "mcl_fishing:pufferfish_raw", weight = 13 }, } }, pr) - elseif r <= 95 then + elseif r <= junk_value then -- Junk items = mcl_loot.get_loot({ items = { @@ -88,8 +103,7 @@ local fish = function(itemstack, player) items = { -- TODO: Enchanted Bow { itemstring = "mcl_bows:bow", wear_min = 49144, wear_max = 65535 }, -- 75%-100% damage - -- TODO: Enchanted Book - { itemstring = "mcl_books:book" }, + { itemstack = mcl_enchanting.get_randomly_enchanted_book(30, true, true)}, -- TODO: Enchanted Fishing Rod { itemstring = "mcl_fishing:fishing_rod", wear_min = 49144, wear_max = 65535 }, -- 75%-100% damage { itemstring = "mcl_mobs:nametag", }, @@ -107,6 +121,8 @@ local fish = function(itemstack, player) local inv = player:get_inventory() if inv:room_for_item("main", item) then inv:add_item("main", item) + else + minetest.add_item(pos, item) end if mcl_experience.throw_experience then mcl_experience.throw_experience(pos, math.random(1,6)) @@ -248,7 +264,8 @@ local bobber_on_step = function(self, dtime) else if not self._waittime or self._waittime <= 0 then -- wait for random number of ticks. local lure_enchantment = wield and mcl_enchanting.get_enchantment(wield, "lure") or 0 - self._waittime = math.random(5, 30) - lure_enchantment * 5 + local reduced = lure_enchantment * 5 + self._waittime = math.random(math.max(0, 5 - reduced), 30 - reduced) else if self._time < self._waittime then self._time = self._time + dtime @@ -330,6 +347,7 @@ minetest.register_tool("mcl_fishing:fishing_rod", { on_place = fish, on_secondary_use = fish, sound = { breaks = "default_tool_breaks" }, + _mcl_uses = 65, }) minetest.register_craft({ diff --git a/mods/ITEMS/mcl_flowers/init.lua b/mods/ITEMS/mcl_flowers/init.lua index 42deede2f..d60cb1e3b 100644 --- a/mods/ITEMS/mcl_flowers/init.lua +++ b/mods/ITEMS/mcl_flowers/init.lua @@ -390,7 +390,7 @@ minetest.register_node("mcl_flowers:waterlily", { liquids_pointable = true, walkable = true, sunlight_propagates = true, - groups = {dig_immediate = 3, plant=1, dig_by_water = 1,destroy_by_lava_flow=1, dig_by_piston = 1, deco_block=1}, + groups = {dig_immediate = 3, plant=1, dig_by_water = 1,destroy_by_lava_flow=1, dig_by_piston = 1, deco_block=1, dig_by_boat=1}, sounds = mcl_sounds.node_sound_leaves_defaults(), node_placement_prediction = "", node_box = { @@ -448,3 +448,34 @@ minetest.register_node("mcl_flowers:waterlily", { -- Legacy support minetest.register_alias("mcl_core:tallgrass", "mcl_flowers:tallgrass") + +-- mcimport support: re-adds missing double_plant tops in mcimported worlds. +local mg_name = minetest.get_mapgen_setting("mg_name") +local mod_mcimport = minetest.get_modpath("mcimport") ~= nil +local fix_doubleplants = minetest.settings:get_bool("fix_doubleplants", true) + + + if mod_mcimport and mg_name == "singlenode" and fix_doubleplants == true then + local flowernames = { "peony", "rose_bush", "lilac", "sunflower", "double_fern", "double_grass" } + for c=1, 6 do + local flowername = flowernames[c] + end + + minetest.register_lbm({ + label = "Add double plant tops.", + name = "mcl_flowers:double_plant_topper", + run_at_every_load = true, + nodenames = { "mcl_flowers:peony", "mcl_flowers:rose_bush", "mcl_flowers:lilac", "mcl_flowers:sunflower", "mcl_flowers:double_fern", "mcl_flowers:double_grass" }, + action = function(pos, node) + for c=1, 6 do + local flowername = flowernames[c] + local bottom = pos + local top = { x = bottom.x, y = bottom.y + 1, z = bottom.z } + if node.name == "mcl_flowers:"..flowername then + minetest.set_node(top, {name = "mcl_flowers:"..flowername.."_top"}) + end + end + end, + }) + end + diff --git a/mods/ITEMS/mcl_furnaces/init.lua b/mods/ITEMS/mcl_furnaces/init.lua index 5bd2d0cfc..d3877d90b 100644 --- a/mods/ITEMS/mcl_furnaces/init.lua +++ b/mods/ITEMS/mcl_furnaces/init.lua @@ -69,6 +69,20 @@ local receive_fields = function(pos, formname, fields, sender) end end +local function give_xp(pos, player) + local meta = minetest.get_meta(pos) + local dir = vector.divide(minetest.facedir_to_dir(minetest.get_node(pos).param2),-1.95) + local xp = meta:get_int("xp") + if xp > 0 then + if player then + mcl_experience.add_experience(player, xp) + else + mcl_experience.throw_experience(vector.add(pos, dir), xp) + end + meta:set_int("xp", 0) + end +end + -- -- Node callback functions that are the same for active and inactive furnace -- @@ -143,6 +157,7 @@ local function on_metadata_inventory_take(pos, listname, index, stack, player) elseif stack:get_name() == "mcl_fishing:fish_cooked" then awards.unlock(player:get_player_name(), "mcl:cookFish") end + give_xp(pos, player) end end @@ -345,10 +360,7 @@ local function furnace_node_timer(pos, elapsed) srclist = inv:get_list("src") src_time = 0 - if mcl_experience.throw_experience then - local dir = vector.divide(minetest.facedir_to_dir(minetest.get_node(pos).param2),-1.95) - mcl_experience.throw_experience(vector.add(pos, dir), 1) - end + meta:set_int("xp", meta:get_int("xp") + 1) -- ToDo give each recipe an idividial XP count end end @@ -462,6 +474,7 @@ minetest.register_node("mcl_furnaces:furnace", { end, on_destruct = function(pos) mcl_particles.delete_node_particlespawners(pos) + give_xp(pos) end, on_metadata_inventory_move = function(pos) @@ -475,11 +488,15 @@ minetest.register_node("mcl_furnaces:furnace", { -- start timer function, it will sort out whether furnace can burn or not. minetest.get_node_timer(pos):start(1.0) end, - on_metadata_inventory_take = function(pos) + on_metadata_inventory_take = function(pos, listname, index, stack, player) -- Reset accumulated game time when player works with furnace: furnace_reset_delta_time(pos) -- start timer function, it will helpful if player clears dst slot minetest.get_node_timer(pos):start(1.0) + + if listname == "dst" then + give_xp(pos, player) + end end, allow_metadata_inventory_put = allow_metadata_inventory_put, @@ -529,6 +546,7 @@ minetest.register_node("mcl_furnaces:furnace_active", { end, on_destruct = function(pos) mcl_particles.delete_node_particlespawners(pos) + give_xp(pos) end, allow_metadata_inventory_put = allow_metadata_inventory_put, diff --git a/mods/ITEMS/mcl_heads/init.lua b/mods/ITEMS/mcl_heads/init.lua index 7502bf115..a8e9f2cb5 100644 --- a/mods/ITEMS/mcl_heads/init.lua +++ b/mods/ITEMS/mcl_heads/init.lua @@ -42,7 +42,7 @@ local function addhead(name, texture, desc, longdesc, rangemob, rangefactor) { -0.25, -0.5, -0.25, 0.25, 0.0, 0.25, }, }, }, - groups = {handy=1, armor_head=1,non_combat_armor=1, head=1, deco_block=1, dig_by_piston=1, enchantability=1}, + groups = {handy=1, armor_head=1,non_combat_armor=1, head=1, deco_block=1, dig_by_piston=1 }, -- The head textures are based off the textures of an actual mob. tiles = { -- Note: bottom texture is overlaid over top texture to get rid of possible transparency. diff --git a/mods/ITEMS/mcl_itemframes/init.lua b/mods/ITEMS/mcl_itemframes/init.lua index 40321efe9..073933fcf 100644 --- a/mods/ITEMS/mcl_itemframes/init.lua +++ b/mods/ITEMS/mcl_itemframes/init.lua @@ -145,8 +145,24 @@ minetest.register_node("mcl_itemframes:item_frame",{ paramtype = "light", paramtype2 = "facedir", sunlight_propagates = true, - groups = { dig_immediate=3,deco_block=1,dig_by_piston=1,container=7 }, + groups = { dig_immediate=3,deco_block=1,dig_by_piston=1,container=7,attached_node_facedir=1 }, sounds = mcl_sounds.node_sound_defaults(), + node_placement_prediction = "", + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return itemstack + end + + -- Use pointed node's on_rightclick function first, if present + local node = minetest.get_node(pointed_thing.under) + if placer and not placer:get_player_control().sneak then + if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then + return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack + end + end + + return minetest.item_place(itemstack, placer, pointed_thing, minetest.dir_to_facedir(vector.direction(pointed_thing.above, pointed_thing.under))) + end, on_construct = function(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() diff --git a/mods/ITEMS/mcl_jukebox/init.lua b/mods/ITEMS/mcl_jukebox/init.lua index 9a101e33f..db9f25312 100644 --- a/mods/ITEMS/mcl_jukebox/init.lua +++ b/mods/ITEMS/mcl_jukebox/init.lua @@ -81,7 +81,7 @@ local function now_playing(player, track_id) if not player or not player:is_player() or not active_huds[playername] or not hud_sequence_numbers[playername] or seq ~= hud_sequence_numbers[playername] then return end - if id == active_huds[playername] then + if id ~= nil and id == active_huds[playername] then player:hud_remove(active_huds[playername]) active_huds[playername] = nil end @@ -162,8 +162,10 @@ minetest.register_node("mcl_jukebox:jukebox", { inv:set_stack("main", 1, "") if active_tracks[cname] ~= nil then minetest.sound_stop(active_tracks[cname]) - clicker:hud_remove(active_huds[cname]) active_tracks[cname] = nil + end + if active_huds[cname] ~= nil then + clicker:hud_remove(active_huds[cname]) active_huds[cname] = nil end else @@ -219,8 +221,10 @@ minetest.register_node("mcl_jukebox:jukebox", { dropped_item:set_yaw(math.pi/2) if active_tracks[name] ~= nil then minetest.sound_stop(active_tracks[name]) - digger:hud_remove(active_huds[name]) active_tracks[name] = nil + end + if active_huds[name] ~= nil then + digger:hud_remove(active_huds[name]) active_huds[name] = nil end end diff --git a/mods/ITEMS/mcl_portals/portal_end.lua b/mods/ITEMS/mcl_portals/portal_end.lua index 9dadc3a0d..5919ef213 100644 --- a/mods/ITEMS/mcl_portals/portal_end.lua +++ b/mods/ITEMS/mcl_portals/portal_end.lua @@ -21,6 +21,21 @@ local destroy_portal = function(pos) end end +local ep_scheme = { + { o={x=0, y=0, z=1}, p=1 }, + { o={x=0, y=0, z=2}, p=1 }, + { o={x=0, y=0, z=3}, p=1 }, + { o={x=1, y=0, z=4}, p=2 }, + { o={x=2, y=0, z=4}, p=2 }, + { o={x=3, y=0, z=4}, p=2 }, + { o={x=4, y=0, z=3}, p=3 }, + { o={x=4, y=0, z=2}, p=3 }, + { o={x=4, y=0, z=1}, p=3 }, + { o={x=3, y=0, z=0}, p=0 }, + { o={x=2, y=0, z=0}, p=0 }, + { o={x=1, y=0, z=0}, p=0 }, +} + -- End portal minetest.register_node("mcl_portals:portal_end", { description = S("End Portal"), @@ -52,7 +67,7 @@ minetest.register_node("mcl_portals:portal_end", { paramtype = "light", sunlight_propagates = true, use_texture_alpha = true, - walkable = true, + walkable = false, diggable = false, pointable = false, buildable_to = false, @@ -107,97 +122,19 @@ end -- Check if pos is part of a valid end portal frame, filled with eyes of ender. local function check_end_portal_frame(pos) - -- Check if pos has an end portal frame with eye of ender - local eframe = function(pos, param2) - local node = minetest.get_node(pos) - if node.name == "mcl_portals:end_portal_frame_eye" then - if param2 == nil or node.param2 == param2 then - return true, node + for i = 1, 12 do + local pos0 = vector.subtract(pos, ep_scheme[i].o) + local portal = true + for j = 1, 12 do + local p = vector.add(pos0, ep_scheme[j].o) + local node = minetest.get_node(p) + if not node or node.name ~= "mcl_portals:end_portal_frame_eye" or node.param2 ~= ep_scheme[j].p then + portal = false + break end end - return false - end - - -- Step 1: Find a row of 3 end portal frames with eyes, all facing the same direction - local streak = 0 - local streak_start, streak_end, streak_start_node, streak_end_node - local last_param2 - local axes = { "x", "z" } - for a=1, #axes do - local axis = axes[a] - for b=pos[axis]-2, pos[axis]+2 do - local cpos = table.copy(pos) - cpos[axis] = b - local e, node = eframe(cpos, last_param2) - if e then - last_param2 = node.param2 - streak = streak + 1 - if streak == 1 then - streak_start = table.copy(pos) - streak_start[axis] = b - streak_start_node = node - elseif streak == 3 then - streak_end = table.copy(pos) - streak_end[axis] = b - streak_end_node = node - break - end - else - streak = 0 - last_param2 = nil - end - end - if streak_end then - break - end - streak = 0 - last_param2 = nil - end - -- Has a row been found? - if streak_end then - -- Step 2: Using the known facedir, check the remaining spots in which we expect - -- “eyed” end portal frames. - local dir = minetest.facedir_to_dir(streak_start_node.param2) - if dir.x ~= 0 then - for i=1, 3 do - if not eframe({x=streak_start.x + i*dir.x, y=streak_start.y, z=streak_start.z - 1}) then - return false - end - if not eframe({x=streak_start.x + i*dir.x, y=streak_start.y, z=streak_end.z + 1}) then - return false - end - if not eframe({x=streak_start.x + 4*dir.x, y=streak_start.y, z=streak_start.z + i-1}) then - return false - end - end - -- All checks survived! We have a valid portal! - local k - if dir.x > 0 then - k = 1 - else - k = -3 - end - return true, { x = streak_start.x + k, y = streak_start.y, z = streak_start.z } - elseif dir.z ~= 0 then - for i=1, 3 do - if not eframe({x=streak_start.x - 1, y=streak_start.y, z=streak_start.z + i*dir.z}) then - return false - end - if not eframe({x=streak_end.x + 1, y=streak_start.y, z=streak_start.z + i*dir.z}) then - return false - end - if not eframe({x=streak_start.x + i-1, y=streak_start.y, z=streak_start.z + 4*dir.z}) then - return false - end - end - local k - if dir.z > 0 then - k = 1 - else - k = -3 - end - -- All checks survived! We have a valid portal! - return true, { x = streak_start.x, y = streak_start.y, z = streak_start.z + k } + if portal then + return true, {x=pos0.x+1, y=pos0.y, z=pos0.z+1} end end return false @@ -222,82 +159,92 @@ local function end_portal_area(pos, destroy) minetest.bulk_set_node(posses, {name=name}) end +function mcl_portals.end_teleport(obj, pos) + if not obj then return end + local pos = pos or obj:get_pos() + if not pos then return end + local dim = mcl_worlds.pos_to_dimension(pos) + + local target + if dim == "end" then + -- End portal in the End: + -- Teleport back to the player's spawn or world spawn in the Overworld. + if obj:is_player() then + target = mcl_spawn.get_player_spawn_pos(obj) + end + + target = target or mcl_spawn.get_world_spawn_pos(obj) + else + -- End portal in any other dimension: + -- Teleport to the End at a fixed position and generate a + -- 5×5 obsidian platform below. + + local platform_pos = mcl_vars.mg_end_platform_pos + -- force emerge of target1 area + minetest.get_voxel_manip():read_from_map(platform_pos, platform_pos) + if not minetest.get_node_or_nil(platform_pos) then + minetest.emerge_area(vector.subtract(platform_pos, 3), vector.add(platform_pos, 3)) + end + + -- Build destination + local function check_and_build_end_portal_destination(pos) + local n = minetest.get_node_or_nil(pos) + if n and n.name ~= "mcl_core:obsidian" then + build_end_portal_destination(pos) + minetest.after(2, check_and_build_end_portal_destination, pos) + elseif not n then + minetest.after(1, check_and_build_end_portal_destination, pos) + end + end + + local platform + build_end_portal_destination(platform_pos) + check_and_build_end_portal_destination(platform_pos) + + target = table.copy(platform_pos) + target.y = target.y + 1 + end + + -- Teleport + obj:set_pos(target) + + if obj:is_player() then + -- Look towards the main End island + if dim ~= "end" then + obj:set_look_horizontal(math.pi/2) + end + mcl_worlds.dimension_change(obj, mcl_worlds.pos_to_dimension(target)) + minetest.sound_play("mcl_portals_teleport", {pos=target, gain=0.5, max_hear_distance = 16}, true) + end +end + +function mcl_portals.end_portal_teleport(pos, node) + for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do + local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel + if obj:is_player() or lua_entity then + local objpos = obj:get_pos() + if objpos == nil then + return + end + + -- Check if object is actually in portal. + objpos.y = math.ceil(objpos.y) + if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then + return + end + + mcl_portals.end_teleport(obj, objpos) + + end + end +end + minetest.register_abm({ label = "End portal teleportation", nodenames = {"mcl_portals:portal_end"}, - interval = 1, + interval = 0.1, chance = 1, - action = function(pos, node) - for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do - local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel - if obj:is_player() or lua_entity then - local dim = mcl_worlds.pos_to_dimension(pos) - - local objpos = obj:get_pos() - if objpos == nil then - return - end - - -- Check if object is actually in portal. - objpos.y = math.ceil(objpos.y) - if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then - return - end - - local target - if dim == "end" then - -- End portal in the End: - -- Teleport back to the player's spawn or world spawn in the Overworld. - - if obj:is_player() then - target = mcl_spawn.get_spawn_pos(obj) - else - target = mcl_spawn.get_world_spawn_pos(obj) - end - else - -- End portal in any other dimension: - -- Teleport to the End at a fixed position and generate a - -- 5×5 obsidian platform below. - - local platform_pos = mcl_vars.mg_end_platform_pos - -- force emerge of target1 area - minetest.get_voxel_manip():read_from_map(platform_pos, platform_pos) - if not minetest.get_node_or_nil(platform_pos) then - minetest.emerge_area(vector.subtract(platform_pos, 3), vector.add(platform_pos, 3)) - end - - -- Build destination - local function check_and_build_end_portal_destination(pos) - local n = minetest.get_node_or_nil(pos) - if n and n.name ~= "mcl_core:obsidian" then - build_end_portal_destination(pos) - minetest.after(2, check_and_build_end_portal_destination, pos) - elseif not n then - minetest.after(1, check_and_build_end_portal_destination, pos) - end - end - - local platform - build_end_portal_destination(platform_pos) - check_and_build_end_portal_destination(platform_pos) - - target = table.copy(platform_pos) - target.y = target.y + 1 - end - - -- Teleport - obj:set_pos(target) - if obj:is_player() then - -- Look towards the main End island - if dim ~= "end" then - obj:set_look_horizontal(math.pi/2) - end - mcl_worlds.dimension_change(obj, mcl_worlds.pos_to_dimension(target)) - minetest.sound_play("mcl_portals_teleport", {pos=target, gain=0.5, max_hear_distance = 16}, true) - end - end - end - end, + action = mcl_portals.end_portal_teleport, }) local rotate_frame, rotate_frame_eye @@ -308,6 +255,21 @@ if minetest.get_modpath("screwdriver") then rotate_frame_eye = false end +local function after_place_node(pos, placer, itemstack, pointed_thing) + local node = minetest.get_node(pos) + if node then + node.param2 = (node.param2+2) % 4 + minetest.swap_node(pos, node) + + local ok, ppos = check_end_portal_frame(pos) + if ok then + -- Epic 'portal open' sound effect that can be heard everywhere + minetest.sound_play("mcl_portals_open_end_portal", {gain=0.8}, true) + end_portal_area(ppos) + end + end +end + minetest.register_node("mcl_portals:end_portal_frame", { description = S("End Portal Frame"), _tt_help = S("Used to construct end portals"), @@ -329,6 +291,8 @@ minetest.register_node("mcl_portals:end_portal_frame", { on_rotate = rotate_frame, + after_place_node = after_place_node, + _mcl_blast_resistance = 36000000, _mcl_hardness = -1, }) @@ -359,17 +323,11 @@ minetest.register_node("mcl_portals:end_portal_frame_eye", { end_portal_area(ppos, true) end end, - on_construct = function(pos) - local ok, ppos = check_end_portal_frame(pos) - if ok then - -- Epic 'portal open' sound effect that can be heard everywhere - minetest.sound_play("mcl_portals_open_end_portal", {gain=0.8}, true) - end_portal_area(ppos) - end - end, on_rotate = rotate_frame_eye, + after_place_node = after_place_node, + _mcl_blast_resistance = 36000000, _mcl_hardness = -1, }) @@ -411,8 +369,11 @@ minetest.override_item("mcl_end:ender_eye", { itemstack:take_item() -- 1 use end - local ok = check_end_portal_frame(pointed_thing.under) + local ok, ppos = check_end_portal_frame(pointed_thing.under) if ok then + -- Epic 'portal open' sound effect that can be heard everywhere + minetest.sound_play("mcl_portals_open_end_portal", {gain=0.8}, true) + end_portal_area(ppos) if minetest.get_modpath("doc") then doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:portal_end") end diff --git a/mods/ITEMS/mcl_potions/potions.lua b/mods/ITEMS/mcl_potions/potions.lua index 9202196a8..4a82348e5 100644 --- a/mods/ITEMS/mcl_potions/potions.lua +++ b/mods/ITEMS/mcl_potions/potions.lua @@ -447,7 +447,7 @@ local dragon_breath_def = { no_throwable = true, no_effect = true, _longdesc = S("This item is used in brewing and can be combined with splash potions to create lingering potions."), - color = "#BF4567", + image = "mcl_potions_dragon_breath.png", groups = { brewitem = 1 }, on_use = nil, stack_max = 64, diff --git a/mods/ITEMS/mcl_signs/init.lua b/mods/ITEMS/mcl_signs/init.lua index 441fcbb4c..d21b58242 100644 --- a/mods/ITEMS/mcl_signs/init.lua +++ b/mods/ITEMS/mcl_signs/init.lua @@ -197,7 +197,9 @@ local destruct_sign = function(pos) end local players = minetest.get_connected_players() for p=1, #players do - minetest.close_formspec(players[p]:get_player_name(), "mcl_signs:set_text_"..pos.x.."_"..pos.y.."_"..pos.z) + if vector.distance(players[p]:get_pos(), pos) <= 30 then + minetest.close_formspec(players[p]:get_player_name(), "mcl_signs:set_text_"..pos.x.."_"..pos.y.."_"..pos.z) + end end end diff --git a/mods/ITEMS/mcl_tools/init.lua b/mods/ITEMS/mcl_tools/init.lua index 5df422f08..91aacc287 100644 --- a/mods/ITEMS/mcl_tools/init.lua +++ b/mods/ITEMS/mcl_tools/init.lua @@ -172,18 +172,20 @@ minetest.register_tool("mcl_tools:pick_diamond", { _repair_material = "mcl_core:diamond", }) -local get_shovel_dig_group = function(itemstring) - local def = minetest.registered_items[itemstring] - if itemstring == "mcl_tools:shovel_wood" then - return "shovely_dig_wood" - elseif itemstring == "mcl_tools:shovel_stone" then - return "shovely_dig_stone" - elseif itemstring == "mcl_tools:shovel_iron" then - return "shovely_dig_iron" - elseif itemstring == "mcl_tools:shovel_gold" then - return "shovely_dig_gold" - elseif itemstring == "mcl_tools:shovel_diamond" then - return "shovely_dig_diamond" +local get_shovel_dig_group = function(itemstack) + local itemstring = itemstack:get_name() + local efficiency_level = mcl_enchanting.get_enchantment(itemstack, "efficiency") + local postfix = efficiency_level > 0 and "_efficiency_" .. efficiency_level or "" + if itemstring:find("mcl_tools:shovel_wood") == 1 then + return "shovely_dig_wood" .. postfix + elseif itemstring:find("mcl_tools:shovel_stone") == 1 then + return "shovely_dig_stone" .. postfix + elseif itemstring:find("mcl_tools:shovel_iron") == 1 then + return "shovely_dig_iron" .. postfix + elseif itemstring:find("mcl_tools:shovel_gold") == 1 then + return "shovely_dig_gold" .. postfix + elseif itemstring:find("mcl_tools:shovel_diamond") == 1 then + return "shovely_dig_diamond" .. postfix else -- Fallback return "shovely_dig_wood" @@ -217,9 +219,10 @@ local make_grass_path = function(itemstack, placer, pointed_thing) -- Add wear, as if digging a level 0 shovely node local toolname = itemstack:get_name() local def = minetest.registered_items[toolname] - local group = get_shovel_dig_group(toolname) - local base_uses = def.tool_capabilities.groupcaps[group].uses - local maxlevel = def.tool_capabilities.groupcaps[group].maxlevel + local group = get_shovel_dig_group(itemstack) + local toolcaps = itemstack:get_tool_capabilities() + local base_uses = toolcaps.groupcaps[group].uses + local maxlevel = toolcaps.groupcaps[group].maxlevel local uses = base_uses * math.pow(3, maxlevel) local wear = math.ceil(65535 / uses) itemstack:add_wear(wear) @@ -275,7 +278,6 @@ minetest.register_tool("mcl_tools:shovel_wood", { _doc_items_usagehelp = shovel_use, _doc_items_hidden = false, inventory_image = "default_tool_woodshovel.png", - wield_image = "default_tool_woodshovel.png^[transformR90", wield_scale = wield_scale, groups = { tool=1, shovel=1, dig_speed_class=2, enchantability=15 }, tool_capabilities = { @@ -296,7 +298,6 @@ minetest.register_tool("mcl_tools:shovel_stone", { _doc_items_longdesc = shovel_longdesc, _doc_items_usagehelp = shovel_use, inventory_image = "default_tool_stoneshovel.png", - wield_image = "default_tool_stoneshovel.png^[transformR90", wield_scale = wield_scale, groups = { tool=1, shovel=1, dig_speed_class=3, enchantability=5 }, tool_capabilities = { @@ -317,7 +318,6 @@ minetest.register_tool("mcl_tools:shovel_iron", { _doc_items_longdesc = shovel_longdesc, _doc_items_usagehelp = shovel_use, inventory_image = "default_tool_steelshovel.png", - wield_image = "default_tool_steelshovel.png^[transformR90", wield_scale = wield_scale, groups = { tool=1, shovel=1, dig_speed_class=4, enchantability=14 }, tool_capabilities = { @@ -338,9 +338,8 @@ minetest.register_tool("mcl_tools:shovel_gold", { _doc_items_longdesc = shovel_longdesc, _doc_items_usagehelp = shovel_use, inventory_image = "default_tool_goldshovel.png", - wield_image = "default_tool_goldshovel.png^[transformR90", wield_scale = wield_scale, - groups = { tool=1, shovel=1, dig_speed_class=6, }, + groups = { tool=1, shovel=1, dig_speed_class=6, enchantability=22 }, tool_capabilities = { full_punch_interval = 1, max_drop_level=2, @@ -359,7 +358,6 @@ minetest.register_tool("mcl_tools:shovel_diamond", { _doc_items_longdesc = shovel_longdesc, _doc_items_usagehelp = shovel_use, inventory_image = "default_tool_diamondshovel.png", - wield_image = "default_tool_diamondshovel.png^[transformR90", wield_scale = wield_scale, groups = { tool=1, shovel=1, dig_speed_class=5, enchantability=10 }, tool_capabilities = { @@ -576,7 +574,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, shears=1, dig_speed_class=4, enchantability=1 }, + groups = { tool=1, shears=1, dig_speed_class=4, }, tool_capabilities = { full_punch_interval = 0.5, max_drop_level=1, diff --git a/mods/ITEMS/screwdriver/init.lua b/mods/ITEMS/screwdriver/init.lua index bf264a50c..ec4f1a2ad 100644 --- a/mods/ITEMS/screwdriver/init.lua +++ b/mods/ITEMS/screwdriver/init.lua @@ -176,7 +176,7 @@ minetest.register_tool("screwdriver:screwdriver", { description = S("Screwdriver"), inventory_image = "screwdriver.png", wield_image = "screwdriver.png^[transformFX", - groups = { tool = 1, not_in_creative_inventory = 1, enchantability = 1 }, + groups = { tool = 1, not_in_creative_inventory = 1 }, on_use = function(itemstack, user, pointed_thing) screwdriver.handler(itemstack, user, pointed_thing, screwdriver.ROTATE_FACE, 200) return itemstack diff --git a/mods/ITEMS/xpanes/init.lua b/mods/ITEMS/xpanes/init.lua index 4ecac8c6e..c17f2cd69 100644 --- a/mods/ITEMS/xpanes/init.lua +++ b/mods/ITEMS/xpanes/init.lua @@ -127,7 +127,7 @@ function xpanes.register_pane(name, def) connect_sides = { "left", "right" }, _mcl_blast_resistance = def._mcl_blast_resistance, _mcl_hardness = def._mcl_hardness, - _mcl_silk_touch_drop = def._mcl_silk_touch_drop, + _mcl_silk_touch_drop = def._mcl_silk_touch_drop and {"xpanes:" .. name .. "_flat"}, }) local groups = table.copy(def.groups) @@ -156,7 +156,7 @@ function xpanes.register_pane(name, def) drop = drop, _mcl_blast_resistance = def._mcl_blast_resistance, _mcl_hardness = def._mcl_hardness, - _mcl_silk_touch_drop = def._mcl_silk_touch_drop, + _mcl_silk_touch_drop = def._mcl_silk_touch_drop and {"xpanes:" .. name .. "_flat"}, }) minetest.register_craft({ diff --git a/mods/MAPGEN/mcl_dungeons/init.lua b/mods/MAPGEN/mcl_dungeons/init.lua index b270e6527..1ce1556b2 100644 --- a/mods/MAPGEN/mcl_dungeons/init.lua +++ b/mods/MAPGEN/mcl_dungeons/init.lua @@ -8,6 +8,7 @@ if mcl_vars.mg_dungeons == false then return end +if mg_name ~= "singlenode" then -- Get loot for dungeon chests local get_loot = function() local loottable = { @@ -21,12 +22,10 @@ local get_loot = function() { itemstring = "mcl_jukebox:record_4", weight = 15 }, { itemstring = "mobs_mc:iron_horse_armor", weight = 15 }, { itemstring = "mcl_core:apple_gold", weight = 15 }, - -- TODO: Enchanted Book - { itemstring = "mcl_books:book", weight = 10 }, + { itemstack = mcl_enchanting.get_uniform_randomly_enchanted_book({"soul_speed"}), weight = 10 }, { itemstring = "mobs_mc:gold_horse_armor", weight = 10 }, { itemstring = "mobs_mc:diamond_horse_armor", weight = 5 }, - -- TODO: Enchanted Golden Apple - { itemstring = "mcl_core:apple_gold", weight = 2 }, + { itemstring = "mcl_core:apple_gold_enchanted", weight = 2 }, } }, { @@ -398,3 +397,4 @@ minetest.register_on_generated(function(minp, maxp) end end) +end diff --git a/mods/MAPGEN/mcl_structures/init.lua b/mods/MAPGEN/mcl_structures/init.lua index 7723a2688..bc3b1b1a7 100644 --- a/mods/MAPGEN/mcl_structures/init.lua +++ b/mods/MAPGEN/mcl_structures/init.lua @@ -402,8 +402,7 @@ mcl_structures.generate_desert_temple = function(pos) { itemstring = "mcl_mobitems:bone", weight = 25, amount_min = 4, amount_max=6 }, { itemstring = "mcl_mobitems:rotten_flesh", weight = 25, amount_min = 3, amount_max=7 }, { itemstring = "mcl_mobitems:spider_eye", weight = 25, amount_min = 1, amount_max=3 }, - -- TODO: Enchanted Book - { itemstring = "mcl_books:book", weight = 20, }, + { itemstack = mcl_enchanting.get_uniform_randomly_enchanted_book({"soul_speed"}), weight = 20, }, { itemstring = "mcl_mobitems:saddle", weight = 20, }, { itemstring = "mcl_core:apple_gold", weight = 20, }, { itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 }, @@ -414,8 +413,7 @@ mcl_structures.generate_desert_temple = function(pos) { itemstring = "mobs_mc:gold_horse_armor", weight = 10, }, { itemstring = "mobs_mc:diamond_horse_armor", weight = 5, }, { itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 3 }, - -- TODO: Enchanted Golden Apple - { itemstring = "mcl_core:apple_gold", weight = 2, }, + { itemstring = "mcl_core:apple_gold_enchanted", weight = 2, }, } }, { diff --git a/mods/MAPGEN/mcl_villages/README.txt b/mods/MAPGEN/mcl_villages/README.txt new file mode 100644 index 000000000..b266a131a --- /dev/null +++ b/mods/MAPGEN/mcl_villages/README.txt @@ -0,0 +1,45 @@ +MCL_Villages: +============================ +A fork of Rochambeau's "Settlements" mod converted for use in MineClone2. + +-------------- +Using the mod: +-------------- +This mod adds settlements on world generation. + +And, in Creative Mode; also comes with a debug tool for spawning in villages. + + +------------- +MCL2 Credits: +------------- +Code forked from: https://github.com/MysticTempest/settlements/tree/mcl_villages + Commit: e24b4be +================================================================================ +Basic conversion of Settlements mod for compatibility with MineClone2, plus new schematics: MysticTempest + +Seed-based Village Generation, multi-threading, bugfixes: kay27 + + + +========================= +version: 0.1 alpha + +License of source code: WTFPL +----------------------------- +(c) Copyright Rochambeau (2018) + +This program is free software. It comes without any warranty, to +the extent permitted by applicable law. You can redistribute it +and/or modify it under the terms of the Do What The Fuck You Want +To Public License, Version 2, as published by Sam Hocevar. See +http://sam.zoy.org/wtfpl/COPYING for more details. + + +Credits: +-------------- +This mod is based on "ruins" by BlockMen + +Completely new schematics for MineClone2: +MysticTempest - CC-BY-SA 4.0 + diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua new file mode 100644 index 000000000..05f08bdcc --- /dev/null +++ b/mods/MAPGEN/mcl_villages/buildings.lua @@ -0,0 +1,236 @@ +--[[ +------------------------------------------------------------------------------- +-- build schematic, replace material, rotation +------------------------------------------------------------------------------- +function settlements.build_schematic(vm, data, va, pos, building, replace_wall, name) + -- get building node material for better integration to surrounding + local platform_material = minetest.get_node_or_nil(pos) + if not platform_material then + return + end + platform_material = platform_material.name + -- pick random material + local material = wallmaterial[math.random(1,#wallmaterial)] + -- schematic conversion to lua + local schem_lua = minetest.serialize_schematic(building, + "lua", + {lua_use_comments = false, lua_num_indent_spaces = 0}).." return(schematic)" + -- replace material + if replace_wall == "y" then + schem_lua = schem_lua:gsub("mcl_core:cobble", material) + end + schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", + platform_material) + +-- Disable special junglewood for now. + -- special material for spawning npcs + -- schem_lua = schem_lua:gsub("mcl_core:junglewood", + -- "settlements:junglewood") +-- + + -- format schematic string + local schematic = loadstring(schem_lua)() + -- build foundation for the building an make room above + local width = schematic["size"]["x"] + local depth = schematic["size"]["z"] + local height = schematic["size"]["y"] + local possible_rotations = {"0", "90", "180", "270"} + local rotation = possible_rotations[ math.random( #possible_rotations ) ] + settlements.foundation( + pos, + width, + depth, + height, + rotation) + vm:set_data(data) + -- place schematic + + minetest.place_schematic_on_vmanip( + vm, + pos, + schematic, + rotation, + nil, + true) + vm:write_to_map(true) +end]] +------------------------------------------------------------------------------- +-- initialize settlement_info +------------------------------------------------------------------------------- +function settlements.initialize_settlement_info(pr) + local count_buildings = {} + + -- count_buildings table reset + for k,v in pairs(settlements.schematic_table) do + count_buildings[v["name"]] = 0 + end + + -- randomize number of buildings + local number_of_buildings = pr:next(10, 25) + local number_built = 1 + settlements.debug("Village ".. number_of_buildings) + + return count_buildings, number_of_buildings, number_built +end +------------------------------------------------------------------------------- +-- fill settlement_info +-------------------------------------------------------------------------------- +function settlements.create_site_plan(maxp, minp, pr) + local settlement_info = {} + local building_all_info + local possible_rotations = {"0", "90", "180", "270"} + -- find center of chunk + local center = { + x=maxp.x-half_map_chunk_size, + y=maxp.y, + z=maxp.z-half_map_chunk_size + } + -- find center_surface of chunk + local center_surface , surface_material = settlements.find_surface(center) + -- go build settlement around center + if not center_surface then return false end + + -- add settlement to list + table.insert(settlements_in_world, center_surface) + -- save list to file + settlements.save() + -- initialize all settlement_info table + local count_buildings, number_of_buildings, number_built = settlements.initialize_settlement_info(pr) + -- first building is townhall in the center + building_all_info = settlements.schematic_table[1] + local rotation = possible_rotations[ pr:next(1, #possible_rotations ) ] + -- add to settlement info table + local index = 1 + settlement_info[index] = { + pos = center_surface, + name = building_all_info["name"], + hsize = building_all_info["hsize"], + rotat = rotation, + surface_mat = surface_material + } + --increase index for following buildings + index = index + 1 + -- now some buildings around in a circle, radius = size of town center + local x, z, r = center_surface.x, center_surface.z, building_all_info["hsize"] + -- draw j circles around center and increase radius by math.random(2,5) + for j = 1,20 do + if number_built < number_of_buildings then + -- set position on imaginary circle + for j = 0, 360, 15 do + local angle = j * math.pi / 180 + local ptx, ptz = x + r * math.cos( angle ), z + r * math.sin( angle ) + ptx = settlements.round(ptx, 0) + ptz = settlements.round(ptz, 0) + local pos1 = { x=ptx, y=center_surface.y+50, z=ptz} + local pos_surface, surface_material = settlements.find_surface(pos1) + if not pos_surface then break end + + local randomized_schematic_table = shuffle(settlements.schematic_table, pr) + -- pick schematic + local size = #randomized_schematic_table + for i = size, 1, -1 do + -- already enough buildings of that type? + if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then + building_all_info = randomized_schematic_table[i] + -- check distance to other buildings + local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"]) + if distance_to_other_buildings_ok then + -- count built houses + count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1 + rotation = possible_rotations[ pr:next(1, #possible_rotations ) ] + number_built = number_built + 1 + settlement_info[index] = { + pos = pos_surface, + name = building_all_info["name"], + hsize = building_all_info["hsize"], + rotat = rotation, + surface_mat = surface_material + } + index = index + 1 + break + end + end + end + if number_of_buildings == number_built then + break + end + end + r = r + pr:next(2,5) + end + end + settlements.debug("really ".. number_built) + return settlement_info +end +------------------------------------------------------------------------------- +-- evaluate settlement_info and place schematics +------------------------------------------------------------------------------- +function settlements.place_schematics(settlement_info, pr) + local building_all_info + for i, built_house in ipairs(settlement_info) do + for j, schem in ipairs(settlements.schematic_table) do + if settlement_info[i]["name"] == schem["name"] then + building_all_info = schem + break + end + end + + local pos = settlement_info[i]["pos"] + local rotation = settlement_info[i]["rotat"] + -- get building node material for better integration to surrounding + local platform_material = settlement_info[i]["surface_mat"] + --platform_material_name = minetest.get_name_from_content_id(platform_material) + -- pick random material + --local material = wallmaterial[pr:next(1,#wallmaterial)] + -- + local building = building_all_info["mts"] + local replace_wall = building_all_info["rplc"] + -- schematic conversion to lua + local schem_lua = minetest.serialize_schematic(building, + "lua", + {lua_use_comments = false, lua_num_indent_spaces = 0}).." return(schematic)" + -- replace material + if replace_wall then + --Note, block substitution isn't matching node names exactly; so nodes that are to be substituted that have the same prefixes cause bugs. + -- Example: Attempting to swap out 'mcl_core:stonebrick'; which has multiple, additional sub-variants: (carved, cracked, mossy). Will currently cause issues, so leaving disabled. + if platform_material == "mcl_core:snow" or platform_material == "mcl_core:dirt_with_grass_snow" or platform_material == "mcl_core:podzol" then + schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sprucetree") + schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sprucewood") + --schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:spruce_fence") + --schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_sprucewood_top") + --schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_sprucewood") + --schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_sprucewood_off") + elseif platform_material == "mcl_core:sand" or platform_material == "mcl_core:redsand" then + schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sandstonecarved") + schem_lua = schem_lua:gsub("mcl_core:cobble", "mcl_core:sandstone") + schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sandstonesmooth") + --schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:birch_fence") + --schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_birchwood_top") + --schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_birchwood") + --schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_birchwood_off") + --schem_lua = schem_lua:gsub("mcl_stairs:stair_stonebrick", "mcl_stairs:stair_redsandstone") + --schem_lua = schem_lua:gsub("mcl_core:stonebrick", "mcl_core:redsandstonesmooth") + schem_lua = schem_lua:gsub("mcl_core:brick_block", "mcl_core:redsandstone") + end + end + schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", platform_material) + + --[[ Disable special junglewood for now. + -- special material for spawning npcs + schem_lua = schem_lua:gsub("mcl_core:junglewood", "settlements:junglewood") + --]] + + schem_lua = schem_lua:gsub("mcl_stairs:stair_wood_outer", "mcl_stairs:slab_wood") + schem_lua = schem_lua:gsub("mcl_stairs:stair_stone_rough_outer", "air") + + -- format schematic string + local schematic = loadstring(schem_lua)() + -- build foundation for the building an make room above + -- place schematic + minetest.place_schematic( + pos, + schematic, + rotation, + nil, + true) + end +end diff --git a/mods/MAPGEN/mcl_villages/const.lua b/mods/MAPGEN/mcl_villages/const.lua new file mode 100644 index 000000000..4e2b39136 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/const.lua @@ -0,0 +1,81 @@ +-- switch for debugging +settlements.debug = function(message) + -- minetest.chat_send_all(message) + -- minetest.log("warning", "[mcl_villages] "..message) + minetest.log("verbose", "[mcl_villages] "..message) +end + +--[[ Manually set in 'buildings.lua' +-- material to replace cobblestone with +wallmaterial = { + "mcl_core:junglewood", + "mcl_core:sprucewood", + "mcl_core:wood", + "mcl_core:birchwood", + "mcl_core:acaciawood", + "mcl_core:stonebrick", + "mcl_core:cobble", + "mcl_core:sandstonecarved", + "mcl_core:sandstone", + "mcl_core:sandstonesmooth2" +} +--]] +settlements.surface_mat = {} +------------------------------------------------------------------------------- +-- Set array to list +-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list +------------------------------------------------------------------------------- +function settlements.grundstellungen() + settlements.surface_mat = settlements.Set { + "mcl_core:dirt_with_grass", + --"mcl_core:dry_dirt_with_grass", + "mcl_core:dirt_with_grass_snow", + --"mcl_core:dirt_with_dry_grass", + "mcl_core:podzol", + "mcl_core:sand", + "mcl_core:redsand", + --"mcl_core:silver_sand", + "mcl_core:snow" + } +end +-- +-- possible surfaces where buildings can be built +-- + +-- +-- path to schematics +-- +schem_path = settlements.modpath.."/schematics/" +-- +-- list of schematics +-- +local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true) + +settlements.schematic_table = { + {name = "large_house", mts = schem_path.."large_house.mts", hwidth = 11, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, + {name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.055, rplc = basic_pseudobiome_villages }, + {name = "butcher", mts = schem_path.."butcher.mts", hwidth = 11, hdepth = 8, hheight = 10, hsize = 14, max_num = 0.03 , rplc = basic_pseudobiome_villages }, + {name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 13, hheight = 14, hsize = 15, max_num = 0.04 , rplc = basic_pseudobiome_villages }, + {name = "farm", mts = schem_path.."farm.mts", hwidth = 7, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1 , rplc = basic_pseudobiome_villages }, + {name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 3, hheight = 13, hsize = 10, max_num = 0.1 , rplc = false }, + {name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 8, hsize = 13, max_num = 0.04 , rplc = basic_pseudobiome_villages }, + {name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 8, hdepth = 12, hheight = 8, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, + {name = "small_house", mts = schem_path.."small_house.mts", hwidth = 9, hdepth = 7, hheight = 8, hsize = 13, max_num = 0.7 , rplc = basic_pseudobiome_villages }, + {name = "tavern", mts = schem_path.."tavern.mts", hwidth = 11, hdepth = 10, hheight = 10, hsize = 13, max_num = 0.050, rplc = basic_pseudobiome_villages }, + {name = "well", mts = schem_path.."well.mts", hwidth = 6, hdepth = 8, hheight = 6, hsize = 10, max_num = 0.045, rplc = basic_pseudobiome_villages }, +} + +-- +-- list of settlements, load on server start up +-- +settlements_in_world = {} +-- +-- +-- maximum allowed difference in height for building a sttlement +-- +max_height_difference = 56 +-- +-- +-- +half_map_chunk_size = 40 +quarter_map_chunk_size = 20 diff --git a/mods/MAPGEN/mcl_villages/depends.txt b/mods/MAPGEN/mcl_villages/depends.txt new file mode 100644 index 000000000..9ba29f3ee --- /dev/null +++ b/mods/MAPGEN/mcl_villages/depends.txt @@ -0,0 +1,4 @@ +mcl_core +mcl_loot +mcl_farming? +mobs_mc? diff --git a/mods/MAPGEN/mcl_villages/foundation.lua b/mods/MAPGEN/mcl_villages/foundation.lua new file mode 100644 index 000000000..15936ef4c --- /dev/null +++ b/mods/MAPGEN/mcl_villages/foundation.lua @@ -0,0 +1,65 @@ +------------------------------------------------------------------------------- +-- function to fill empty space below baseplate when building on a hill +------------------------------------------------------------------------------- +function settlements.ground(pos, pr) -- role model: Wendelsteinkircherl, Brannenburg + local p2 = vector.new(pos) + local cnt = 0 + local mat = "mcl_core:dirt" + p2.y = p2.y-1 + while true do + cnt = cnt+1 + if cnt > 20 then break end + if cnt>pr:next(2,4) then + mat = "mcl_core:stone" + end + minetest.swap_node(p2, {name=mat}) + p2.y = p2.y-1 + end +end +------------------------------------------------------------------------------- +-- function clear space above baseplate +------------------------------------------------------------------------------- +function settlements.terraform(settlement_info, pr) + local fheight, fwidth, fdepth, schematic_data + + for i, built_house in ipairs(settlement_info) do + -- pick right schematic_info to current built_house + for j, schem in ipairs(settlements.schematic_table) do + if settlement_info[i]["name"] == schem["name"] then + schematic_data = schem + break + end + end + local pos = settlement_info[i]["pos"] + if settlement_info[i]["rotat"] == "0" or settlement_info[i]["rotat"] == "180" then + fwidth = schematic_data["hwidth"] + fdepth = schematic_data["hdepth"] + else + fwidth = schematic_data["hdepth"] + fdepth = schematic_data["hwidth"] + end + --fheight = schematic_data["hheight"] * 3 -- remove trees and leaves above + fheight = schematic_data["hheight"] -- remove trees and leaves above + -- + -- now that every info is available -> create platform and clear space above + -- + for xi = 0,fwidth-1 do + for zi = 0,fdepth-1 do + for yi = 0,fheight *3 do + if yi == 0 then + local p = {x=pos.x+xi, y=pos.y, z=pos.z+zi} + settlements.ground(p, pr) + else + -- write ground + local p = {x=pos.x+xi, y=pos.y+yi, z=pos.z+zi} + minetest.forceload_block(p) + local node = minetest.get_node_or_nil(p) + if node and node.name ~= "air" then + minetest.swap_node(p,{name="air"}) + end + end + end + end + end + end +end diff --git a/mods/MAPGEN/mcl_villages/init.lua b/mods/MAPGEN/mcl_villages/init.lua new file mode 100644 index 000000000..5ee3b054a --- /dev/null +++ b/mods/MAPGEN/mcl_villages/init.lua @@ -0,0 +1,111 @@ +settlements = {} +settlements.modpath = minetest.get_modpath("mcl_villages") + +dofile(settlements.modpath.."/const.lua") +dofile(settlements.modpath.."/utils.lua") +dofile(settlements.modpath.."/foundation.lua") +dofile(settlements.modpath.."/buildings.lua") +dofile(settlements.modpath.."/paths.lua") +--dofile(settlements.modpath.."/convert_lua_mts.lua") +-- +-- load settlements on server +-- +settlements_in_world = settlements.load() +settlements.grundstellungen() + +--[[ Disable custom node spawning. +-- +-- register block for npc spawn +-- +minetest.register_node("settlements:junglewood", { + description = "special junglewood floor", + tiles = {"default_junglewood.png"}, + groups = {choppy=3, wood=2}, + sounds = default.node_sound_wood_defaults(), + }) + +--]] + + +--[[ Enable for testing, but use MineClone2's own spawn code if/when merging. +-- +-- register inhabitants +-- +if minetest.get_modpath("mobs_mc") ~= nil then + mobs:register_spawn("mobs_mc:villager", --name + {"mcl_core:stonebrickcarved"}, --nodes + 15, --max_light + 0, --min_light + 20, --chance + 7, --active_object_count + 31000, --max_height + nil) --day_toggle +end +--]] + +-- +-- on map generation, try to build a settlement +-- +local function build_a_settlement_no_delay(minp, maxp, blockseed) + local pr = PseudoRandom(blockseed) + + -- fill settlement_info with buildings and their data + local settlement_info = settlements.create_site_plan(maxp, minp, pr) + if not settlement_info then return end + + -- evaluate settlement_info and prepair terrain + settlements.terraform(settlement_info, pr) + + -- evaluate settlement_info and build paths between buildings + settlements.paths(settlement_info) + + -- evaluate settlement_info and place schematics + settlements.place_schematics(settlement_info, pr) + + -- evaluate settlement_info and initialize furnaces and chests + settlements.initialize_nodes(settlement_info, pr) +end + +local function ecb_build_a_settlement(blockpos, action, calls_remaining, param) + if calls_remaining <= 0 then + build_a_settlement_no_delay(param.minp, param.maxp, param.blockseed) + end +end + +-- Disable natural generation in singlenode. +local mg_name = minetest.get_mapgen_setting("mg_name") +if mg_name ~= "singlenode" then + minetest.register_on_generated(function(minp, maxp, blockseed) + -- needed for manual and automated settlement building + local heightmap = minetest.get_mapgen_object("heightmap") + + -- randomly try to build settlements + if blockseed % 77 ~= 17 then return end + + -- don't build settlement underground + if maxp.y < 0 then return end + + -- don't build settlements on (too) uneven terrain + local height_difference = settlements.evaluate_heightmap(minp, maxp) + if height_difference > max_height_difference then return end + + -- new way - slow :((((( + minetest.emerge_area(vector.subtract(minp,24), vector.add(maxp,24), ecb_build_a_settlement, {minp = vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed}) + -- old way - wait 3 seconds: + -- minetest.after(3, ecb_build_a_settlement, nil, 1, 0, {minp = vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed}) + end) +end +-- manually place villages +if minetest.is_creative_enabled("") then + minetest.register_craftitem("mcl_villages:tool", { + description = "mcl_villages build tool", + inventory_image = "default_tool_woodshovel.png", + -- build ssettlement + on_place = function(itemstack, placer, pointed_thing) + if not pointed_thing.under then return end + local minp = vector.subtract( pointed_thing.under, half_map_chunk_size) + local maxp = vector.add( pointed_thing.under, half_map_chunk_size) + build_a_settlement_no_delay(minp, maxp, math.random(0,32767)) + end + }) +end diff --git a/mods/MAPGEN/mcl_villages/paths.lua b/mods/MAPGEN/mcl_villages/paths.lua new file mode 100644 index 000000000..4973171a6 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/paths.lua @@ -0,0 +1,91 @@ +------------------------------------------------------------------------------- +-- generate paths between buildings +------------------------------------------------------------------------------- +function settlements.paths(settlement_info) + local starting_point + local end_point + local distance + --for k,v in pairs(settlement_info) do + starting_point = settlement_info[1]["pos"] + for o,p in pairs(settlement_info) do + + end_point = settlement_info[o]["pos"] + if starting_point ~= end_point + then + -- loop until end_point is reched (distance == 0) + while true do + + -- define surrounding pos to starting_point + local north_p = {x=starting_point.x+1, y=starting_point.y, z=starting_point.z} + local south_p = {x=starting_point.x-1, y=starting_point.y, z=starting_point.z} + local west_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z+1} + local east_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z-1} + -- measure distance to end_point + local dist_north_p_to_end = math.sqrt( + ((north_p.x - end_point.x)*(north_p.x - end_point.x))+ + ((north_p.z - end_point.z)*(north_p.z - end_point.z)) + ) + local dist_south_p_to_end = math.sqrt( + ((south_p.x - end_point.x)*(south_p.x - end_point.x))+ + ((south_p.z - end_point.z)*(south_p.z - end_point.z)) + ) + local dist_west_p_to_end = math.sqrt( + ((west_p.x - end_point.x)*(west_p.x - end_point.x))+ + ((west_p.z - end_point.z)*(west_p.z - end_point.z)) + ) + local dist_east_p_to_end = math.sqrt( + ((east_p.x - end_point.x)*(east_p.x - end_point.x))+ + ((east_p.z - end_point.z)*(east_p.z - end_point.z)) + ) + -- evaluate which pos is closer to the end_point + if dist_north_p_to_end <= dist_south_p_to_end and + dist_north_p_to_end <= dist_west_p_to_end and + dist_north_p_to_end <= dist_east_p_to_end + then + starting_point = north_p + distance = dist_north_p_to_end + + elseif dist_south_p_to_end <= dist_north_p_to_end and + dist_south_p_to_end <= dist_west_p_to_end and + dist_south_p_to_end <= dist_east_p_to_end + then + starting_point = south_p + distance = dist_south_p_to_end + + elseif dist_west_p_to_end <= dist_north_p_to_end and + dist_west_p_to_end <= dist_south_p_to_end and + dist_west_p_to_end <= dist_east_p_to_end + then + starting_point = west_p + distance = dist_west_p_to_end + + elseif dist_east_p_to_end <= dist_north_p_to_end and + dist_east_p_to_end <= dist_south_p_to_end and + dist_east_p_to_end <= dist_west_p_to_end + then + starting_point = east_p + distance = dist_east_p_to_end + end + -- find surface of new starting point + local surface_point, surface_mat = settlements.find_surface(starting_point) + -- replace surface node with mcl_core:grass_path + if surface_point + then + if surface_mat == "mcl_core:sand" or surface_mat == "mcl_core:redsand" then + minetest.swap_node(surface_point,{name="mcl_core:sandstonesmooth2"}) + else + minetest.swap_node(surface_point,{name="mcl_core:grass_path"}) + end + -- don't set y coordinate, surface might be too low or high + starting_point.x = surface_point.x + starting_point.z = surface_point.z + end + if distance <= 1 or + starting_point == end_point + then + break + end + end + end + end +end diff --git a/mods/MAPGEN/mcl_villages/schematics/blacksmith.mts b/mods/MAPGEN/mcl_villages/schematics/blacksmith.mts new file mode 100644 index 000000000..d7fb66593 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/blacksmith.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/butcher.mts b/mods/MAPGEN/mcl_villages/schematics/butcher.mts new file mode 100644 index 000000000..251033b1e Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/butcher.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/church.mts b/mods/MAPGEN/mcl_villages/schematics/church.mts new file mode 100644 index 000000000..dbf022cb4 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/church.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/farm.mts b/mods/MAPGEN/mcl_villages/schematics/farm.mts new file mode 100644 index 000000000..9094c8681 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/farm.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/lamp.mts b/mods/MAPGEN/mcl_villages/schematics/lamp.mts new file mode 100644 index 000000000..c8d907eba Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/lamp.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/large_house.mts b/mods/MAPGEN/mcl_villages/schematics/large_house.mts new file mode 100644 index 000000000..36be603f4 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/large_house.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/library.mts b/mods/MAPGEN/mcl_villages/schematics/library.mts new file mode 100644 index 000000000..b47e0b413 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/library.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/medium_house.mts b/mods/MAPGEN/mcl_villages/schematics/medium_house.mts new file mode 100644 index 000000000..43ce2391b Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/medium_house.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/small_house.mts b/mods/MAPGEN/mcl_villages/schematics/small_house.mts new file mode 100644 index 000000000..d7b62529c Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/small_house.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/tavern.mts b/mods/MAPGEN/mcl_villages/schematics/tavern.mts new file mode 100644 index 000000000..5eae8ae23 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/tavern.mts differ diff --git a/mods/MAPGEN/mcl_villages/schematics/well.mts b/mods/MAPGEN/mcl_villages/schematics/well.mts new file mode 100644 index 000000000..6ea47fea4 Binary files /dev/null and b/mods/MAPGEN/mcl_villages/schematics/well.mts differ diff --git a/mods/MAPGEN/mcl_villages/utils.lua b/mods/MAPGEN/mcl_villages/utils.lua new file mode 100644 index 000000000..96d540b57 --- /dev/null +++ b/mods/MAPGEN/mcl_villages/utils.lua @@ -0,0 +1,338 @@ +local c_dirt_with_grass = minetest.get_content_id("mcl_core:dirt_with_grass") +local c_dirt_with_snow = minetest.get_content_id("mcl_core:dirt_with_grass_snow") +--local c_dirt_with_dry_grass = minetest.get_content_id("mcl_core:dirt_with_dry_grass") +local c_podzol = minetest.get_content_id("mcl_core:podzol") +local c_sand = minetest.get_content_id("mcl_core:sand") +local c_desert_sand = minetest.get_content_id("mcl_core:redsand") +--local c_silver_sand = minetest.get_content_id("mcl_core:silver_sand") +-- +local c_air = minetest.get_content_id("air") +local c_snow = minetest.get_content_id("mcl_core:snow") +local c_fern_1 = minetest.get_content_id("mcl_flowers:fern") +local c_fern_2 = minetest.get_content_id("mcl_flowers:fern") +local c_fern_3 = minetest.get_content_id("mcl_flowers:fern") +local c_rose = minetest.get_content_id("mcl_flowers:poppy") +local c_viola = minetest.get_content_id("mcl_flowers:blue_orchid") +local c_geranium = minetest.get_content_id("mcl_flowers:allium") +local c_tulip = minetest.get_content_id("mcl_flowers:tulip_orange") +local c_dandelion_y = minetest.get_content_id("mcl_flowers:dandelion") +local c_dandelion_w = minetest.get_content_id("mcl_flowers:oxeye_daisy") +local c_bush_leaves = minetest.get_content_id("mcl_core:leaves") +local c_bush_stem = minetest.get_content_id("mcl_core:tree") +local c_a_bush_leaves = minetest.get_content_id("mcl_core:acacialeaves") +local c_a_bush_stem = minetest.get_content_id("mcl_core:acaciatree") +local c_water_source = minetest.get_content_id("mcl_core:water_source") +local c_water_flowing = minetest.get_content_id("mcl_core:water_flowing") +------------------------------------------------------------------------------- +-- function to copy tables +------------------------------------------------------------------------------- +function settlements.shallowCopy(original) + local copy = {} + for key, value in pairs(original) do + copy[key] = value + end + return copy +end +-- +-- +-- +function settlements.round(num, numDecimalPlaces) + local mult = 10^(numDecimalPlaces or 0) + return math.floor(num * mult + 0.5) / mult +end + +------------------------------------------------------------------------------- +-- function to find surface block y coordinate +-- returns surface postion +------------------------------------------------------------------------------- +function settlements.find_surface(pos) + local p6 = vector.new(pos) + local cnt = 0 + local itter -- count up or down + local cnt_max = 200 + -- check, in which direction to look for surface + local surface_node = minetest.get_node_or_nil(p6) + if surface_node and string.find(surface_node.name,"air") then + itter = -1 + else + itter = 1 + end + -- go through nodes an find surface + while cnt < cnt_max do + cnt = cnt+1 + minetest.forceload_block(p6) + surface_node = minetest.get_node_or_nil(p6) + + if not surface_node then + -- Load the map at pos and try again + minetest.get_voxel_manip():read_from_map(p6, p6) + surface_node = minetest.get_node(p6) + if surface_node.name == "ignore" then + settlements.debug("find_surface1: nil or ignore") + return nil + end + end + + -- if surface_node == nil or surface_node.name == "ignore" then + -- --return nil + -- local fl = minetest.forceload_block(p6) + -- if not fl then + -- + -- return nil + -- end + -- end + -- + -- Check Surface_node and Node above + -- + if settlements.surface_mat[surface_node.name] then + local surface_node_plus_1 = minetest.get_node_or_nil({ x=p6.x, y=p6.y+1, z=p6.z}) + if surface_node_plus_1 and surface_node and + (string.find(surface_node_plus_1.name,"air") or + string.find(surface_node_plus_1.name,"snow") or + string.find(surface_node_plus_1.name,"fern") or + string.find(surface_node_plus_1.name,"flower") or + string.find(surface_node_plus_1.name,"bush") or + string.find(surface_node_plus_1.name,"tree") or + string.find(surface_node_plus_1.name,"grass")) + then + settlements.debug("find_surface7: " ..surface_node.name.. " " .. surface_node_plus_1.name) + return p6, surface_node.name + else + settlements.debug("find_surface2: wrong surface+1") + end + else + settlements.debug("find_surface3: wrong surface "..surface_node.name.." at pos "..minetest.pos_to_string(p6)) + end + + p6.y = p6.y + itter + if p6.y < 0 then + settlements.debug("find_surface4: y<0") + return nil + end + end + settlements.debug("find_surface5: cnt_max overflow") + return nil +end +------------------------------------------------------------------------------- +-- check distance for new building +------------------------------------------------------------------------------- +function settlements.check_distance(settlement_info, building_pos, building_size) + local distance + for i, built_house in ipairs(settlement_info) do + distance = math.sqrt( + ((building_pos.x - built_house["pos"].x)*(building_pos.x - built_house["pos"].x))+ + ((building_pos.z - built_house["pos"].z)*(building_pos.z - built_house["pos"].z))) + if distance < building_size or distance < built_house["hsize"] then + return false + end + end + return true +end +------------------------------------------------------------------------------- +-- save list of generated settlements +------------------------------------------------------------------------------- +function settlements.save() + local file = io.open(minetest.get_worldpath().."/settlements.txt", "w") + if file then + file:write(minetest.serialize(settlements_in_world)) + file:close() + end +end +------------------------------------------------------------------------------- +-- load list of generated settlements +------------------------------------------------------------------------------- +function settlements.load() + local file = io.open(minetest.get_worldpath().."/settlements.txt", "r") + if file then + local table = minetest.deserialize(file:read("*all")) + if type(table) == "table" then + return table + end + end + return {} +end +------------------------------------------------------------------------------- +-- fill chests +------------------------------------------------------------------------------- +function settlements.fill_chest(pos, pr) + -- initialize chest (mts chests don't have meta) + local meta = minetest.get_meta(pos) + if meta:get_string("infotext") ~= "Chest" then + -- For MineClone2 0.70 or before + -- minetest.registered_nodes["mcl_chests:chest"].on_construct(pos) + -- + -- For MineClone2 after commit 09ab1482b5 (the new entity chests) + minetest.registered_nodes["mcl_chests:chest_small"].on_construct(pos) + end + -- fill chest + local inv = minetest.get_inventory( {type="node", pos=pos} ) + + local function get_treasures(pr) + local loottable = {{ + stacks_min = 3, + stacks_max = 8, + items = { + { itemstring = "mcl_core:diamond", weight = 3, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 }, + { itemstring = "mcl_core:gold_ingot", weight = 5, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_farming:bread", weight = 15, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_core:apple", weight = 15, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_tools:pick_iron", weight = 5 }, + { itemstring = "mcl_tools:sword_iron", weight = 5 }, + { itemstring = "mcl_armor:chestplate_iron", weight = 5 }, + { itemstring = "mcl_armor:helmet_iron", weight = 5 }, + { itemstring = "mcl_armor:leggings_iron", weight = 5 }, + { itemstring = "mcl_armor:boots_iron", weight = 5 }, + { itemstring = "mcl_core:obsidian", weight = 5, amount_min = 3, amount_max = 7 }, + { itemstring = "mcl_core:sapling", weight = 5, amount_min = 3, amount_max = 7 }, + { itemstring = "mcl_mobitems:saddle", weight = 3 }, + { itemstring = "mobs_mc:iron_horse_armor", weight = 1 }, + { itemstring = "mobs_mc:gold_horse_armor", weight = 1 }, + { itemstring = "mobs_mc:diamond_horse_armor", weight = 1 }, + } + }} + local items = mcl_loot.get_multi_loot(loottable, pr) + return items + end + + local items = get_treasures(pr) + mcl_loot.fill_inventory(inv, "main", items) +end + +------------------------------------------------------------------------------- +-- initialize furnace +------------------------------------------------------------------------------- +function settlements.initialize_furnace(pos) + -- find chests within radius + local furnacepos = minetest.find_node_near(pos, + 7, --radius + {"mcl_furnaces:furnace"}) + -- initialize furnacepos (mts furnacepos don't have meta) + if furnacepos + then + local meta = minetest.get_meta(furnacepos) + if meta:get_string("infotext") ~= "furnace" + then + minetest.registered_nodes["mcl_furnaces:furnace"].on_construct(furnacepos) + end + end +end +------------------------------------------------------------------------------- +-- initialize anvil +------------------------------------------------------------------------------- +function settlements.initialize_anvil(pos) + -- find chests within radius + local anvilpos = minetest.find_node_near(pos, + 7, --radius + {"mcl_anvils:anvil"}) + -- initialize anvilpos (mts anvilpos don't have meta) + if anvilpos + then + local meta = minetest.get_meta(anvilpos) + if meta:get_string("infotext") ~= "anvil" + then + minetest.registered_nodes["mcl_anvils:anvil"].on_construct(anvilpos) + end + end +end +------------------------------------------------------------------------------- +-- initialize furnace, chests, anvil +------------------------------------------------------------------------------- +local building_all_info +function settlements.initialize_nodes(settlement_info, pr) + for i, built_house in ipairs(settlement_info) do + for j, schem in ipairs(settlements.schematic_table) do + if settlement_info[i]["name"] == schem["name"] then + building_all_info = schem + break + end + end + + local width = building_all_info["hwidth"] + local depth = building_all_info["hdepth"] + local height = building_all_info["hheight"] + + local p = settlement_info[i]["pos"] + for yi = 1,height do + for xi = 0,width do + for zi = 0,depth do + local ptemp = {x=p.x+xi, y=p.y+yi, z=p.z+zi} + local node = minetest.get_node(ptemp) + if node.name == "mcl_furnaces:furnace" or + node.name == "mcl_chests:chest" or + node.name == "mcl_anvils:anvil" then + minetest.registered_nodes[node.name].on_construct(ptemp) + end + -- when chest is found -> fill with stuff + if node.name == "mcl_chests:chest" then + minetest.after(3, settlements.fill_chest, ptemp, pr) + end + end + end + end + end +end +------------------------------------------------------------------------------- +-- randomize table +------------------------------------------------------------------------------- +function shuffle(tbl, pr) + local table = settlements.shallowCopy(tbl) + local size = #table + for i = size, 1, -1 do + local rand = pr:next(1, size) + table[i], table[rand] = table[rand], table[i] + end + return table +end +------------------------------------------------------------------------------- +-- evaluate heightmap +------------------------------------------------------------------------------- +function settlements.evaluate_heightmap() + local heightmap = minetest.get_mapgen_object("heightmap") + -- max height and min height, initialize with impossible values for easier first time setting + local max_y = -50000 + local min_y = 50000 + -- only evaluate the center square of heightmap 40 x 40 + local square_start = 1621 + local square_end = 1661 + for j = 1 , 40, 1 do + for i = square_start, square_end, 1 do + -- skip buggy heightmaps, return high value + if heightmap[i] == -31000 or + heightmap[i] == 31000 + then + return max_height_difference + 1 + end + if heightmap[i] < min_y + then + min_y = heightmap[i] + end + if heightmap[i] > max_y + then + max_y = heightmap[i] + end + end + -- set next line + square_start = square_start + 80 + square_end = square_end + 80 + end + -- return the difference between highest and lowest pos in chunk + local height_diff = max_y - min_y + -- filter buggy heightmaps + if height_diff <= 1 + then + return max_height_difference + 1 + end + -- debug info + settlements.debug("heightdiff ".. height_diff) + return height_diff +end +------------------------------------------------------------------------------- +-- Set array to list +-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list +------------------------------------------------------------------------------- +function settlements.Set (list) + local set = {} + for _, l in ipairs(list) do set[l] = true end + return set +end diff --git a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua index 615abdc8b..00e2af687 100644 --- a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua +++ b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua @@ -35,7 +35,7 @@ else end end end - + -- TODO: Use minecart with chest instead of normal minecart tsm_railcorridors.carts = { "mcl_minecarts:minecart" } @@ -66,10 +66,10 @@ function tsm_railcorridors.get_treasures(pr) items = { { itemstring = "mcl_mobs:nametag", weight = 30 }, { itemstring = "mcl_core:apple_gold", weight = 20 }, - { itemstring = "mcl_books:book", weight = 10 }, -- TODO: Enchanted Book + { itemstack = mcl_enchanting.get_uniform_randomly_enchanted_book({"soul_speed"}), weight = 10 }, { itemstring = "", weight = 5}, { itemstring = "mcl_core:pick_iron", weight = 5 }, - { itemstring = "mcl_core:apple_gold", weight = 1 }, -- TODO: Enchanted Golden Apple + { itemstring = "mcl_core:apple_gold_enchanted", weight = 1 }, } }, { diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index 3cc0d75da..f2e02d997 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -15,7 +15,11 @@ end -- 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 +-- Extra check to prevent mod griefing in singlenode, mcimported worlds. +local mg_name = minetest.get_mapgen_setting("mg_name") +if mg_name == "singlenode" then + probability_railcaves_in_mapchunk = P(0) +elseif setting then probability_railcaves_in_mapchunk = P(setting) end diff --git a/mods/MISC/mcl_wip/init.lua b/mods/MISC/mcl_wip/init.lua index 211536bbf..22785d85a 100644 --- a/mods/MISC/mcl_wip/init.lua +++ b/mods/MISC/mcl_wip/init.lua @@ -53,7 +53,7 @@ for i=1,#wip_items do new_description = wip_items[i] end new_description = new_description .. "\n"..core.colorize("#FF0000", S("(WIP)")) - new_groups.not_in_craft_guide = 1 + --new_groups.not_in_craft_guide = 1 minetest.override_item(wip_items[i], { description = new_description, groups = new_groups }) end diff --git a/mods/PLAYER/mcl_hunger/hunger.lua b/mods/PLAYER/mcl_hunger/hunger.lua index 5a1e34ee3..393bdc9a9 100644 --- a/mods/PLAYER/mcl_hunger/hunger.lua +++ b/mods/PLAYER/mcl_hunger/hunger.lua @@ -156,7 +156,7 @@ function mcl_hunger.item_eat(hunger_change, replace_with_item, poisontime, poiso -- Special item definition field: _food_particles -- If false, force item to not spawn any food partiles when eaten if def._food_particles ~= false and texture and texture ~= "" then - local v = user:get_player_velocity() + local v = user:get_velocity() or user:get_player_velocity() local minvel = vector.add(v, {x=-1, y=1, z=-1}) local maxvel = vector.add(v, {x=1, y=2, z=1}) diff --git a/mods/PLAYER/mcl_player/init.lua b/mods/PLAYER/mcl_player/init.lua index d2dca49ca..28c12f251 100644 --- a/mods/PLAYER/mcl_player/init.lua +++ b/mods/PLAYER/mcl_player/init.lua @@ -59,6 +59,7 @@ function mcl_player.player_set_model(player, model_name) textures = player_textures[name] or model.textures, visual = "mesh", visual_size = model.visual_size or {x=1, y=1}, + damage_texture_modifier = "^[colorize:red:130", }) mcl_player.player_set_animation(player, "stand") else @@ -163,3 +164,37 @@ minetest.register_globalstep(function(dtime) end end end) + +-- Don't change HP if the player falls in the water or through End Portal: +minetest.register_on_player_hpchange(function(player, hp_change, reason) + if reason and reason.type == "fall" and player then + local pos = player:get_pos() + local node = minetest.get_node(pos) + local velocity = player:get_velocity() or player:get_player_velocity() or {x=0,y=-10,z=0} + local v_axis_max = math.max(math.abs(velocity.x), math.abs(velocity.y), math.abs(velocity.z)) + local step = {x = velocity.x / v_axis_max, y = velocity.y / v_axis_max, z = velocity.z / v_axis_max} + for i = 1, math.ceil(v_axis_max/5)+1 do -- trace at least 1/5 of the way per second + if not node or node.name == "ignore" then + minetest.get_voxel_manip():read_from_map(pos, pos) + node = minetest.get_node(pos) + end + if node then + if minetest.registered_nodes[node.name].walkable then + return hp_change + end + if minetest.get_item_group(node.name, "water") ~= 0 then + return 0 + end + if node.name == "mcl_portals:portal_end" then + if mcl_portals and mcl_portals.end_teleport then + mcl_portals.end_teleport(player) + end + return 0 + end + end + pos = vector.add(pos, step) + node = minetest.get_node(pos) + end + end + return hp_change +end, true) diff --git a/mods/PLAYER/mcl_spawn/init.lua b/mods/PLAYER/mcl_spawn/init.lua index ae23e122f..a3a461f7d 100644 --- a/mods/PLAYER/mcl_spawn/init.lua +++ b/mods/PLAYER/mcl_spawn/init.lua @@ -2,7 +2,50 @@ mcl_spawn = {} local S = minetest.get_translator("mcl_spawn") local mg_name = minetest.get_mapgen_setting("mg_name") +local storage = minetest.get_mod_storage() +-- Parameters +------------- + +local respawn_search_interval = 30 -- seconds +local respawn_search_initial_delay = 30 -- seconds +local trees_distance_max = 30 -- nodes +local attempts_to_find_pos = 50 +local attempts_to_find_trees = 50 +local node_groups_white_list = {"group:soil"} +local biomes_white_list = { + "ColdTaiga", + "Taiga", + "MegaTaiga", + "MegaSpruceTaiga", + "Plains", + "SunflowerPlains", + "Forest", + "FlowerForest", + "BirchForest", + "BirchForestM", + "Jungle", + "JungleM", + "JungleEdge", + "JungleEdgeM", + "Savanna", + "SavannaM", +} + +-- Resolution of search grid in nodes. +local res = 64 +local half_res = 32 -- for emerge areas around the position +local alt_min = -10 +local alt_max = 200 +-- Number of points checked in the square search grid (edge * edge). +local checks = 128 * 128 +-- Starting point for biome checks. This also sets the y co-ordinate for all +-- points checked, so the suitable biomes must be active at this y. +local start_pos = minetest.setting_get_pos("static_spawnpoint") or {x = 0, y = 8, z = 0} +-- Table of suitable biomes +local biome_ids = {} + +-- Bed spawning offsets local node_search_list = { --[[1]] {x = 0, y = 0, z = -1}, -- @@ -19,41 +62,273 @@ local node_search_list = --[[C]] {x = 0, y = 1, z = 1}, -- } -local cached_world_spawn +-- End of parameters +-------------------- + + +-- Initial variables + +local success = storage:get_int("mcl_spawn_success")==1 +local searched = (storage:get_int("mcl_spawn_searched")==1) or mg_name == "v6" or mg_name == "singlenode" or minetest.settings:get("static_spawnpoint") +local wsp = minetest.string_to_pos(storage:get_string("mcl_spawn_world_spawn_point")) or {} -- world spawn position +local check = storage:get_int("mcl_spawn_check") or 0 +local cp = minetest.string_to_pos(storage:get_string("mcl_spawn_cp")) or {x=start_pos.x, y=start_pos.y, z=start_pos.z} +local edge_len = storage:get_int("mcl_spawn_edge_len") or 1 +local edge_dist = storage:get_int("mcl_spawn_edge_dist") or 0 +local dir_step = storage:get_int("mcl_spawn_dir_step") or 0 +local dir_ind = storage:get_int("mcl_spawn_dir_ind") or 1 +local emerge_pos1, emerge_pos2 + +-- Get world 'mapgen_limit' and 'chunksize' to calculate 'spawn_limit'. +-- This accounts for how mapchunks are not generated if they or their shell exceed +-- 'mapgen_limit'. + +local mapgen_limit = tonumber(minetest.get_mapgen_setting("mapgen_limit")) +local chunksize = tonumber(minetest.get_mapgen_setting("chunksize")) +local spawn_limit = math.max(mapgen_limit - (chunksize + 1) * 16, 0) + + +--Functions +----------- + +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 + +local function get_trees(pos, pos2) + if emerge_pos1 and emerge_pos2 and pos then + if not pos2 then + local b1 = {x = math.max(pos.x-trees_distance_max, emerge_pos1.x), y = math.max(pos.y-trees_distance_max, emerge_pos1.y), z = math.max(pos.z-trees_distance_max, emerge_pos1.z)} + local b2 = {x = math.min(pos.x+trees_distance_max, emerge_pos2.x), y = math.min(pos.y+trees_distance_max, emerge_pos2.y), z = math.min(pos.z+trees_distance_max, emerge_pos2.z)} + return minetest.find_nodes_in_area(b1, b2, {"group:tree"}, false) + else + local b1 = {x = math.max(pos.x , emerge_pos1.x), y = math.max(pos.y , emerge_pos1.y), z = math.max(pos.z, emerge_pos1.z)} + local b2 = {x = math.min(pos2.x, emerge_pos2.x), y = math.min(pos2.y, emerge_pos2.y), z = math.min(pos2.z, emerge_pos2.z)} + return minetest.find_nodes_in_area(b1, b2, {"group:tree"}, false) + end + end + return nil +end + +local function good_for_respawn(pos, player) + local pos0 = {x = pos.x, y = pos.y - 1, z = pos.z} + local pos1 = {x = pos.x, y = pos.y, z = pos.z} + local pos2 = {x = pos.x, y = pos.y + 1, z = pos.z} + local node0 = get_far_node(pos0) + local node1 = get_far_node(pos1) + local node2 = get_far_node(pos2) + + local nn0, nn1, nn2 = node0.name, node1.name, node2.name + if minetest.get_item_group(nn0, "destroys_items") ~=0 + or minetest.get_item_group(nn1, "destroys_items") ~=0 + or minetest.get_item_group(nn2, "destroys_items") ~=0 + or minetest.get_item_group(nn0, "portal") ~=0 + or minetest.get_item_group(nn1, "portal") ~=0 + or minetest.get_item_group(nn2, "portal") ~=0 + or minetest.is_protected(pos0, player or "") + or minetest.is_protected(pos1, player or "") + or minetest.is_protected(pos2, player or "") + or (not player and minetest.get_node_light(pos1, 0.5) < 8) + or (not player and minetest.get_node_light(pos2, 0.5) < 8) + or nn0 == "ignore" + or nn1 == "ignore" + or nn2 == "ignore" + then + return false + end + + local def0 = minetest.registered_nodes[nn0] + local def1 = minetest.registered_nodes[nn1] + local def2 = minetest.registered_nodes[nn2] + return def0.walkable and (not def1.walkable) and (not def2.walkable) and + (def1.damage_per_second == nil or def2.damage_per_second <= 0) and + (def1.damage_per_second == nil or def2.damage_per_second <= 0) +end + +local function can_find_tree(pos1, trees) + if not emerge_pos1 or not emerge_pos2 then return false end + local trees = trees or get_trees(pos1) + if not trees then return false end + + if (attempts_to_find_trees * 3 < #trees) then + -- random search + for i = 1, attempts_to_find_trees do + local pos2 = trees[math.random(1,#trees)] + if not minetest.is_protected(pos2, "") then + if pos2.x < pos1.x then + pos2.x = pos2.x + 1 + elseif pos2.x > pos1.x then + pos2.x = pos2.x - 1 + end + if pos2.z < pos1.z then + pos2.z = pos2.z + 1 + elseif pos2.z > pos1.z then + pos2.z = pos2.z - 1 + end + local way = minetest.find_path(pos1, pos2, res, 1, 3, "A*_noprefetch") + if way then + return true + end + end + end + return false + end + + for i, pos2 in ipairs(trees) do + -- full search + if not minetest.is_protected(pos2, "") then + if pos2.x < pos1.x then + pos2.x = pos2.x + 1 + elseif pos2.x > pos1.x then + pos2.x = pos2.x - 1 + end + if pos2.z < pos1.z then + pos2.z = pos2.z + 1 + elseif pos2.z > pos1.z then + pos2.z = pos2.z - 1 + end + local way = minetest.find_path(pos1, pos2, res, 1, 3, "A*_noprefetch") + if way then + return true + end + end + if i > attempts_to_find_trees then return false end + end + return false +end + +local function next_pos() + if edge_dist >= edge_len then + edge_dist = 1 + dir_ind = (dir_ind % 4) + 1 + dir_step = dir_step + 1 + edge_len = math.floor(dir_step / 2) + 1 + else + edge_dist = edge_dist + 1 + end + if dir_ind==1 then + cp.z = cp.z + res + elseif dir_ind==2 then + cp.x = cp.x - res + elseif dir_ind==3 then + cp.z = cp.z - res + else + cp.x = cp.x + res + end +end + +-- Spawn position search + +local function next_biome() + while check <= checks do + local biome_data = minetest.get_biome_data(cp) + -- Sometimes biome_data is nil + local biome = biome_data and biome_data.biome + if biome then + minetest.log("verbose", "[mcl_spawn] Search white-listed biome at "..minetest.pos_to_string(cp)..": "..minetest.get_biome_name(biome)) + for _, biome_id in ipairs(biome_ids) do + if biome == biome_id then + cp.y = minetest.get_spawn_level(cp.x, cp.z) or start_pos.y + if cp.y then + wsp = {x = cp.x, y = cp.y, z = cp.z} + return true + end + break + end + end + end + + next_pos() + + -- Check for position being outside world edge + if math.abs(cp.x) > spawn_limit or math.abs(cp.z) > spawn_limit then + check = checks + 1 + return false + end + + check = check + 1 + end + + return false +end + +local function ecb_search_continue(blockpos, action, calls_remaining, param) + if calls_remaining <= 0 then + emerge_pos1 = {x = wsp.x-half_res, y = alt_min, z = wsp.z-half_res} + emerge_pos2 = {x = wsp.x+half_res, y = alt_max, z = wsp.z+half_res} + local nodes = minetest.find_nodes_in_area_under_air(emerge_pos1, emerge_pos2, node_groups_white_list) + minetest.log("verbose", "[mcl_spawn] Data emerge callback: "..minetest.pos_to_string(wsp).." - "..tostring(nodes and #nodes) .. " node(s) found under air") + if nodes then + local trees = get_trees(emerge_pos1, emerge_pos2) + if trees then + if attempts_to_find_pos * 3 < #nodes then + -- random + for i=1, attempts_to_find_pos do + wsp = nodes[math.random(1,#nodes)] + if wsp then + wsp.y = wsp.y + 1 + if good_for_respawn(wsp) and can_find_tree(wsp, trees) then + minetest.log("action", "[mcl_spawn] Dynamic world spawn determined to be "..minetest.pos_to_string(wsp)) + searched = true + success = true + return + end + end + end + else + -- in a sequence + for i=1, math.min(#nodes, attempts_to_find_pos) do + wsp = nodes[i] + if wsp then + wsp.y = wsp.y + 1 + if good_for_respawn(wsp) and can_find_tree(wsp, trees) then + minetest.log("action", "[mcl_spawn] Dynamic world spawn determined to be "..minetest.pos_to_string(wsp)) + searched = true + success = true + return + end + end + end + end + end + end + next_pos() + mcl_spawn.search() + end +end + +function mcl_spawn.search() + if not next_biome() or check > checks then + return false + end + check = check + 1 + if not wsp.y then + wsp.y = 8 + end + local pos1 = {x = wsp.x-half_res, y = alt_min, z = wsp.z-half_res} + local pos2 = {x = wsp.x+half_res, y = alt_max, z = wsp.z+half_res} + minetest.emerge_area(pos1, pos2, ecb_search_continue) +end + mcl_spawn.get_world_spawn_pos = function() - local spawn - spawn = minetest.setting_get_pos("static_spawnpoint") - if spawn then - return spawn - end - if cached_world_spawn then - return cached_world_spawn - end - -- 32 attempts to find a suitable spawn point - spawn = { x=math.random(-16, 16), y=8, z=math.random(-16, 16) } - for i=1, 32 do - local y = minetest.get_spawn_level(spawn.x, spawn.z) - if y then - spawn.y = y - cached_world_spawn = spawn - minetest.log("action", "[mcl_spawn] Dynamic world spawn determined to be "..minetest.pos_to_string(spawn)) - return spawn - end - -- Random walk - spawn.x = spawn.x + math.random(-64, 64) - spawn.z = spawn.z + math.random(-64, 64) + if success then + return wsp end minetest.log("action", "[mcl_spawn] Failed to determine dynamic world spawn!") - -- Use dummy position if nothing found - return { x=math.random(-16, 16), y=8, z=math.random(-16, 16) } + return start_pos end -- Returns a spawn position of player. -- If player is nil or not a player, a world spawn point is returned. -- The second return value is true if returned spawn point is player-chosen, -- false otherwise. -mcl_spawn.get_spawn_pos = function(player) +mcl_spawn.get_bed_spawn_pos = function(player) local spawn, custom_spawn = nil, false if player ~= nil and player:is_player() then local attr = player:get_meta():get_string("mcl_beds:spawn") @@ -101,30 +376,8 @@ 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 - -local function good_for_respawn(pos) - local node0 = get_far_node({x = pos.x, y = pos.y - 1, z = pos.z}) - local node1 = get_far_node({x = pos.x, y = pos.y, z = pos.z}) - local node2 = get_far_node({x = pos.x, y = pos.y + 1, z = pos.z}) - local def0 = minetest.registered_nodes[node0.name] - local def1 = minetest.registered_nodes[node1.name] - local def2 = minetest.registered_nodes[node2.name] - return def0.walkable and (not def1.walkable) and (not def2.walkable) and - (def1.damage_per_second == nil or def2.damage_per_second <= 0) and - (def1.damage_per_second == nil or def2.damage_per_second <= 0) -end - --- Respawn player at specified respawn position -minetest.register_on_respawnplayer(function(player) - local pos, custom_spawn = mcl_spawn.get_spawn_pos(player) +mcl_spawn.get_player_spawn_pos = function(player) + local pos, custom_spawn = mcl_spawn.get_bed_spawn_pos(player) if pos and custom_spawn then -- Check if bed is still there local node_bed = get_far_node(pos) @@ -135,7 +388,7 @@ minetest.register_on_respawnplayer(function(player) player:get_meta():set_string("mcl_beds:spawn", "") end minetest.chat_send_player(player:get_player_name(), S("Your spawn bed was missing or blocked.")) - return false + return mcl_spawn.get_world_spawn_pos(), false end -- Find spawning position on/near the bed free of solid or damaging blocks iterating a square spiral 15x15: @@ -152,15 +405,59 @@ minetest.register_on_respawnplayer(function(player) else -- dir.x == 1 offset = {x = -o.z, y = o.y, z = o.x} end - local spawn_pos = vector.add(pos, offset) - if good_for_respawn(spawn_pos) then - player:set_pos(spawn_pos) - return true + local player_spawn_pos = vector.add(pos, offset) + if good_for_respawn(player_spawn_pos, player:get_player_name()) then + return player_spawn_pos, true end end - - -- We here if we didn't find suitable place for respawn: - return false + -- We here if we didn't find suitable place for respawn end -end) + return mcl_spawn.get_world_spawn_pos(), false +end +mcl_spawn.spawn = function(player) + local pos, in_bed = mcl_spawn.get_player_spawn_pos(player) + player:set_pos(pos) + return in_bed or success +end + +-- Respawn player at specified respawn position +minetest.register_on_respawnplayer(mcl_spawn.spawn) + +function mcl_spawn.shadow_worker() + if #biome_ids > 1 then + for _, biome_name in pairs(biomes_white_list) do + table.insert(biome_ids, minetest.get_biome_id(biome_name)) + end + end + if not searched then + searched = true + mcl_spawn.search() + minetest.log("action", "[mcl_spawn] Started world spawn point search") + end + if success and ((not good_for_respawn(wsp)) or (not can_find_tree(wsp))) then + success = false + minetest.log("action", "[mcl_spawn] World spawn position isn't safe anymore: "..minetest.pos_to_string(wsp)) + mcl_spawn.search() + end + + minetest.after(respawn_search_interval, mcl_spawn.shadow_worker) +end + +minetest.after(respawn_search_initial_delay, function() + mcl_spawn.shadow_worker() + + minetest.register_on_shutdown(function() + storage:set_int("mcl_spawn_success", success and 1 or 0) + if wsp and wsp.x then + storage:set_string("mcl_spawn_world_spawn_point", minetest.pos_to_string(wsp)) + end + storage:set_int("mcl_spawn_searched", searched and 1 or 0) + storage:set_int("mcl_spawn_check", check) + storage:set_string("mcl_spawn_cp", minetest.pos_to_string(cp)) + storage:set_int("mcl_spawn_edge_len", edge_len) + storage:set_int("mcl_spawn_edge_dist", edge_dist) + storage:set_int("mcl_spawn_dir_step", dir_step) + storage:set_int("mcl_spawn_dir_ind", dir_ind) + end) +end) diff --git a/settingtypes.txt b/settingtypes.txt index 18471d7bf..394749fd7 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -85,6 +85,15 @@ mobs_disable_blood (Disable mob damage particles) bool false # Enable flame sound. flame_sound (Flame sound) bool true +[Graphics] +# How many vertical animation frames the fire texture (fire_basic_flame_animated.png) has. +# This may vary depending on the texture pack you use. +# Form: Image height / Image width +fire_animation_frames (Fire Animation Frames) int 8 + +# Whether to animate chests when open / close +animated_chests (Animated chests) bool true + [Experimental] # Whether ice is translucent. If disabled, ice is fully opaque. # @@ -120,3 +129,9 @@ mcl_superflat_classic (Classic superflat map generation) bool false # WARNING: This setting has quite poor performance and can slow down your # game by a lot. mcl_node_particles (Block particles detail level) enum none high,medium,low,none + +# If enabled, this will substitute a few blocks in village schematics so they blend into normal, snowy, and sandy areas. Defaults to true. +basic_pseudobiome_villages (Enables very basic, and experimental "pseudobiome-based" villages) bool true + +# If enabled, will run an LBM to fix the top 1/2 of double plants in mcimported worlds; defaults to true. +fix_doubleplants (Mcimport double plant fixes) bool true diff --git a/tools/Conversion_Table.csv b/tools/Conversion_Table.csv index 0cad45eaf..426712f5b 100644 --- a/tools/Conversion_Table.csv +++ b/tools/Conversion_Table.csv @@ -331,6 +331,8 @@ Source path,Source file,Target path,Target file,xs,ys,xl,yl,xt,yt,Blacklisted? /assets/minecraft/textures/blocks,endframe_side.png,/mods/ITEMS/mcl_end/textures,mcl_end_endframe_side.png,,,,,,, /assets/minecraft/textures/blocks,endframe_top.png,/mods/ITEMS/mcl_end/textures,mcl_end_endframe_top.png,,,,,,, /assets/minecraft/textures/blocks,end_stone.png,/mods/ITEMS/mcl_end/textures,mcl_end_end_stone.png,,,,,,, +/assets/minecraft/textures/items,end_crystal.png,/mods/ITEMS/mcl_end/textures,mcl_end_crystal_item.png,,,,,,, +/assets/minecraft/textures/entity/endercrystal,endercrystal.png,/mods/ITEMS/mcl_end/textures,mcl_end_crystal.png,,,,,,, /assets/minecraft/textures/blocks,purpur_block.png,/mods/ITEMS/mcl_end/textures,mcl_end_purpur_block.png,,,,,,, /assets/minecraft/textures/blocks,purpur_pillar.png,/mods/ITEMS/mcl_end/textures,mcl_end_purpur_pillar.png,,,,,,, /assets/minecraft/textures/blocks,purpur_pillar_top.png,/mods/ITEMS/mcl_end/textures,mcl_end_purpur_pillar_top.png,,,,,,, @@ -386,6 +388,8 @@ Source path,Source file,Target path,Target file,xs,ys,xl,yl,xt,yt,Blacklisted? /assets/minecraft/textures/blocks,wheat_stage_6.png,/mods/ITEMS/mcl_farming/textures,mcl_farming_wheat_stage_6.png,,,,,,, /assets/minecraft/textures/blocks,wheat_stage_7.png,/mods/ITEMS/mcl_farming/textures,mcl_farming_wheat_stage_7.png,,,,,,, /assets/minecraft/textures/blocks,fire_layer_0.png,/mods/ITEMS/mcl_fire/textures,fire_basic_flame_animated.png,,,,,,, +/assets/minecraft/textures/blocks,fire_layer_0.png,/mods/ITEMS/mcl_fire/textures,mcl_burning_entity_flame_animated.png,,,,,,, +/assets/minecraft/textures/blocks,fire_layer_0.png,/mods/ITEMS/mcl_fire/textures,mcl_burning_hud_flame_animated.png,,,,,,, /assets/minecraft/textures/blocks,fire_layer_0.png,/mods/ITEMS/mcl_fire/textures,fire_basic_flame.png,0,0,16,16,0,0,y /assets/minecraft/textures/items,fireball.png,/mods/ITEMS/mcl_fire/textures,mcl_fire_fire_charge.png,,,,,,, /assets/minecraft/textures/items,flint_and_steel.png,/mods/ITEMS/mcl_fire/textures,mcl_fire_flint_and_steel.png,,,,,,, @@ -530,9 +534,11 @@ Source path,Source file,Target path,Target file,xs,ys,xl,yl,xt,yt,Blacklisted? /assets/minecraft/textures/blocks,sea_lantern.png,/mods/ITEMS/mcl_ocean/textures,mcl_ocean_sea_lantern.png,,,,,,, /assets/minecraft/textures/items,dragon_breath.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_dragon_breath.png,,,,,,, /assets/minecraft/textures/items,melon_speckled.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_melon_speckled.png,,,,,,, -/assets/minecraft/textures/items,potion_bottle_drinkable.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_potion_bottle_drinkable.png,,,,,,, -/assets/minecraft/textures/items,potion_bottle_empty.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_potion_bottle_empty.png,,,,,,, +/assets/minecraft/textures/items,potion_bottle_empty.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_potion_bottle.png,,,,,,, +/assets/minecraft/textures/items,potion_bottle_splash.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_splash_bottle.png,,,,,,, +/assets/minecraft/textures/items,potion_bottle_lingering.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_lingering_bottle.png,,,,,,, /assets/minecraft/textures/items,potion_overlay.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_potion_overlay.png,,,,,,, +/assets/minecraft/textures/items,potion_overlay.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_splash_overlay.png,,,,,,, /assets/minecraft/textures/items,spider_eye_fermented.png,/mods/ITEMS/mcl_potions/textures,mcl_potions_spider_eye_fermented.png,,,,,,, /assets/minecraft/textures/blocks,sponge.png,/mods/ITEMS/mcl_sponges/textures,mcl_sponges_sponge.png,,,,,,, /assets/minecraft/textures/blocks,sponge_wet.png,/mods/ITEMS/mcl_sponges/textures,mcl_sponges_sponge_wet.png,,,,,,, @@ -947,9 +953,11 @@ Source path,Source file,Target path,Target file,xs,ys,xl,yl,xt,yt,Blacklisted? /assets/minecraft/textures/blocks,portal.png,/mods/ITEMS/mcl_portals/textures,mcl_portals_portal.png,,,,,,, /assets/minecraft/textures/entity,end_portal.png,/mods/ITEMS/mcl_portals/textures,mcl_portals_end_portal.png,,,,,,, /assets/minecraft/textures/environment,end_sky.png,/mods/PLAYER/mcl_playerplus/textures,mcl_playerplus_end_sky.png,,,,,,, -/assets/minecraft/textures/entity/chest,normal.png,/mods/ITEMS/mcl_chests/textures,mcl_chests_normal.png,,,,,,,y -/assets/minecraft/textures/entity/chest,normal_double.png,/mods/ITEMS/mcl_chests/textures,mcl_chests_normal_double.png,,,,,,,y -/assets/minecraft/textures/entity/chest,ender.png,/mods/ITEMS/mcl_chests/textures,mcl_chests_ender.png,,,,,,,y +/assets/minecraft/textures/entity/chest,normal.png,/mods/ITEMS/mcl_chests/textures,mcl_chests_normal.png,,,,,,, +/assets/minecraft/textures/entity/chest,normal_double.png,/mods/ITEMS/mcl_chests/textures,mcl_chests_normal_double.png,,,,,,, +/assets/minecraft/textures/entity/chest,trapped.png,/mods/ITEMS/mcl_chests/textures,mcl_chests_trapped.png,,,,,,, +/assets/minecraft/textures/entity/chest,trapped_double.png,/mods/ITEMS/mcl_chests/textures,mcl_chests_trapped_double.png,,,,,,, +/assets/minecraft/textures/entity/chest,ender.png,/mods/ITEMS/mcl_chests/textures,mcl_chests_ender.png,,,,,,, /assets/minecraft/textures/blocks,endframe_top.png,/mods/ITEMS/mcl_portals/textures,mcl_portals_endframe_top.png,,,,,,, /assets/minecraft/textures/blocks,endframe_side.png,/mods/ITEMS/mcl_portals/textures,mcl_portals_endframe_side.png,,,,,,, /assets/minecraft/textures/blocks,endframe_eye.png,/mods/ITEMS/mcl_portals/textures,mcl_portals_endframe_eye.png,,,,,,, @@ -964,3 +972,5 @@ Source path,Source file,Target path,Target file,xs,ys,xl,yl,xt,yt,Blacklisted? /assets/minecraft/textures/blocks,cobblestone_mossy.png,/mods/ITEMS/mcl_walls/textures,mcl_walls_cobble_mossy_wall_side.png,,,,,,, /assets/minecraft/textures/blocks,grass_top.png,/mods/ITEMS/mcl_core/textures,mcl_core_grass_block_top.png,,,,,,, /assets/minecraft/textures/blocks,grass_side_overlay.png,/mods/ITEMS/mcl_core/textures,mcl_core_grass_block_side_overlay.png,,,,,,, +/assets/minecraft/textures/items,book_enchanted.png,/mods/ITEMS/mcl_enchanting/textures,mcl_enchanting_book_enchanted.png,,,,,,, +/assets/minecraft/textures/items,experience_bottle.png,/mods/HUD/mcl_experience/textures,mcl_experience_bottle.png,,,,,,, diff --git a/tools/Texture_Converter.py b/tools/Texture_Converter.py index 9fa90365b..c23bc9fce 100755 --- a/tools/Texture_Converter.py +++ b/tools/Texture_Converter.py @@ -196,6 +196,30 @@ def convert_textures(): if verbose: print(src_file + " → " + dst_file) + # Convert armor textures (requires ImageMagick) + armor_files = [ + [ tex_dir + "/models/armor/leather_layer_1.png", tex_dir + "/models/armor/leather_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures"), "mcl_armor_helmet_leather.png", "mcl_armor_chestplate_leather.png", "mcl_armor_leggings_leather.png", "mcl_armor_boots_leather.png" ], + [ tex_dir + "/models/armor/chainmail_layer_1.png", tex_dir + "/models/armor/chainmail_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures"), "mcl_armor_helmet_chain.png", "mcl_armor_chestplate_chain.png", "mcl_armor_leggings_chain.png", "mcl_armor_boots_chain.png" ], + [ tex_dir + "/models/armor/gold_layer_1.png", tex_dir + "/models/armor/gold_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures"), "mcl_armor_helmet_gold.png", "mcl_armor_chestplate_gold.png", "mcl_armor_leggings_gold.png", "mcl_armor_boots_gold.png" ], + [ tex_dir + "/models/armor/iron_layer_1.png", tex_dir + "/models/armor/iron_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures"), "mcl_armor_helmet_iron.png", "mcl_armor_chestplate_iron.png", "mcl_armor_leggings_iron.png", "mcl_armor_boots_iron.png" ], + [ tex_dir + "/models/armor/diamond_layer_1.png", tex_dir + "/models/armor/diamond_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures"), "mcl_armor_helmet_diamond.png", "mcl_armor_chestplate_diamond.png", "mcl_armor_leggings_diamond.png", "mcl_armor_boots_diamond.png" ] + ] + for a in armor_files: + APXSIZE = 16 # for some reason MineClone2 requires this + layer_1 = a[0] + layer_2 = a[1] + adir = a[2] + if os.path.isfile(layer_1): + helmet = adir + "/" + a[3] + chestplate = adir + "/" + a[4] + boots = adir + "/" + a[6] + os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +"+str(APXSIZE * 2)+"+0 -crop "+str(APXSIZE * 2)+"x"+str(APXSIZE)+"+0+0 \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+helmet) + os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +"+str(APXSIZE)+"+"+str(APXSIZE)+" -crop "+str(APXSIZE * 2.5)+"x"+str(APXSIZE)+"+"+str(APXSIZE)+"+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+chestplate) + os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +0+"+str(APXSIZE)+" -crop "+str(APXSIZE)+"x"+str(APXSIZE)+"+0+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+boots) + if os.path.isfile(layer_2): + leggings = adir + "/" + a[5] + os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_2+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +0+"+str(APXSIZE)+" -crop "+str(APXSIZE * 2.5)+"x"+str(APXSIZE)+"+0+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+leggings) + # Convert chest textures (requires ImageMagick) chest_files = [ [ tex_dir + "/entity/chest/normal.png", target_dir("/mods/ITEMS/mcl_chests/textures"), "default_chest_top.png", "mcl_chests_chest_bottom.png", "default_chest_front.png", "mcl_chests_chest_left.png", "mcl_chests_chest_right.png", "mcl_chests_chest_back.png" ], @@ -352,7 +376,7 @@ def convert_textures(): FOLIAG = tex_dir+"/colormap/foliage.png" GRASS = tex_dir+"/colormap/grass.png" - + # Leaves colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_oak.png", "116+143", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_leaves.png") colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_big_oak.png", "158+177", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/mcl_core_leaves_big_oak.png")