1
0
Fork 0

Merge pull request 'master' (#5) from MineClone2/MineClone2:master into master

Reviewed-on: https://git.minetest.land/epCode/MineClone2/pulls/5
objects
epCode 2021-02-02 22:19:58 +00:00
commit e0ad09900c
165 changed files with 5047 additions and 2164 deletions

View File

@ -7,45 +7,45 @@ But first, some things to note:
MineClone 2's development target is to make a free software clone of Minecraft, MineClone 2's development target is to make a free software clone of Minecraft,
***version 1.11***, ***PC edition***. ***version 1.11***, ***PC edition***.
MineClone 2 is maintained by one person. Namely, Wuzzy. You can find me, MineClone 2 is maintained by two persons. Namely, kay27 and EliasFleckenstein. You can find us
Wuzzy, in the Minetest forums (forums.minetest.net), in IRC in the #minetest 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 channel on irc.freenode.net. And finally, you can send e-mails to
<Wuzzy2@mail.ru>. <eliasfleckenstein@web.de> or <kay27@bk.ru>.
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 you agree that they fall under the terms of the LGPLv2.1, which basically
means they will become part of a free software. means they will become part of a free software.
## The suggested workflow ## The suggested workflow
I don't **dictate** your workflow, but in order to work with me in an efficient We don't **dictate** your workflow, but in order to work with us in an efficient
way, you can follow my suggestions. way, you can follow these suggestions:
For small and medium changes: For small and medium changes:
* Fork the repository * Fork the repository
* Do your change in a new branch * Do your change in a new branch
* Upload the repository somewhere where it can be accessed from the Internet and * 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. ;-) 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 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 need to show your contributions align with the project goals. We still
reserve the right to revert everything that I don't like. reserve the right to revert everything that we don't like.
For bigger changes, I strongly recommend to use feature branches and For bigger changes, we strongly recommend to use feature branches and
discuss with me first. discuss with me first.
Contributors will be credited in `README.md`. Contributors will be credited in `README.md`.
## Quality remarks ## Quality remarks
Again: There is ***no*** guarantee I will accept anything from anybody. Again: There is ***no*** guarantee we will accept anything from anybody.
But I will gladly take in code from others when I feel it saves me work But we will gladly take in code from others when we feel it saves us work
in the long run. in the long run.
### Inclusion criteria ### Inclusion criteria
@ -79,9 +79,18 @@ Depending on what you add, the chances for inclusion vary:
## Reporting bugs ## Reporting bugs
Report all bugs and missing Minecraft features here: Report all bugs and missing Minecraft features here:
<https://git.minetest.land/Wuzzy/MineClone2/issues> <https://git.minetest.land/MineClone2/MineClone2/issues>
## Direct discussion ## Direct discussion
We have an IRC channel! Join us on #mineclone2 in freenode.net. We have an IRC channel! Join us on #mineclone2 in freenode.net.
<ircs://irc.freenode.net:6697/#mineclone2> <ircs://irc.freenode.net:6697/#mineclone2>
## Creating releases
* Launch MineClone2 to make sure it still runs
* Update the version number in README.md
* Use `git tag <version number>` 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

View File

@ -70,6 +70,7 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
* `coral_fan=X`: Coral fan (1 = alive, 2 = dead) * `coral_fan=X`: Coral fan (1 = alive, 2 = dead)
* `coral_block=X`: Coral block (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 * `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 #### Footnotes
@ -96,6 +97,8 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
* `carpet=1:` (Wool) carpet * `carpet=1:` (Wool) carpet
* `stick=1`: Stick * `stick=1`: Stick
* `water_bucket=1`: Bucket containing a liquid of group “water” * `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 ### Material groups
@ -197,6 +200,7 @@ These groups are used mostly for informational purposes
* `building_block=1`: Block is a building block * `building_block=1`: Block is a building block
* `deco_block=1`: Block is a decorational block * `deco_block=1`: Block is a decorational block
## Fake item groups ## Fake item groups
These groups put similar items together which should all be treated by the gameplay or the GUI as a single item. 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. You should not add custom items to these groups for no good reason, this is likely to cause a ton of conflicts.

View File

@ -20,7 +20,6 @@ For these features, no easy Lua workaround could be found.
## Interface ## 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 - 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+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 - 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 ## 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 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 - 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 - 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 #### 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 - 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

View File

@ -1,8 +1,8 @@
# MineClone 2 # MineClone 2
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils. 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 ### Gameplay
You start in a randomly-generated world made entirely of cubes. You can explore 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 #### Gameplay summary
* Sandbox-style gameplay, no goals (for now) * Sandbox-style gameplay, no goals
* Survive: Fight against hostile monsters and hunger * Survive: Fight against hostile monsters and hunger
* Mine for ores and other treasures * 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 * Use the collected blocks to create great buildings, your imagination is the limit
* Collect flowers (and other dye sources) and colorize your world * Collect flowers (and other dye sources) and colorize your world
* Find some seeds and start farming * 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: The following main features are available:
* Tools, weapons * Tools, weapons
* Armor (unbalanced) * Armor
* Crafting system: 2×2 grid, crafting table (3×3 grid), furnace, including a crafting guide * Crafting system: 2×2 grid, crafting table (3×3 grid), furnace, including a crafting guide
* Chests, large chests, ender chests, shulker boxes * Chests, large chests, ender chests, shulker boxes
* Furnaces, hoppers * Furnaces, hoppers
@ -117,6 +118,8 @@ The following main features are available:
* Redstone circuits (partially) * Redstone circuits (partially)
* Minecarts (partial) * Minecarts (partial)
* Status effects (partial) * Status effects (partial)
* Experience
* Enchanting
* Brewing, potions, tipped arrow (partial) * Brewing, potions, tipped arrow (partial)
* Boats * Boats
* Fire * Fire
@ -142,12 +145,9 @@ The following main features are available:
The following features are incomplete: The following features are incomplete:
* Generated structures (especially villages) * Generated structures (especially villages)
* NPCs
* Some monsters and animals * Some monsters and animals
* Redstone-related things * Redstone-related things
* The End * The End
* Enchanting
* Experience
* Special minecarts * Special minecarts
* A couple of non-trivial blocks and items * A couple of non-trivial blocks and items
@ -182,7 +182,7 @@ Technical differences from Minecraft:
## Reporting bugs ## Reporting bugs
Please report all bugs and missing Minecraft features here: Please report all bugs and missing Minecraft features here:
<https://git.minetest.land/Wuzzy/MineClone2/issues> <https://git.minetest.land/MineClone2/MineClone2/issues>
## Other readme files ## 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. 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 ### 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 * davedevils: Creator of MineClone on which MineClone 2 is based on
* [ex-bart](https://github.com/ex-bart): Redstone comparators * [ex-bart](https://github.com/ex-bart): Redstone comparators
* [Rootyjr](https://github.com/Rootyjr): Fishing rod and bugfixes * [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 * [ryvnf](https://github.com/ryvnf): Explosion mechanics
* MysticTempest: Bugfixes * MysticTempest: Bugfixes
* [bzoss](https://github.com/bzoss): Status effects, potions, brewing stand * [bzoss](https://github.com/bzoss): Status effects, potions, brewing stand
* kay27 <kay27@bk.ru>: Experience system, bugfixes, optimizations * kay27 <kay27@bk.ru>: 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 * 2mac: Fix bug with powered rail
* Lots of other people: TO BE WRITTEN (see mod directories for details) * 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 ### Special thanks
* davedevils for starting MineClone, the original version of this game * davedevils for starting MineClone, the original version of this game
* Wuzzy for starting and maintaining MineClone2 for several years
* celeron55 for creating Minetest * celeron55 for creating Minetest
* Minetest's modding community for providing a huge selection of mods, some of which ended up in MineClone 2 * 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 * 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 ## Info for programmers
You find interesting and useful infos in `API.md`. You find interesting and useful infos in `API.md`.
This project is currently mostly a one-person project.
## Legal information ## Legal information
This is a fan game, not developed or endorsed by Mojang AB. 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: Here's the detailed legalese for those who need it:
### License of source code ### 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. is an imitation of Minecraft.
MineClone 2 is free software: you can redistribute it and/or modify MineClone 2 is free software: you can redistribute it and/or modify

View File

@ -32,6 +32,10 @@ local STEP_LENGTH = 0.3
-- How many rays to compute entity exposure to explosion -- How many rays to compute entity exposure to explosion
local N_EXPOSURE_RAYS = 16 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() minetest.register_on_mods_loaded(function()
-- Store blast resistance values by content ids to improve performance. -- Store blast resistance values by content ids to improve performance.
for name, def in pairs(minetest.registered_nodes) do for name, def in pairs(minetest.registered_nodes) do
@ -135,14 +139,21 @@ end
-- strength - The strength of each ray -- strength - The strength of each ray
-- raydirs - The directions for each ray -- raydirs - The directions for each ray
-- radius - The maximum distance each ray will go -- radius - The maximum distance each ray will go
-- drop_chance - The chance that destroyed nodes will drop their items -- info - Table containing information about explosion
-- fire - If true, 1/3 of destroyed nodes become fire
-- puncher - object that punches other objects (optional) -- 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 -- Note that this function has been optimized, it contains code which has been
-- inlined to avoid function calls and unnecessary table creation. This was -- inlined to avoid function calls and unnecessary table creation. This was
-- measured to give a significant performance increase. -- 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 vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(vector.subtract(pos, radius), 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 data = vm:get_data()
local destroy = {} 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 -- Trace rays for environment destruction
for i = 1, #raydirs do if info.griefing then
local rpos_x = pos.x for i = 1, #raydirs do
local rpos_y = pos.y local rpos_x = pos.x
local rpos_z = pos.z local rpos_y = pos.y
local rdir_x = raydirs[i].x local rpos_z = pos.z
local rdir_y = raydirs[i].y local rdir_x = raydirs[i].x
local rdir_z = raydirs[i].z local rdir_y = raydirs[i].y
local rstr = (0.7 + math.random() * 0.6) * strength 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 for r = 0, math.ceil(radius * (1.0 / STEP_LENGTH)) do
local npos_x = math.floor(rpos_x + 0.5) local npos_x = math.floor(rpos_x + 0.5)
local npos_y = math.floor(rpos_y + 0.5) local npos_y = math.floor(rpos_y + 0.5)
local npos_z = math.floor(rpos_z + 0.5) local npos_z = math.floor(rpos_z + 0.5)
local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride + local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride +
npos_x - emin_x + 1 npos_x - emin_x + 1
local cid = data[idx] local cid = data[idx]
local br = node_blastres[cid] local br = node_blastres[cid]
local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z}) if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then
br = max_blast_resistance
end
rpos_x = rpos_x + STEP_LENGTH * rdir_x local hash = minetest.hash_node_position({x=npos_x, y=npos_y, z=npos_z})
rpos_y = rpos_y + STEP_LENGTH * rdir_y
rpos_z = rpos_z + STEP_LENGTH * rdir_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 rstr = rstr - 0.75 * STEP_LENGTH - (br + 0.3) * STEP_LENGTH
break
end
if cid ~= minetest.CONTENT_AIR and not minetest.is_protected({x = npos_x, y = npos_y, z = npos_z}, "") then if rstr <= 0 then
destroy[hash] = idx 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 end
end end
@ -284,8 +305,18 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
impact = 0 impact = 0
end end
local damage = math.floor((impact * impact + impact) * 7 * strength + 1) 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 if obj:is_player() then
local name = obj:get_player_name() 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 if mod_death_messages then
mcl_death_messages.player_damage(obj, S("@1 was caught in an explosion.", name)) mcl_death_messages.player_damage(obj, S("@1 was caught in an explosion.", name))
end end
@ -293,17 +324,21 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
armor.last_damage_types[name] = "explosion" armor.last_damage_types[name] = "explosion"
end end
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 if sleep_formspec_doesnt_close_mt53 then
obj:add_player_velocity(vector.multiply(punch_dir, impact * 20)) 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
elseif ent.tnt_knockback then if not obj then return end
obj:add_velocity(vector.multiply(punch_dir, impact * 20)) 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 end
end end
@ -313,14 +348,14 @@ local function trace_explode(pos, strength, raydirs, radius, drop_chance, fire,
-- Remove destroyed blocks and drop items -- Remove destroyed blocks and drop items
for hash, idx in pairs(destroy) do 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 on_blast = node_on_blast[data[idx]]
local remove = true local remove = true
if do_drop or on_blast ~= nil then if do_drop or on_blast ~= nil then
local npos = minetest.get_position_from_hash(hash) local npos = minetest.get_position_from_hash(hash)
if on_blast ~= nil then if on_blast ~= nil then
on_blast(npos, 1.0) on_blast(npos, 1.0, do_drop)
remove = false remove = false
else else
local name = minetest.get_name_from_content_id(data[idx]) 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 -- Log explosion
minetest.log('action', 'Explosion at ' .. minetest.pos_to_string(pos) .. minetest.log('action', 'Explosion at ' .. minetest.pos_to_string(pos) ..
' with strength ' .. strength .. ' and radius ' .. radius) ' with strength ' .. strength .. ' and radius ' .. radius)
end end
-- Create an explosion with strength at pos. -- Create an explosion with strength at pos.
@ -371,16 +405,24 @@ end
-- Parameters: -- Parameters:
-- pos - The position where the explosion originates from -- pos - The position where the explosion originates from
-- strength - The blast strength of the explosion (a TNT explosion uses 4) -- 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) -- puncher - object that is reported as source of punches/damage (optional)
-- --
-- Values in info: -- Values in info:
-- drop_chance - If specified becomes the drop chance of all nodes in the -- drop_chance - If specified becomes the drop chance of all nodes in the
-- explosion (defaults to 1.0 / strength) -- explosion (default: 1.0 / strength)
-- no_sound - If true then the explosion will not play a sound -- max_blast_resistance - If specified the explosion will treat all
-- no_particle - If true then the explosion will not create particles -- 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) -- 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) function mcl_explosions.explode(pos, strength, info, puncher)
if info == nil then
info = {}
end
-- The maximum blast radius (in the air) -- The maximum blast radius (in the air)
local radius = math.ceil(1.3 * strength / (0.3 * 0.75) * 0.3) 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 end
local shape = sphere_shapes[radius] local shape = sphere_shapes[radius]
local creative_enabled = minetest.is_creative_enabled("") -- Default values
trace_explode(pos, strength, shape, radius, (info and info.drop_chance) or 1 / strength, info.fire == true, puncher, creative_enabled) 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) add_particles(pos, radius)
end end
if not (info and info.no_sound) then if info.sound then
minetest.sound_play("tnt_explode", { minetest.sound_play("tnt_explode", {
pos = pos, gain = 1.0, pos = pos, gain = 1.0,
max_hear_distance = strength * 16 max_hear_distance = strength * 16

View File

@ -25,6 +25,7 @@ mcl_vars.inventory_header = ""
local mg_name = minetest.get_mapgen_setting("mg_name") local mg_name = minetest.get_mapgen_setting("mg_name")
local minecraft_height_limit = 256 local minecraft_height_limit = 256
local superflat = mg_name == "flat" and minetest.get_mapgen_setting("mcl_superflat_classic") == "true" 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 -- Calculate mapgen_edge_min/mapgen_edge_max
mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5) 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_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * 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 -- Normal mode
--[[ Realm stacking (h is for height) --[[ Realm stacking (h is for height)
- Overworld (h>=256) - Overworld (h>=256)
@ -66,6 +67,14 @@ if not superflat then
mcl_vars.mg_lava = true mcl_vars.mg_lava = true
mcl_vars.mg_bedrock_is_rough = 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 else
-- Classic superflat -- Classic superflat
local ground = minetest.get_mapgen_setting("mgflat_ground_level") 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) -- Set random seed for all other mods (Remember to make sure no other mod calls this function)
math.randomseed(os.time()) math.randomseed(os.time())

View File

@ -11,12 +11,15 @@ Parameters:
stacks_max = 3, -- Maximum number of item stacks to get. Default: 1 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. 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 itemstring = "example:item1", -- Which item to select
amount_min = 1, -- Minimum size of itemstack. Must not be larger than 6553. Optional (default: 1) 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) 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_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) 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 } { -- more tables like above, one table per item stack }
} }
@ -56,24 +59,29 @@ function mcl_loot.get_loot(loot_definitions, pr)
end end
if item then if item then
local itemstring = item.itemstring local itemstring = item.itemstring
if item.amount_min and item.amount_max then local itemstack = item.itemstack
itemstring = itemstring .. " " .. pr:next(item.amount_min, item.amount_max) if itemstring then
end if item.amount_min and item.amount_max then
if item.wear_min and item.wear_max then itemstring = itemstring .. " " .. pr:next(item.amount_min, item.amount_max)
-- 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"
end 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 end
table.insert(items, itemstring)
else
minetest.log("error", "[mcl_loot] INTERNAL ERROR! Failed to select random loot item!")
end end
end end

View File

@ -395,4 +395,13 @@ function mcl_util.generate_on_place_plant_function(condition)
end end
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

View File

@ -50,8 +50,10 @@ local boat = {
mesh = "mcl_boats_boat.b3d", mesh = "mcl_boats_boat.b3d",
textures = {"mcl_boats_texture_oak_boat.png"}, textures = {"mcl_boats_texture_oak_boat.png"},
visual_size = boat_visual_size, visual_size = boat_visual_size,
hp_max = 4,
_driver = nil, -- Attached driver (player) or nil if none _driver = nil, -- Attached driver (player) or nil if none
_passenger = nil,
_v = 0, -- Speed _v = 0, -- Speed
_last_v = 0, -- Temporary speed variable _last_v = 0, -- Temporary speed variable
_removed = false, -- If true, boat entity is considered removed (e.g. after punch) and should be ignored _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 _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) 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 return
end end
local name = clicker:get_player_name() local name = clicker:get_player_name()
if self._driver and clicker == self._driver then --[[if attach and attach:get_luaentity() then
self._driver = nil local luaentity = attach:get_luaentity()
if luaentity._driver then
luaentity._driver = nil
end
clicker:set_detach() clicker:set_detach()
clicker:set_properties({visual_size = {x=1, y=1}}) clicker:set_properties({visual_size = {x=1, y=1}})
mcl_player.player_attached[name] = false end--]]
mcl_player.player_set_animation(clicker, "stand" , 30) if self._driver then
local pos = clicker:get_pos() if self._driver:is_player() then
pos = {x = pos.x, y = pos.y + 0.2, z = pos.z} self._passenger = clicker
clicker:set_pos(pos) else
elseif not self._driver then -- for later use: transport mobs in boats
local attach = clicker:get_attach() self._passenger = self._driver
if attach and attach:get_luaentity() then self._driver = clicker
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 end
set_double_attach(self)
else
self._driver = clicker self._driver = clicker
clicker:set_attach(self.object, "", set_attach(self)
{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())
end 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 end
function boat.on_activate(self, staticdata, dtime_s) 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) local data = minetest.deserialize(staticdata)
if type(data) == "table" then if type(data) == "table" then
self._v = data.v self._v = data.v
self._last_v = self._v self._last_v = self._v
self._itemstring = data.itemstring self._itemstring = data.itemstring
self.object:set_properties({textures=data.textures}) self.object:set_properties({textures = data.textures, damage_texture_modifier = ""})
end end
end end
@ -120,32 +149,26 @@ function boat.get_staticdata(self)
end end
function boat.on_punch(self, puncher) function boat.on_death(self, killer)
if not puncher or not puncher:is_player() or self._removed then if killer and killer:is_player() and minetest.is_creative_enabled(killer:get_player_name()) then
return local inv = killer:get_inventory()
end if not inv:contains_item("main", self._itemstring) then
if self._driver and puncher == self._driver then inv:add_item("main", self._itemstring)
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
end end
self.object:remove() else
minetest.add_item(self.object:get_pos(), self._itemstring)
end 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 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) self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
local on_water = true local on_water = true
local in_water = false local in_water = false
@ -163,8 +186,43 @@ function boat.on_step(self, dtime)
v_slowdown = 0.05 v_slowdown = 0.05
end 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 self._driver then
if had_passenger and not self._passenger then
set_attach(self)
end
local ctrl = self._driver:get_player_control() 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() local yaw = self.object:get_yaw()
if ctrl.up then if ctrl.up then
-- Forwards -- Forwards
@ -191,13 +249,13 @@ function boat.on_step(self, dtime)
self._animation = 0 self._animation = 0
end end
end end
if ctrl.left then if ctrl and ctrl.left then
if self._v < 0 then if self._v < 0 then
self.object:set_yaw(yaw - (1 + dtime) * 0.03 * v_factor) self.object:set_yaw(yaw - (1 + dtime) * 0.03 * v_factor)
else else
self.object:set_yaw(yaw + (1 + dtime) * 0.03 * v_factor) self.object:set_yaw(yaw + (1 + dtime) * 0.03 * v_factor)
end end
elseif ctrl.right then elseif ctrl and ctrl.right then
if self._v < 0 then if self._v < 0 then
self.object:set_yaw(yaw + (1 + dtime) * 0.03 * v_factor) self.object:set_yaw(yaw + (1 + dtime) * 0.03 * v_factor)
else else

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1,3 @@
name = mcl_burning
description = Burning Objects for MineClone2
author = Fleckenstein

View File

@ -2,6 +2,7 @@
local item_drop_settings = {} --settings table 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.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.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.radius_collect = 0.2 --radius of collection
item_drop_settings.player_collect_height = 1.0 --added to their pos y value 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 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} local checkpos = {x=pos.x,y=pos.y + item_drop_settings.player_collect_height,z=pos.z}
--magnet and collection --magnet and collection
for _,object in ipairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.radius_magnet)) do for _,object in ipairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_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 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 object:get_luaentity()._magnet_timer = object:get_luaentity()._magnet_timer + dtime
local collected = false 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 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 if toolgroupcaps[plus] then
return true return true
end end
for e=1,5 do
local effplus = plus .. "_efficiency_" .. e
if toolgroupcaps[effplus] then
return true
end
end
end end
end end
for b=1, #basegroups do for b=1, #basegroups do
@ -204,6 +211,12 @@ local check_can_drop = function(node_name, tool_capabilities)
if toolgroupcaps[plus] then if toolgroupcaps[plus] then
return true return true
end end
for e=1,5 do
local effplus = plus .. "_efficiency_" .. e
if toolgroupcaps[effplus] then
return true
end
end
end 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. -- by hand. Creative Mode is intentionally ignored in this case.
local doTileDrops = minetest.settings:get_bool("mcl_doTileDrops", true) 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 return
end end
@ -285,9 +298,9 @@ function minetest.handle_node_drops(pos, drops, digger)
* true: Drop itself when dug by shears / silk touch tool * 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 * 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 enchantments = tool and mcl_enchanting.get_enchantments(tool, "silk_touch")
local silk_touch_drop = false local silk_touch_drop = false
local nodedef = minetest.registered_nodes[dug_node.name] 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 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 drops = nodedef._mcl_silk_touch_drop
end end
end end
if tool and nodedef._mcl_fortune_drop and enchantments.fortune then if tool and nodedef._mcl_fortune_drop and enchantments.fortune then
local fortune_level = enchantments.fortune local fortune_level = enchantments.fortune
local fortune_drop = nodedef._mcl_fortune_drop local fortune_drop = nodedef._mcl_fortune_drop
@ -319,7 +332,7 @@ function minetest.handle_node_drops(pos, drops, digger)
end end
else else
-- Fixed Behavior -- 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) drops = get_drops(drop, tool:get_name(), dug_node.param2, nodedef.paramtype2)
end end
end end
@ -330,7 +343,7 @@ function minetest.handle_node_drops(pos, drops, digger)
mcl_experience.throw_experience(pos, experience_amount) mcl_experience.throw_experience(pos, experience_amount)
end end
end end
for _,item in ipairs(drops) do for _,item in ipairs(drops) do
local count local count
if type(item) == "string" then if type(item) == "string" then
@ -632,12 +645,15 @@ minetest.register_entity(":__builtin:item", {
local fg = minetest.get_item_group(nn, "fire") local fg = minetest.get_item_group(nn, "fire")
local dg = minetest.get_item_group(nn, "destroys_items") local dg = minetest.get_item_group(nn, "destroys_items")
if (def and (lg ~= 0 or fg ~= 0 or dg == 1)) then if (def and (lg ~= 0 or fg ~= 0 or dg == 1)) then
if dg ~= 2 then --Wait 2 seconds to allow mob drops to be cooked, & picked up instead of instantly destroyed.
minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.5}, true) 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 end
self._removed = true
self.object:remove()
return
end end
-- Push item out when stuck inside solid opaque node -- Push item out when stuck inside solid opaque node

View File

@ -3,7 +3,7 @@
mobs = {} mobs = {}
mobs.mod = "mrm" 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 MAX_MOB_NAME_LENGTH = 30
local HORNY_TIME = 30 local HORNY_TIME = 30
@ -226,7 +226,7 @@ local collision = function(self)
local z = 0 local z = 0
local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5 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() if object:is_player()
or (object:get_luaentity()._cmi_is_mob == true and object ~= self.object) then or (object:get_luaentity()._cmi_is_mob == true and object ~= self.object) then
@ -276,13 +276,16 @@ end
local get_velocity = function(self) local get_velocity = function(self)
local v = self.object:get_velocity() 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 end
-- set and return valid yaw -- 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 if not yaw or yaw ~= yaw then
yaw = 0 yaw = 0
@ -291,6 +294,9 @@ local set_yaw = function(self, yaw, delay)
delay = delay or 0 delay = delay or 0
if delay == 0 then if delay == 0 then
if self.shaking and dtime then
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
end
self.object:set_yaw(yaw) self.object:set_yaw(yaw)
return yaw return yaw
end end
@ -302,8 +308,8 @@ local set_yaw = function(self, yaw, delay)
end end
-- global function to set mob yaw -- global function to set mob yaw
function mobs:yaw(self, yaw, delay) function mobs:yaw(self, yaw, delay, dtime)
set_yaw(self, yaw, delay) set_yaw(self, yaw, delay, dtime)
end end
local add_texture_mod = function(self, mod) local add_texture_mod = function(self, mod)
@ -390,7 +396,7 @@ local is_node_dangerous = function(self, nodename)
return true return true
end end
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 return true
end end
return false return false
@ -643,11 +649,13 @@ end
-- drop items -- drop items
local item_drop = function(self, cooked) local item_drop = function(self, cooked, looting_level)
-- no drops if disabled by setting -- no drops if disabled by setting
if not mobs_drop_items then return end if not mobs_drop_items then return end
looting_level = looting_level or 0
-- no drops for child mobs (except monster) -- no drops for child mobs (except monster)
if (self.child and self.type ~= "monster") then if (self.child and self.type ~= "monster") then
return return
@ -659,11 +667,33 @@ local item_drop = function(self, cooked)
self.drops = self.drops or {} -- nil check self.drops = self.drops or {} -- nil check
for n = 1, #self.drops do 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) local num = 0
item = self.drops[n].name 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 -- cook items when true
if cooked then if cooked then
@ -752,15 +782,22 @@ local check_for_death = function(self, cause, cmi_cause)
local function death_handle(self) local function death_handle(self)
-- dropped cooked item if mob died in fire or lava -- dropped cooked item if mob died in fire or lava
if cause == "lava" or cause == "fire" then if cause == "lava" or cause == "fire" then
item_drop(self, true) item_drop(self, true, 0)
else else
item_drop(self, nil) local wielditem = ItemStack()
end 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(self.object:get_pos(), math.random(self.xp_min, self.xp_max))
if mod_experience and ((not self.child) or self.type ~= "animal") then end
mcl_experience.throw_experience(pos, math.random(self.xp_min, self.xp_max)) 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
end end
@ -768,7 +805,7 @@ local check_for_death = function(self, cause, cmi_cause)
if self.on_die then if self.on_die then
local pos = self.object:get_pos() 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 if on_die_exit ~= true then
death_handle(self) death_handle(self)
end end
@ -779,6 +816,7 @@ local check_for_death = function(self, cause, cmi_cause)
if on_die_exit == true then if on_die_exit == true then
self.state = "die" self.state = "die"
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return true return true
end end
@ -841,6 +879,7 @@ local check_for_death = function(self, cause, cmi_cause)
local dpos = self.object:get_pos() local dpos = self.object:get_pos()
local cbox = self.collisionbox local cbox = self.collisionbox
local yaw = self.object:get_rotation().y local yaw = self.object:get_rotation().y
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
mobs.death_effect(dpos, yaw, cbox, not self.instant_death) mobs.death_effect(dpos, yaw, cbox, not self.instant_death)
end end
@ -903,7 +942,7 @@ local is_at_cliff_or_danger = function(self)
return true return true
else else
local def = minetest.registered_nodes[bnode.name] local def = minetest.registered_nodes[bnode.name]
if def and def.walkable then if def and def.walkable then
return false return false
end end
end end
@ -916,7 +955,7 @@ end
-- copy the 'mob facing cliff_or_danger check' from above, and rework to avoid water -- copy the 'mob facing cliff_or_danger check' from above, and rework to avoid water
local is_at_water_danger = function(self) local is_at_water_danger = function(self)
if not self.object:get_luaentity() then if not self.object:get_luaentity() then
return false return false
end end
@ -941,7 +980,7 @@ local is_at_water_danger = function(self)
return true return true
else else
local def = minetest.registered_nodes[bnode.name] local def = minetest.registered_nodes[bnode.name]
if def and def.walkable then if def and def.walkable then
return false return false
end end
end end
@ -989,6 +1028,7 @@ local do_env_damage = function(self)
-- remove mob if beyond map limits -- remove mob if beyond map limits
if not within_limits(pos, 0) then if not within_limits(pos, 0) then
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return true return true
end end
@ -1017,8 +1057,11 @@ local do_env_damage = function(self)
if mod_worlds then if mod_worlds then
_, dim = mcl_worlds.y_to_layer(pos.y) _, dim = mcl_worlds.y_to_layer(pos.y)
end end
if self.sunlight_damage ~= 0 and (minetest.get_node_light(pos) or 0) >= minetest.LIGHT_MAX and dim == "overworld" 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 deal_light_damage(self, pos, self.sunlight_damage) 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 return true
end end
end end
@ -1106,7 +1149,7 @@ local do_env_damage = function(self)
end end
-- damage_per_second node check -- 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 self.health = self.health - nodef.damage_per_second
@ -1238,7 +1281,7 @@ local do_jump = function(self)
}, "air") }, "air")
-- we don't attempt to jump if there's a stack of blocks blocking -- 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 return false
end end
@ -1269,7 +1312,7 @@ local do_jump = function(self)
end end
self.object:set_acceleration({ self.object:set_acceleration({
x = v.x * 2, x = v.x * 2,
y = 0, y = -10,
z = v.z * 2, z = v.z * 2,
}) })
end, self, v) end, self, v)
@ -2059,6 +2102,7 @@ local follow_flop = function(self)
or self.order == "follow") or self.order == "follow")
and not self.following and not self.following
and self.state ~= "attack" and self.state ~= "attack"
and self.order ~= "sit"
and self.state ~= "runaway" then and self.state ~= "runaway" then
local s = self.object:get_pos() local s = self.object:get_pos()
@ -2079,6 +2123,7 @@ local follow_flop = function(self)
if self.type == "npc" if self.type == "npc"
and self.order == "follow" and self.order == "follow"
and self.state ~= "attack" and self.state ~= "attack"
and self.order ~= "sit"
and self.owner ~= "" then and self.owner ~= "" then
-- npc stop following player if not owner -- npc stop following player if not owner
@ -2211,7 +2256,6 @@ local dogswitch = function(self, dtime)
return self.dogshoot_switch return self.dogshoot_switch
end end
-- execute current state (stand, walk, run, attacks) -- execute current state (stand, walk, run, attacks)
-- returns true if mob has died -- returns true if mob has died
local do_states = function(self, dtime) local do_states = function(self, dtime)
@ -2309,10 +2353,10 @@ local do_states = function(self, dtime)
lp = minetest.find_nodes_in_area_under_air( 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 - 0.5, z = s.z - 5},
{x = s.x + 5, y = s.y + 1, 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)] lp = #lp > 0 and lp[random(#lp)]
-- did we find land? -- did we find land?
if lp then if lp then
@ -2364,6 +2408,8 @@ local do_states = function(self, dtime)
set_velocity(self, 0) set_velocity(self, 0)
self.state = "stand" self.state = "stand"
set_animation(self, "stand") set_animation(self, "stand")
local yaw = self.object:get_yaw() or 0
yaw = set_yaw(self, yaw + 0.78, 8)
else else
set_velocity(self, self.walk_velocity) set_velocity(self, self.walk_velocity)
@ -2390,6 +2436,8 @@ local do_states = function(self, dtime)
set_velocity(self, 0) set_velocity(self, 0)
self.state = "stand" self.state = "stand"
set_animation(self, "stand") set_animation(self, "stand")
local yaw = self.object:get_yaw() or 0
yaw = set_yaw(self, yaw + 0.78, 8)
else else
set_velocity(self, self.run_velocity) set_velocity(self, self.run_velocity)
set_animation(self, "run") set_animation(self, "run")
@ -2434,7 +2482,7 @@ local do_states = function(self, dtime)
if p.x > s.x then yaw = yaw + pi end 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 node_break_radius = self.explosion_radius or 1
local entity_damage_radius = self.explosion_damage_radius local entity_damage_radius = self.explosion_damage_radius
@ -2499,7 +2547,7 @@ local do_states = function(self, dtime)
if mod_explosions then if mod_explosions then
if mobs_griefing and not minetest.is_protected(pos, "") 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 else
minetest.sound_play(self.sounds.explode, { minetest.sound_play(self.sounds.explode, {
pos = pos, 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) effect(pos, 32, "mcl_particles_smoke.png", nil, nil, node_break_radius, 1, 0)
end end
end end
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return true return true
@ -2606,7 +2655,7 @@ local do_states = function(self, dtime)
if p.x > s.x then yaw = yaw + pi end 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 -- move towards enemy if beyond mob reach
if dist > self.reach then if dist > self.reach then
@ -2622,6 +2671,8 @@ local do_states = function(self, dtime)
set_velocity(self, 0) set_velocity(self, 0)
set_animation(self, "stand") set_animation(self, "stand")
local yaw = self.object:get_yaw() or 0
yaw = set_yaw(self, yaw + 0.78, 8)
else else
if self.path.stuck then 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 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) 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 if self.shoot_interval
and self.timer > 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 and random(1, 100) <= 60 then
self.timer = 0 self.timer = 0
@ -2723,10 +2778,6 @@ local do_states = function(self, dtime)
-- play shoot attack sound -- play shoot attack sound
mob_sound(self, "shoot_attack") mob_sound(self, "shoot_attack")
local p = self.object:get_pos()
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2
-- Shoot arrow -- Shoot arrow
if minetest.registered_entities[self.arrow] then if minetest.registered_entities[self.arrow] then
@ -2915,6 +2966,13 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
end end
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 -- check for tool immunity or special damage
for n = 1, #self.immune_to do 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 kb = kb * 1.5
end 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({ self.object:set_velocity({
x = dir.x * kb, x = dir.x * kb,
y = dir.y * kb + up, y = dir.y * kb + up,
@ -3121,7 +3191,8 @@ local mob_staticdata = function(self)
and ((not self.nametag) or (self.nametag == "")) and ((not self.nametag) or (self.nametag == ""))
and self.lifetimer <= 20 then 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() self.object:remove()
return ""-- nil return ""-- nil
@ -3160,7 +3231,7 @@ local mob_activate = function(self, staticdata, def, dtime)
-- remove monsters in peaceful mode -- remove monsters in peaceful mode
if self.type == "monster" if self.type == "monster"
and minetest.settings:get_bool("only_peaceful_mobs", false) then and minetest.settings:get_bool("only_peaceful_mobs", false) then
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return return
@ -3324,6 +3395,10 @@ end
-- main mob function -- main mob function
local mob_step = function(self, dtime) local mob_step = function(self, dtime)
if not self.fire_resistant then
mcl_burning.tick(self.object, dtime)
end
if use_cmi then if use_cmi then
cmi.notify_step(self.object, dtime) cmi.notify_step(self.object, dtime)
end end
@ -3385,6 +3460,9 @@ local mob_step = function(self, dtime)
end end
self.delay = self.delay - 1 self.delay = self.delay - 1
if self.shaking then
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
end
self.object:set_yaw(yaw) self.object:set_yaw(yaw)
end end
@ -3516,39 +3594,27 @@ local mob_step = function(self, dtime)
set_velocity(self, 0) set_velocity(self, 0)
self.state = "stand" self.state = "stand"
set_animation(self, "stand") set_animation(self, "stand")
local yaw = self.object:get_yaw() or 0
yaw = set_yaw(self, yaw + 0.78, 8)
end end
-- Despawning: when lifetimer expires, remove mob -- Despawning: when lifetimer expires, remove mob
if remove_far if remove_far
and self.can_despawn == true 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 self.lifetimer = self.lifetimer - dtime
if self.lifetimer <= 10 then 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))
-- only despawn away from player mcl_burning.extinguish(self.object)
local far_objs = minetest.get_objects_inside_radius(pos, 48) self.object:remove()
for n = 1, #far_objs do elseif self.lifetimer <= 10 then
if math.random(10) < 4 then
if far_objs[n]:is_player() then self.despawn_immediately = true
else
local close_objs = minetest.get_objects_inside_radius(pos, 16) self.lifetimer = 20
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
end end
end end
end end
@ -3750,6 +3816,9 @@ minetest.register_entity(name, {
suffocation_timer = 0, suffocation_timer = 0,
follow_velocity = def.follow_velocity or 2.4, follow_velocity = def.follow_velocity or 2.4,
instant_death = def.instant_death or false, 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 -- End of MCL2 extensions
on_spawn = def.on_spawn, on_spawn = def.on_spawn,
@ -3775,7 +3844,7 @@ minetest.register_entity(name, {
get_staticdata = function(self) get_staticdata = function(self)
return mob_staticdata(self) return mob_staticdata(self)
end, end,
harmed_by_heal = def.harmed_by_heal, 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 pos.y = pos.y + 1
-- only spawn away from player -- 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 for n = 1, #objs do
@ -4106,7 +4175,7 @@ function mobs:register_arrow(name, def)
if self.switch == 0 if self.switch == 0
or self.timer > 150 or self.timer > 150
or not within_limits(pos, 0) then or not within_limits(pos, 0) then
mcl_burning.extinguish(self.object)
self.object:remove(); self.object:remove();
return return
@ -4489,3 +4558,20 @@ function mobs:alias_mob(old_name, new_name)
}) })
end 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)

View File

@ -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) '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_min' the minimum XP it drops on death (default: 0)
'xp_max' the maximum 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)

View File

@ -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) function mobs:spawn_abm_check(pos, node, name)
-- Don't spawn monsters on mycelium -- 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 if (node.name == "mcl_core:mycelium" or node.name == "mcl_core:mycelium_snow") and minetest.registered_entities[name].type == "monster" then
return true 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 -- Spawn on opaque or liquid nodes
elseif minetest.get_item_group(node.name, "opaque") ~= 0 or minetest.registered_nodes[node.name].liquidtype ~= "none" then elseif minetest.get_item_group(node.name, "opaque") ~= 0 or minetest.registered_nodes[node.name].liquidtype ~= "none" then
return false return false
end end
-- Reject everything else -- Reject everything else
return true return true
end end

View File

@ -41,7 +41,8 @@ mobs:register_mob("mobs_mc:blaze", {
{name = mobs_mc.items.blaze_rod, {name = mobs_mc.items.blaze_rod,
chance = 1, chance = 1,
min = 0, min = 0,
max = 1,}, max = 1,
looting = "common",},
}, },
animation = { animation = {
stand_speed = 25, stand_speed = 25,
@ -73,6 +74,7 @@ mobs:register_mob("mobs_mc:blaze", {
makes_footstep_sound = false, makes_footstep_sound = false,
fear_height = 0, fear_height = 0,
glow = 14, 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) 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 if rawget(_G, "armor") and armor.last_damage_types then
armor.last_damage_types[player:get_player_name()] = "fireball" armor.last_damage_types[player:get_player_name()] = "fireball"
end end
mcl_burning.set_on_fire(player, 5, 1, "blaze")
player:punch(self.object, 1.0, { player:punch(self.object, 1.0, {
full_punch_interval = 1.0, full_punch_interval = 1.0,
damage_groups = {fleshy = 5}, damage_groups = {fleshy = 5},
@ -96,6 +99,7 @@ mobs:register_arrow("mobs_mc:blaze_fireball", {
end, end,
hit_mob = function(self, mob) hit_mob = function(self, mob)
mcl_burning.set_on_fire(mob, 5)
mob:punch(self.object, 1.0, { mob:punch(self.object, 1.0, {
full_punch_interval = 1.0, full_punch_interval = 1.0,
damage_groups = {fleshy = 5}, damage_groups = {fleshy = 5},

View File

@ -32,11 +32,13 @@ mobs:register_mob("mobs_mc:chicken", {
{name = mobs_mc.items.chicken_raw, {name = mobs_mc.items.chicken_raw,
chance = 1, chance = 1,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "common",},
{name = mobs_mc.items.feather, {name = mobs_mc.items.feather,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
}, },
fall_damage = 0, fall_damage = 0,
fall_speed = -2.25, fall_speed = -2.25,

View File

@ -23,11 +23,13 @@ local cow_def = {
{name = mobs_mc.items.beef_raw, {name = mobs_mc.items.beef_raw,
chance = 1, chance = 1,
min = 1, min = 1,
max = 3,}, max = 3,
looting = "common",},
{name = mobs_mc.items.leather, {name = mobs_mc.items.leather,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
}, },
runaway = true, runaway = true,
sounds = { sounds = {

View File

@ -37,7 +37,7 @@ mobs:register_mob("mobs_mc:creeper", {
run_velocity = 2.1, run_velocity = 2.1,
runaway_from = { "mobs_mc:ocelot", "mobs_mc:cat" }, runaway_from = { "mobs_mc:ocelot", "mobs_mc:cat" },
attack_type = "explode", attack_type = "explode",
explosion_strength = 3, explosion_strength = 3,
reach = 4, reach = 4,
explosion_timer = 1.5, explosion_timer = 1.5,
@ -71,17 +71,21 @@ mobs:register_mob("mobs_mc:creeper", {
if self._forced_explosion_countdown_timer ~= nil then if self._forced_explosion_countdown_timer ~= nil then
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
if self._forced_explosion_countdown_timer <= 0 then 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() self.object:remove()
end end
end end
end, end,
on_die = function(self, pos) on_die = function(self, pos, cmi_cause)
-- Drop a random music disc -- Drop a random music disc when killed by skeleton or stray
-- TODO: Only do this if killed by skeleton if cmi_cause and cmi_cause.type == "punch" then
if math.random(1, 200) == 1 then local luaentity = cmi_cause.puncher and cmi_cause.puncher:get_luaentity()
local r = math.random(1, #mobs_mc.items.music_discs) if luaentity and luaentity.name:find("arrow") then
minetest.add_item({x=pos.x, y=pos.y+1, z=pos.z}, mobs_mc.items.music_discs[r]) 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
end, end,
maxdrops = 2, maxdrops = 2,
@ -89,7 +93,8 @@ mobs:register_mob("mobs_mc:creeper", {
{name = mobs_mc.items.gunpowder, {name = mobs_mc.items.gunpowder,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
-- Head -- Head
-- TODO: Only drop if killed by charged creeper -- TODO: Only drop if killed by charged creeper

View File

@ -49,6 +49,8 @@ mobs:register_mob("mobs_mc:enderdragon", {
arrow = "mobs_mc:dragon_fireball", arrow = "mobs_mc:dragon_fireball",
shoot_interval = 0.5, shoot_interval = 0.5,
shoot_offset = -1.0, shoot_offset = -1.0,
xp_min = 12000,
xp_max = 12000,
animation = { animation = {
fly_speed = 8, stand_speed = 8, fly_speed = 8, stand_speed = 8,
stand_start = 0, stand_end = 20, stand_start = 0, stand_end = 20,
@ -65,7 +67,8 @@ mobs:register_mob("mobs_mc:enderdragon", {
--end --end
end end
minetest.add_item(own_pos, mobs_mc.items.dragon_egg) minetest.add_item(own_pos, mobs_mc.items.dragon_egg)
end end,
fire_resistant = true,
}) })

View File

@ -43,10 +43,10 @@ end
local pr = PseudoRandom(os.time()*(-334)) local pr = PseudoRandom(os.time()*(-334))
-- How freqeuntly to take and place blocks, in seconds -- How freqeuntly to take and place blocks, in seconds
local take_frequency_min = 25 local take_frequency_min = 235
local take_frequency_max = 90 local take_frequency_max = 245
local place_frequency_min = 10 local place_frequency_min = 235
local place_frequency_max = 30 local place_frequency_max = 245
-- Create the textures table for the enderman, depending on which kind of block -- Create the textures table for the enderman, depending on which kind of block
-- the enderman holds (if any). -- the enderman holds (if any).
@ -220,7 +220,8 @@ mobs:register_mob("mobs_mc:enderman", {
{name = mobs_mc.items.ender_pearl, {name = mobs_mc.items.ender_pearl,
chance = 1, chance = 1,
min = 0, min = 0,
max = 1,}, max = 1,
looting = "common"},
}, },
animation = select_enderman_animation("normal"), animation = select_enderman_animation("normal"),
_taken_node = "", _taken_node = "",
@ -273,10 +274,10 @@ mobs:register_mob("mobs_mc:enderman", {
end end
-- AGRESSIVELY WARP/CHASE PLAYER BEHAVIOUR HERE. -- AGRESSIVELY WARP/CHASE PLAYER BEHAVIOUR HERE.
if self.state == "attack" then if self.state == "attack" then
if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then --if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
self:teleport(nil) --self:teleport(nil)
self.state = "" --self.state = ""
else --else
if self.attack then if self.attack then
local target = self.attack local target = self.attack
local pos = target:get_pos() local pos = target:get_pos()
@ -286,7 +287,7 @@ mobs:register_mob("mobs_mc:enderman", {
end end
end end
end end
end --end
end end
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE. -- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
-- Check for arrows and people nearby. -- Check for arrows and people nearby.
@ -297,9 +298,9 @@ mobs:register_mob("mobs_mc:enderman", {
if obj then if obj then
if minetest.is_player(obj) then if minetest.is_player(obj) then
-- Warp from players during day. -- Warp from players during day.
if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then --if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
self:teleport(nil) -- self:teleport(nil)
end --end
else else
local lua = obj:get_luaentity() local lua = obj:get_luaentity()
if lua then if lua then
@ -314,14 +315,14 @@ mobs:register_mob("mobs_mc:enderman", {
local enderpos = self.object:get_pos() local enderpos = self.object:get_pos()
if self.provoked == "broke_contact" then if self.provoked == "broke_contact" then
self.provoked = "false" self.provoked = "false"
if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then --if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
self:teleport(nil) -- self:teleport(nil)
self.state = "" -- self.state = ""
else --else
if self.attack ~= nil then if self.attack ~= nil then
self.state = 'attack' self.state = 'attack'
end end
end --end
end end
-- Check to see if people are near by enough to look at us. -- Check to see if people are near by enough to look at us.
local objs = minetest.get_objects_inside_radius(enderpos, 64) 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) do_punch = function(self, hitter, tflp, tool_caps, dir)
-- damage from rain caused by itself so we don't want it to attack itself. -- damage from rain caused by itself so we don't want it to attack itself.
if hitter ~= self.object and hitter ~= nil then if hitter ~= self.object and hitter ~= nil then
if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then --if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
self:teleport(nil) -- self:teleport(nil)
else --else
self:teleport(hitter) self:teleport(hitter)
self.attack=hitter self.attack=hitter
self.state="attack" self.state="attack"
end --end
end end
end, end,
armor = { fleshy = 100, water_vulnerable = 100 }, armor = { fleshy = 100, water_vulnerable = 100 },

View File

@ -38,8 +38,8 @@ mobs:register_mob("mobs_mc:ghast", {
walk_velocity = 1.6, walk_velocity = 1.6,
run_velocity = 3.2, run_velocity = 3.2,
drops = { drops = {
{name = mobs_mc.items.gunpowder, chance = 1, min = 0, max = 2,}, {name = mobs_mc.items.gunpowder, chance = 1, min = 0, max = 2, looting = "common"},
{name = mobs_mc.items.ghast_tear, chance = 3,min = 0,max = 1,}, {name = mobs_mc.items.ghast_tear, chance = 10/6, min = 0, max = 1, looting = "common", looting_ignore_chance = true},
}, },
animation = { animation = {
stand_speed = 50, walk_speed = 50, run_speed = 50, stand_speed = 50, walk_speed = 50, run_speed = 50,
@ -62,6 +62,7 @@ mobs:register_mob("mobs_mc:ghast", {
fly = true, fly = true,
makes_footstep_sound = false, makes_footstep_sound = false,
instant_death = true, instant_death = true,
fire_resistant = true,
}) })

View File

@ -46,7 +46,8 @@ mobs:register_mob("mobs_mc:guardian", {
{name = mobs_mc.items.prismarine_shard, {name = mobs_mc.items.prismarine_shard,
chance = 1, chance = 1,
min = 0, min = 0,
max = 32,}, max = 32,
looting = "common",},
-- TODO: Reduce of drops when ocean monument is ready. -- TODO: Reduce of drops when ocean monument is ready.
-- The following drops are approximations -- The following drops are approximations
@ -54,29 +55,39 @@ mobs:register_mob("mobs_mc:guardian", {
{name = mobs_mc.items.fish_raw, {name = mobs_mc.items.fish_raw,
chance = 4, chance = 4,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "common",},
{name = mobs_mc.items.prismarine_crystals, {name = mobs_mc.items.prismarine_crystals,
chance = 4, chance = 4,
min = 1, min = 1,
max = 2,}, max = 2,
looting = "common",},
-- Rare drop: fish -- Rare drop: fish
{name = mobs_mc.items.fish_raw, {name = mobs_mc.items.fish_raw,
chance = 160, -- 2.5% / 4 chance = 160, -- 2.5% / 4
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.0025,},
{name = mobs_mc.items.salmon_raw, {name = mobs_mc.items.salmon_raw,
chance = 160, chance = 160,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.0025,},
{name = mobs_mc.items.clownfish_raw, {name = mobs_mc.items.clownfish_raw,
chance = 160, chance = 160,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.0025,},
{name = mobs_mc.items.pufferfish_raw, {name = mobs_mc.items.pufferfish_raw,
chance = 160, chance = 160,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.0025,},
}, },
fly = true, fly = true,
makes_footstep_sound = false, makes_footstep_sound = false,

View File

@ -51,7 +51,8 @@ mobs:register_mob("mobs_mc:guardian_elder", {
{name = mobs_mc.items.prismarine_shard, {name = mobs_mc.items.prismarine_shard,
chance = 1, chance = 1,
min = 1, min = 1,
max = 64,}, max = 64,
looting = "common",},
-- TODO: Only drop if killed by player -- TODO: Only drop if killed by player
{name = mobs_mc.items.wet_sponge, {name = mobs_mc.items.wet_sponge,
@ -64,29 +65,39 @@ mobs:register_mob("mobs_mc:guardian_elder", {
{name = mobs_mc.items.fish_raw, {name = mobs_mc.items.fish_raw,
chance = 4, chance = 4,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "common",},
{name = mobs_mc.items.prismarine_crystals, {name = mobs_mc.items.prismarine_crystals,
chance = 1, chance = 1,
min = 1, min = 1,
max = 10,}, max = 10,
looting = "common",},
-- Rare drop: fish -- Rare drop: fish
{name = mobs_mc.items.fish_raw, {name = mobs_mc.items.fish_raw,
chance = 160, -- 2.5% / 4 chance = 160, -- 2.5% / 4
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 4,},
{name = mobs_mc.items.salmon_raw, {name = mobs_mc.items.salmon_raw,
chance = 160, chance = 160,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 4,},
{name = mobs_mc.items.clownfish_raw, {name = mobs_mc.items.clownfish_raw,
chance = 160, chance = 160,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 4,},
{name = mobs_mc.items.pufferfish_raw, {name = mobs_mc.items.pufferfish_raw,
chance = 160, chance = 160,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 4,},
}, },
fly = true, fly = true,
makes_footstep_sound = false, makes_footstep_sound = false,

View File

@ -127,7 +127,8 @@ local horse = {
{name = mobs_mc.items.leather, {name = mobs_mc.items.leather,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
}, },
do_custom = function(self, dtime) do_custom = function(self, dtime)

View File

@ -54,7 +54,8 @@ mobs:register_mob("mobs_mc:llama", {
{name = mobs_mc.items.leather, {name = mobs_mc.items.leather,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
}, },
fear_height = 4, fear_height = 4,
sounds = { sounds = {

View File

@ -37,7 +37,8 @@ mobs:register_mob("mobs_mc:parrot", {
{name = mobs_mc.items.feather, {name = mobs_mc.items.feather,
chance = 1, chance = 1,
min = 1, min = 1,
max = 2,}, max = 2,
looting = "common",},
}, },
animation = { animation = {
stand_speed = 50, stand_speed = 50,

View File

@ -27,7 +27,8 @@ mobs:register_mob("mobs_mc:pig", {
{name = mobs_mc.items.porkchop_raw, {name = mobs_mc.items.porkchop_raw,
chance = 1, chance = 1,
min = 1, min = 1,
max = 3,}, max = 3,
looting = "common",},
}, },
fear_height = 4, fear_height = 4,
sounds = { sounds = {

View File

@ -36,12 +36,14 @@ mobs:register_mob("mobs_mc:polar_bear", {
{name = mobs_mc.items.fish_raw, {name = mobs_mc.items.fish_raw,
chance = 2, chance = 2,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
-- 1/4 to drop raw salmon -- 1/4 to drop raw salmon
{name = mobs_mc.items.salmon_raw, {name = mobs_mc.items.salmon_raw,
chance = 4, chance = 4,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
}, },
floats = 1, floats = 1,

View File

@ -41,11 +41,9 @@ local rabbit = {
runaway = true, runaway = true,
jump = true, jump = true,
drops = { drops = {
{name = mobs_mc.items.rabbit_raw, chance = 1, min = 0, 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}, {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}, {name = mobs_mc.items.rabbit_foot, chance = 10, min = 0, max = 1, looting = "rare", looting_factor = 0.03,},
-- TODO: Drop rabbit's foot when it's useful
--{name = mobs_mc.items.rabbit_foot, chance = 10, min = 1, max = 1},
}, },
fear_height = 4, fear_height = 4,
animation = { animation = {

View File

@ -63,11 +63,13 @@ mobs:register_mob("mobs_mc:sheep", {
{name = mobs_mc.items.mutton_raw, {name = mobs_mc.items.mutton_raw,
chance = 1, chance = 1,
min = 1, min = 1,
max = 2,}, max = 2,
looting = "common",},
{name = colors["unicolor_white"][1], {name = colors["unicolor_white"][1],
chance = 1, chance = 1,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "common",},
}, },
fear_height = 4, fear_height = 4,
sounds = { sounds = {

View File

@ -35,9 +35,11 @@ mobs:register_mob("mobs_mc:shulker", {
jump = false, jump = false,
drops = { drops = {
{name = mobs_mc.items.shulker_shell, {name = mobs_mc.items.shulker_shell,
chance = 1, chance = 2,
min = 0, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.0625},
}, },
animation = { animation = {
stand_speed = 25, walk_speed = 25, run_speed = 50, punch_speed = 25, stand_speed = 25, walk_speed = 25, run_speed = 50, punch_speed = 25,

View File

@ -46,15 +46,18 @@ local skeleton = {
{name = mobs_mc.items.arrow, {name = mobs_mc.items.arrow,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
{name = mobs_mc.items.bow, {name = mobs_mc.items.bow,
chance = 11, chance = 100 / 8.5,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",},
{name = mobs_mc.items.bone, {name = mobs_mc.items.bone,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
-- Head -- Head
-- TODO: Only drop if killed by charged creeper -- TODO: Only drop if killed by charged creeper
@ -78,7 +81,7 @@ local skeleton = {
die_speed = 15, die_speed = 15,
die_loop = false, die_loop = false,
}, },
sunlight_damage = 1, ignited_by_sunlight = true,
view_range = 16, view_range = 16,
fear_height = 4, fear_height = 4,
attack_type = "dogshoot", attack_type = "dogshoot",
@ -116,12 +119,21 @@ stray.textures = {
-- TODO: different sound (w/ echo) -- TODO: different sound (w/ echo)
-- TODO: stray's arrow inflicts slowness status -- TODO: stray's arrow inflicts slowness status
table.insert(stray.drops, { table.insert(stray.drops, {
-- Chance to drop additional arrow. name = "mcl_potions:slowness_arrow",
-- TODO: Should be tipped arrow of slowness
name = mobs_mc.items.arrow,
chance = 2, chance = 2,
min = 1, min = 1,
max = 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) mobs:register_mob("mobs_mc:stray", stray)

View File

@ -45,17 +45,20 @@ mobs:register_mob("mobs_mc:witherskeleton", {
{name = mobs_mc.items.coal, {name = mobs_mc.items.coal,
chance = 1, chance = 1,
min = 0, min = 0,
max = 1,}, max = 1,
looting = "common",},
{name = mobs_mc.items.bone, {name = mobs_mc.items.bone,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
-- Head -- Head
{name = mobs_mc.items.head_wither_skeleton, {name = mobs_mc.items.head_wither_skeleton,
chance = 40, -- 2.5% chance chance = 40, -- 2.5% chance
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",},
}, },
animation = { animation = {
stand_start = 0, stand_start = 0,
@ -87,6 +90,7 @@ mobs:register_mob("mobs_mc:witherskeleton", {
dogshoot_count_max =0.5, dogshoot_count_max =0.5,
fear_height = 4, fear_height = 4,
harmed_by_heal = true, harmed_by_heal = true,
fire_resistant = true,
}) })
--spawn --spawn

View File

@ -108,7 +108,8 @@ local slime_big = {
jump_height = 5.2, jump_height = 5.2,
fear_height = 0, fear_height = 0,
spawn_small_alternative = "mobs_mc:slime_small", 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) mobs:register_mob("mobs_mc:slime_big", slime_big)
@ -220,7 +221,8 @@ local magma_cube_big = {
walk_chance = 0, walk_chance = 0,
fear_height = 0, fear_height = 0,
spawn_small_alternative = "mobs_mc:magma_cube_small", 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) mobs:register_mob("mobs_mc:magma_cube_big", magma_cube_big)

View File

@ -49,8 +49,10 @@ local spider = {
view_range = 16, view_range = 16,
floats = 1, floats = 1,
drops = { drops = {
{name = mobs_mc.items.string, chance = 1, min = 0, max = 2,}, {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,}, {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" }, specific_attack = { "player", "mobs_mc:iron_golem" },
fear_height = 4, fear_height = 4,

View File

@ -42,7 +42,8 @@ mobs:register_mob("mobs_mc:squid", {
{name = mobs_mc.items.black_dye, {name = mobs_mc.items.black_dye,
chance = 1, chance = 1,
min = 1, min = 1,
max = 3,}, max = 3,
looting = "common",},
}, },
visual_size = {x=3, y=3}, visual_size = {x=3, y=3},
makes_footstep_sound = false, makes_footstep_sound = false,

View File

@ -99,8 +99,7 @@ local professions = {
{ {
{ { "mcl_fishing:fish_raw", 6, 6, "mcl_core:emerald", 1, 1 }, { "mcl_fishing:fish_cooked", 6, 6 } }, { { "mcl_fishing:fish_raw", 6, 6, "mcl_core:emerald", 1, 1 }, { "mcl_fishing:fish_cooked", 6, 6 } },
{ { "mcl_mobitems:string", 15, 20 }, E1 }, { { "mcl_mobitems:string", 15, 20 }, E1 },
-- TODO: replace with enchanted fishing rod { { "mcl_core:emerald", 3, 11 }, { "mcl_fishing:fishing_rod_enchanted", 1, 1} },
{ { "mcl_core:emerald", 3, 11 }, { "mcl_fishing:fishing_rod", 1, 1} },
}, },
}, },
}, },
@ -154,23 +153,27 @@ local professions = {
trades = { trades = {
{ {
{ { "mcl_core:paper", 24, 36 }, E1 }, { { "mcl_core:paper", 24, 36 }, E1 },
-- TODO: enchanted book
{ { "mcl_books:book", 8, 10 }, E1 }, { { "mcl_books:book", 8, 10 }, E1 },
{ { "mcl_core:emerald", 10, 12 }, { "mcl_compass:compass", 1 ,1 }}, { { "mcl_core:emerald", 10, 12 }, { "mcl_compass:compass", 1 ,1 }},
{ { "mcl_core:emerald", 3, 4 }, { "mcl_books:bookshelf", 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_books:written_book", 2, 2 }, E1 },
{ { "mcl_core:emerald", 10, 12 }, { "mcl_clock:clock", 1, 1 } }, { { "mcl_core:emerald", 10, 12 }, { "mcl_clock:clock", 1, 1 } },
{ E1, { "mcl_core:glass", 3, 5 } }, { E1, { "mcl_core:glass", 3, 5 } },
{ { "mcl_core:emerald", 5, 64 }, { "mcl_enchanting:book_enchanted", 1 ,1 }},
}, },
{ {
{ E1, { "mcl_core:glass", 3, 5 } }, { 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 } }, { { "mcl_core:emerald", 20, 22 }, { "mcl_mobs:nametag", 1, 1 } },
@ -214,8 +217,7 @@ local professions = {
{ {
{ { "mcl_core:diamond", 3, 4 }, E1 }, { { "mcl_core:diamond", 3, 4 }, E1 },
-- TODO: enchant { { "mcl_core:emerald", 16, 19 }, { "mcl_armor:chestplate_diamond_enchanted", 1, 1 } },
{ { "mcl_core:emerald", 16, 19 }, { "mcl_armor:chestplate_diamond", 1, 1 } },
}, },
{ {
@ -236,8 +238,7 @@ local professions = {
}, },
{ {
-- TODO: enchant { { "mcl_core:emerald", 7, 12 }, { "mcl_armor:chestplate_leather_enchanted", 1, 1 } },
{ { "mcl_core:emerald", 7, 12 }, { "mcl_armor:chestplate_leather", 1, 1 } },
}, },
{ {
@ -272,16 +273,13 @@ local professions = {
{ {
{ { "mcl_core:iron_ingot", 7, 9 }, E1 }, { { "mcl_core:iron_ingot", 7, 9 }, E1 },
-- TODO: enchant { { "mcl_core:emerald", 9, 10 }, { "mcl_tools:sword_iron_enchanted", 1, 1 } },
{ { "mcl_core:emerald", 9, 10 }, { "mcl_tools:sword_iron", 1, 1 } },
}, },
{ {
{ { "mcl_core:diamond", 3, 4 }, E1 }, { { "mcl_core:diamond", 3, 4 }, E1 },
-- TODO: enchant { { "mcl_core:emerald", 12, 15 }, { "mcl_tools:sword_diamond_enchanted", 1, 1 } },
{ { "mcl_core:emerald", 12, 15 }, { "mcl_tools:sword_diamond", 1, 1 } }, { { "mcl_core:emerald", 9, 12 }, { "mcl_tools:axe_diamond_enchanted", 1, 1 } },
-- TODO: enchant
{ { "mcl_core:emerald", 9, 12 }, { "mcl_tools:axe_diamond", 1, 1 } },
}, },
}, },
}, },
@ -291,20 +289,17 @@ local professions = {
trades = { trades = {
{ {
{ { "mcl_core:coal_lump", 16, 24 }, E1 }, { { "mcl_core:coal_lump", 16, 24 }, E1 },
-- TODO: enchant { { "mcl_core:emerald", 5, 7 }, { "mcl_tools:shovel_iron_enchanted", 1, 1 } },
{ { "mcl_core:emerald", 5, 7 }, { "mcl_tools:shovel_iron", 1, 1 } },
}, },
{ {
{ { "mcl_core:iron_ingot", 7, 9 }, E1 }, { { "mcl_core:iron_ingot", 7, 9 }, E1 },
-- TODO: enchant { { "mcl_core:emerald", 9, 11 }, { "mcl_tools:pick_iron_enchanted", 1, 1 } },
{ { "mcl_core:emerald", 9, 11 }, { "mcl_tools:pick_iron", 1, 1 } },
}, },
{ {
{ { "mcl_core:diamond", 3, 4 }, E1 }, { { "mcl_core:diamond", 3, 4 }, E1 },
-- TODO: enchant { { "mcl_core:emerald", 12, 15 }, { "mcl_tools:pick_diamond_enchanted", 1, 1 } },
{ { "mcl_core:emerald", 12, 15 }, { "mcl_tools:pick_diamond", 1, 1 } },
}, },
}, },
}, },
@ -328,7 +323,10 @@ local professions = {
TRADE_V6_RED_SANDSTONE, 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 = { nitwit = {
@ -408,6 +406,15 @@ local init_trades = function(self, inv)
local offered_item = trade[2][1] local offered_item = trade[2][1]
local offered_count = math.random(trade[2][2], trade[2][3]) 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 } local wanted = { wanted1_item .. " " ..wanted1_count }
if trade[1][4] then if trade[1][4] then
local wanted2_item = trade[1][4] local wanted2_item = trade[1][4]
@ -417,7 +424,7 @@ local init_trades = function(self, inv)
table.insert(trades, { table.insert(trades, {
wanted = wanted, wanted = wanted,
offered = offered_item .. " " .. offered_count, offered = offered_stack:to_table(),
tier = tiernum, -- tier of this trade tier = tiernum, -- tier of this trade
traded_once = false, -- true if trade was traded at least once 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 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
end end
self._trades = minetest.serialize(trades) self._trades = minetest.serialize(trades)
minetest.deserialize(self._trades)
end end
local set_trade = function(trader, player, inv, concrete_tradenum) 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().."]" .."item_image[2,1;1,1;"..wanted1:to_string().."]"
.."tooltip[2,1;0.8,0.8;"..F(wanted1:get_description()).."]" .."tooltip[2,1;0.8,0.8;"..F(wanted1:get_description()).."]"
..w2_formspec ..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()).."]" .."tooltip[5.76,1;0.8,0.8;"..F(offered:get_description()).."]"
.."list["..tradeinv..";input;2,2.5;2,1;]" .."list["..tradeinv..";input;2,2.5;2,1;]"
.."list["..tradeinv..";output;5.76,2.55;1,1;]" .."list["..tradeinv..";output;5.76,2.55;1,1;]"
@ -954,6 +962,7 @@ mobs:register_mob("mobs_mc:villager", {
walk_velocity = 1.2, walk_velocity = 1.2,
run_velocity = 2.4, run_velocity = 2.4,
drops = {}, drops = {},
can_despawn = false,
-- TODO: sounds -- TODO: sounds
animation = { animation = {
stand_speed = 25, stand_speed = 25,

View File

@ -55,7 +55,8 @@ mobs:register_mob("mobs_mc:evoker", {
{name = mobs_mc.items.emerald, {name = mobs_mc.items.emerald,
chance = 1, chance = 1,
min = 0, min = 0,
max = 1,}, max = 1,
looting = "common",},
{name = mobs_mc.items.totem, {name = mobs_mc.items.totem,
chance = 1, chance = 1,
min = 1, min = 1,

View File

@ -41,11 +41,13 @@ mobs:register_mob("mobs_mc:vindicator", {
{name = mobs_mc.items.emerald, {name = mobs_mc.items.emerald,
chance = 1, chance = 1,
min = 0, min = 0,
max = 1,}, max = 1,
looting = "common",},
{name = mobs_mc.items.iron_axe, {name = mobs_mc.items.iron_axe,
chance = 11, chance = 100 / 8.5,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",},
}, },
-- TODO: sounds -- TODO: sounds
animation = { animation = {
@ -66,7 +68,6 @@ mobs:register_mob("mobs_mc:vindicator", {
}, },
view_range = 16, view_range = 16,
fear_height = 4, fear_height = 4,
}) })
-- spawn eggs -- spawn eggs

View File

@ -5,12 +5,25 @@
local S = minetest.get_translator("mobs_mc") local S = minetest.get_translator("mobs_mc")
-- TODO: Turn villagers to zombie villager
--################### --###################
--################### 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", { mobs:register_mob("mobs_mc:villager_zombie", {
type = "monster", type = "monster",
@ -44,19 +57,26 @@ mobs:register_mob("mobs_mc:villager_zombie", {
{name = mobs_mc.items.rotten_flesh, {name = mobs_mc.items.rotten_flesh,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
{name = mobs_mc.items.iron_ingot, {name = mobs_mc.items.iron_ingot,
chance = 120, -- 2.5% / 3 chance = 120, -- 2.5% / 3
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 3,},
{name = mobs_mc.items.carrot, {name = mobs_mc.items.carrot,
chance = 120, -- 2.5% / 3 chance = 120, -- 2.5% / 3
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 3,},
{name = mobs_mc.items.potato, {name = mobs_mc.items.potato,
chance = 120, -- 2.5% / 3 chance = 120, -- 2.5% / 3
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 3,},
}, },
sounds = { sounds = {
random = "mobs_mc_zombie_growl", random = "mobs_mc_zombie_growl",
@ -75,14 +95,60 @@ mobs:register_mob("mobs_mc:villager_zombie", {
run_start = 0, run_start = 0,
run_end = 20, 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, view_range = 16,
fear_height = 4, fear_height = 4,
harmed_by_heal = true, 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 -- spawn eggs
mobs:register_egg("mobs_mc:villager_zombie", S("Zombie Villager"), "mobs_mc_spawn_icon_zombie_villager.png", 0) mobs:register_egg("mobs_mc:villager_zombie", S("Zombie Villager"), "mobs_mc_spawn_icon_zombie_villager.png", 0)

View File

@ -41,13 +41,13 @@ mobs:register_mob("mobs_mc:witch", {
dogshoot_count_max =1.8, dogshoot_count_max =1.8,
max_drops = 3, max_drops = 3,
drops = { drops = {
{name = mobs_mc.items.glass_bottle, chance = 8, 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,}, {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,}, {name = mobs_mc.items.gunpowder, chance = 8, min = 0, max = 2, looting = "common",},
{name = mobs_mc.items.redstone, chance = 8, min = 0, max = 2,}, {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,}, {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,}, {name = mobs_mc.items.sugar, chance = 8, min = 0, max = 2, looting = "common",},
{name = mobs_mc.items.stick, chance = 4, min = 0, max = 2,}, {name = mobs_mc.items.stick, chance = 4, min = 0, max = 2, looting = "common",},
}, },
-- TODO: sounds -- TODO: sounds
animation = { animation = {
@ -68,7 +68,6 @@ mobs:register_mob("mobs_mc:witch", {
}, },
view_range = 16, view_range = 16,
fear_height = 4, fear_height = 4,
}) })
-- potion projectile (EXPERIMENTAL) -- potion projectile (EXPERIMENTAL)

View File

@ -13,19 +13,26 @@ local drops_common = {
{name = mobs_mc.items.rotten_flesh, {name = mobs_mc.items.rotten_flesh,
chance = 1, chance = 1,
min = 0, min = 0,
max = 2,}, max = 2,
looting = "common",},
{name = mobs_mc.items.iron_ingot, {name = mobs_mc.items.iron_ingot,
chance = 120, -- 2.5% / 3 chance = 120, -- 2.5% / 3
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 3,},
{name = mobs_mc.items.carrot, {name = mobs_mc.items.carrot,
chance = 120, -- 2.5% / 3 chance = 120, -- 2.5% / 3
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 3,},
{name = mobs_mc.items.potato, {name = mobs_mc.items.potato,
chance = 120, -- 2.5% / 3 chance = 120, -- 2.5% / 3
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare",
looting_factor = 0.01 / 3,},
} }
local drops_zombie = table.copy(drops_common) local drops_zombie = table.copy(drops_common)
@ -78,6 +85,7 @@ local zombie = {
walk_start = 0, walk_end = 40, walk_start = 0, walk_end = 40,
run_start = 0, run_end = 40, run_start = 0, run_end = 40,
}, },
ignited_by_sunlight = true,
sunlight_damage = 2, sunlight_damage = 2,
view_range = 16, view_range = 16,
attack_type = "dogfight", attack_type = "dogfight",
@ -104,6 +112,7 @@ mobs:register_mob("mobs_mc:baby_zombie", baby_zombie)
-- Desert variant of the zombie -- Desert variant of the zombie
local husk = table.copy(zombie) local husk = table.copy(zombie)
husk.textures = {{"mobs_mc_husk.png"}} husk.textures = {{"mobs_mc_husk.png"}}
husk.ignited_by_sunlight = false
husk.sunlight_damage = 0 husk.sunlight_damage = 0
husk.drops = drops_common husk.drops = drops_common
-- TODO: Husks avoid water -- TODO: Husks avoid water

View File

@ -19,7 +19,6 @@ local pigman = {
hp_max = 20, hp_max = 20,
xp_min = 6, xp_min = 6,
xp_max = 6, xp_max = 6,
breath_max = -1,
armor = {undead = 90, fleshy = 90}, armor = {undead = 90, fleshy = 90},
attack_type = "dogfight", attack_type = "dogfight",
group_attack = { "mobs_mc:pigman", "mobs_mc:baby_pigman" }, group_attack = { "mobs_mc:pigman", "mobs_mc:baby_pigman" },
@ -50,19 +49,23 @@ local pigman = {
{name = mobs_mc.items.rotten_flesh, {name = mobs_mc.items.rotten_flesh,
chance = 1, chance = 1,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "common"},
{name = mobs_mc.items.gold_nugget, {name = mobs_mc.items.gold_nugget,
chance = 1, chance = 1,
min = 0, min = 0,
max = 1,}, max = 1,
looting = "common"},
{name = mobs_mc.items.gold_ingot, {name = mobs_mc.items.gold_ingot,
chance = 40, -- 2.5% chance = 40, -- 2.5%
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare"},
{name = mobs_mc.items.gold_sword, {name = mobs_mc.items.gold_sword,
chance = 12, -- 8.333%, approximation to 8.5% chance = 100 / 8.5,
min = 1, min = 1,
max = 1,}, max = 1,
looting = "rare"},
}, },
animation = { animation = {
stand_speed = 25, stand_speed = 25,
@ -82,6 +85,7 @@ local pigman = {
fear_height = 4, fear_height = 4,
view_range = 16, view_range = 16,
harmed_by_heal = true, harmed_by_heal = true,
fire_damage_resistant = true,
} }
mobs:register_mob("mobs_mc:pigman", pigman) mobs:register_mob("mobs_mc:pigman", pigman)

View File

@ -65,7 +65,7 @@ minetest.register_globalstep(function(dtime)
if is_immortal or not enable_damage then if is_immortal or not enable_damage then
-- If damage is disabled, we can't kill players. -- If damage is disabled, we can't kill players.
-- So we just teleport the player back to spawn. -- 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) player:set_pos(spawn)
mcl_worlds.dimension_change(player, mcl_worlds.pos_to_dimension(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!")) minetest.chat_send_player(player:get_player_name(), S("The void is off-limits to you!"))

View File

@ -1,10 +1,10 @@
local modpath = minetest.get_modpath("mcl_weather"); local modpath = minetest.get_modpath("mcl_weather")
mcl_weather = {} mcl_weather = {}
-- If not located then embeded skycolor mod version will be loaded. -- If not located then embeded skycolor mod version will be loaded.
if minetest.get_modpath("skycolor") == nil then if minetest.get_modpath("skycolor") == nil then
dofile(modpath.."/skycolor.lua") dofile(modpath.."/skycolor.lua")
end end
dofile(modpath.."/weather_core.lua") dofile(modpath.."/weather_core.lua")
@ -13,5 +13,5 @@ dofile(modpath.."/rain.lua")
dofile(modpath.."/nether_dust.lua") dofile(modpath.."/nether_dust.lua")
if minetest.get_modpath("lightning") ~= nil then if minetest.get_modpath("lightning") ~= nil then
dofile(modpath.."/thunder.lua") dofile(modpath.."/thunder.lua")
end end

View File

@ -24,11 +24,8 @@ end
local timer = 0 local timer = 0
minetest.register_globalstep(function(dtime) minetest.register_globalstep(function(dtime)
timer = timer + dtime timer = timer + dtime
if timer >= 0.7 then if timer < 0.7 then return end
timer = 0 timer = 0
else
return
end
for _, player in ipairs(minetest.get_connected_players()) do for _, player in ipairs(minetest.get_connected_players()) do
if not mcl_worlds.has_dust(player:get_pos()) then if not mcl_worlds.has_dust(player:get_pos()) then

View File

@ -2,198 +2,198 @@ local PARTICLES_COUNT_RAIN = 30
local PARTICLES_COUNT_THUNDER = 45 local PARTICLES_COUNT_THUNDER = 45
mcl_weather.rain = { mcl_weather.rain = {
-- max rain particles created at time -- max rain particles created at time
particles_count = PARTICLES_COUNT_RAIN, particles_count = PARTICLES_COUNT_RAIN,
-- flag to turn on/off extinguish fire for rain -- flag to turn on/off extinguish fire for rain
extinguish_fire = true, extinguish_fire = true,
-- flag useful when mixing weathers -- flag useful when mixing weathers
raining = false, raining = false,
-- keeping last timeofday value (rounded). -- keeping last timeofday value (rounded).
-- Defaulted to non-existing value for initial comparing. -- Defaulted to non-existing value for initial comparing.
sky_last_update = -1, sky_last_update = -1,
init_done = false, init_done = false,
} }
mcl_weather.rain.sound_handler = function(player) mcl_weather.rain.sound_handler = function(player)
return minetest.sound_play("weather_rain", { return minetest.sound_play("weather_rain", {
to_player = player:get_player_name(), to_player = player:get_player_name(),
loop = true, loop = true,
}) })
end end
-- set skybox based on time (uses skycolor api) -- set skybox based on time (uses skycolor api)
mcl_weather.rain.set_sky_box = function() mcl_weather.rain.set_sky_box = function()
if mcl_weather.state == "rain" then if mcl_weather.state == "rain" then
mcl_weather.skycolor.add_layer( mcl_weather.skycolor.add_layer(
"weather-pack-rain-sky", "weather-pack-rain-sky",
{{r=0, g=0, b=0}, {{r=0, g=0, b=0},
{r=85, g=86, b=98}, {r=85, g=86, b=98},
{r=135, g=135, b=151}, {r=135, g=135, b=151},
{r=85, g=86, b=98}, {r=85, g=86, b=98},
{r=0, g=0, b=0}}) {r=0, g=0, b=0}})
mcl_weather.skycolor.active = true mcl_weather.skycolor.active = true
for _, player in pairs(minetest.get_connected_players()) do for _, player in pairs(minetest.get_connected_players()) do
player:set_clouds({color="#5D5D5FE8"}) player:set_clouds({color="#5D5D5FE8"})
end end
end end
end end
-- creating manually parctiles instead of particles spawner because of easier to control -- creating manually parctiles instead of particles spawner because of easier to control
-- spawn position. -- spawn position.
mcl_weather.rain.add_rain_particles = function(player) mcl_weather.rain.add_rain_particles = function(player)
mcl_weather.rain.last_rp_count = 0 mcl_weather.rain.last_rp_count = 0
for i=mcl_weather.rain.particles_count, 1,-1 do 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) 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 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 mcl_weather.rain.last_rp_count = mcl_weather.rain.last_rp_count + 1
minetest.add_particle({ minetest.add_particle({
pos = {x=random_pos_x, y=random_pos_y, z=random_pos_z}, pos = {x=random_pos_x, y=random_pos_y, z=random_pos_z},
velocity = {x=0, y=-10, z=0}, velocity = {x=0, y=-10, z=0},
acceleration = {x=0, y=-30, z=0}, acceleration = {x=0, y=-30, z=0},
expirationtime = 1.0, expirationtime = 1.0,
size = math.random(0.5, 3), size = math.random(0.5, 3),
collisiondetection = true, collisiondetection = true,
collision_removal = true, collision_removal = true,
vertical = true, vertical = true,
texture = mcl_weather.rain.get_texture(), texture = mcl_weather.rain.get_texture(),
playername = player:get_player_name() playername = player:get_player_name()
}) })
end end
end end
end end
-- Simple random texture getter -- Simple random texture getter
mcl_weather.rain.get_texture = function() mcl_weather.rain.get_texture = function()
local texture_name local texture_name
local random_number = math.random() local random_number = math.random()
if random_number > 0.33 then if random_number > 0.33 then
texture_name = "weather_pack_rain_raindrop_1.png" texture_name = "weather_pack_rain_raindrop_1.png"
elseif random_number > 0.66 then elseif random_number > 0.66 then
texture_name = "weather_pack_rain_raindrop_2.png" texture_name = "weather_pack_rain_raindrop_2.png"
else else
texture_name = "weather_pack_rain_raindrop_3.png" texture_name = "weather_pack_rain_raindrop_3.png"
end end
return texture_name; return texture_name;
end end
-- register player for rain weather. -- register player for rain weather.
-- basically needs for origin sky reference and rain sound controls. -- basically needs for origin sky reference and rain sound controls.
mcl_weather.rain.add_player = function(player) mcl_weather.rain.add_player = function(player)
if mcl_weather.players[player:get_player_name()] == nil then if mcl_weather.players[player:get_player_name()] == nil then
local player_meta = {} local player_meta = {}
player_meta.origin_sky = {player:get_sky()} player_meta.origin_sky = {player:get_sky()}
mcl_weather.players[player:get_player_name()] = player_meta mcl_weather.players[player:get_player_name()] = player_meta
end end
end end
-- remove player from player list effected by rain. -- remove player from player list effected by rain.
-- be sure to remove sound before removing player otherwise soundhandler reference will be lost. -- be sure to remove sound before removing player otherwise soundhandler reference will be lost.
mcl_weather.rain.remove_player = function(player) mcl_weather.rain.remove_player = function(player)
local player_meta = mcl_weather.players[player:get_player_name()] local player_meta = mcl_weather.players[player:get_player_name()]
if player_meta ~= nil and player_meta.origin_sky ~= nil then if player_meta ~= nil and player_meta.origin_sky ~= nil then
player:set_clouds({color="#FFF0F0E5"}) player:set_clouds({color="#FFF0F0E5"})
mcl_weather.players[player:get_player_name()] = nil mcl_weather.players[player:get_player_name()] = nil
end end
end end
mcl_worlds.register_on_dimension_change(function(player, dimension) mcl_worlds.register_on_dimension_change(function(player, dimension)
if dimension ~= "overworld" and dimension ~= "void" then if dimension ~= "overworld" and dimension ~= "void" then
mcl_weather.rain.remove_sound(player) mcl_weather.rain.remove_sound(player)
mcl_weather.rain.remove_player(player) mcl_weather.rain.remove_player(player)
elseif dimension == "overworld" then elseif dimension == "overworld" then
mcl_weather.rain.update_sound(player) mcl_weather.rain.update_sound(player)
if mcl_weather.rain.raining then if mcl_weather.rain.raining then
mcl_weather.rain.add_rain_particles(player) mcl_weather.rain.add_rain_particles(player)
mcl_weather.rain.add_player(player) mcl_weather.rain.add_player(player)
end end
end end
end) end)
-- adds and removes rain sound depending how much rain particles around player currently exist. -- 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 -- 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. -- when player stay on 'edge' where sound should play and stop depending from random raindrop appearance.
mcl_weather.rain.update_sound = function(player) mcl_weather.rain.update_sound = function(player)
local player_meta = mcl_weather.players[player:get_player_name()] local player_meta = mcl_weather.players[player:get_player_name()]
if player_meta ~= nil then if player_meta ~= nil then
if player_meta.sound_updated ~= nil and player_meta.sound_updated + 5 > minetest.get_gametime() then if player_meta.sound_updated ~= nil and player_meta.sound_updated + 5 > minetest.get_gametime() then
return false return false
end end
if player_meta.sound_handler ~= nil then if player_meta.sound_handler ~= nil then
if mcl_weather.rain.last_rp_count == 0 then if mcl_weather.rain.last_rp_count == 0 then
minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0) minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0)
player_meta.sound_handler = nil player_meta.sound_handler = nil
end end
elseif mcl_weather.rain.last_rp_count > 0 then elseif mcl_weather.rain.last_rp_count > 0 then
player_meta.sound_handler = mcl_weather.rain.sound_handler(player) player_meta.sound_handler = mcl_weather.rain.sound_handler(player)
end end
player_meta.sound_updated = minetest.get_gametime() player_meta.sound_updated = minetest.get_gametime()
end end
end end
-- rain sound removed from player. -- rain sound removed from player.
mcl_weather.rain.remove_sound = function(player) mcl_weather.rain.remove_sound = function(player)
local player_meta = mcl_weather.players[player:get_player_name()] local player_meta = mcl_weather.players[player:get_player_name()]
if player_meta ~= nil and player_meta.sound_handler ~= nil then if player_meta ~= nil and player_meta.sound_handler ~= nil then
minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0) minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0)
player_meta.sound_handler = nil player_meta.sound_handler = nil
player_meta.sound_updated = nil player_meta.sound_updated = nil
end end
end end
-- callback function for removing rain -- callback function for removing rain
mcl_weather.rain.clear = function() mcl_weather.rain.clear = function()
mcl_weather.rain.raining = false mcl_weather.rain.raining = false
mcl_weather.rain.sky_last_update = -1 mcl_weather.rain.sky_last_update = -1
mcl_weather.rain.init_done = false mcl_weather.rain.init_done = false
mcl_weather.rain.set_particles_mode("rain") mcl_weather.rain.set_particles_mode("rain")
mcl_weather.skycolor.remove_layer("weather-pack-rain-sky") mcl_weather.skycolor.remove_layer("weather-pack-rain-sky")
for _, player in ipairs(minetest.get_connected_players()) do for _, player in ipairs(minetest.get_connected_players()) do
mcl_weather.rain.remove_sound(player) mcl_weather.rain.remove_sound(player)
mcl_weather.rain.remove_player(player) mcl_weather.rain.remove_player(player)
end end
end end
minetest.register_globalstep(function(dtime) minetest.register_globalstep(function(dtime)
if mcl_weather.state ~= "rain" then if mcl_weather.state ~= "rain" then
return false return false
end end
mcl_weather.rain.make_weather() mcl_weather.rain.make_weather()
end) end)
mcl_weather.rain.make_weather = function() mcl_weather.rain.make_weather = function()
if mcl_weather.rain.init_done == false then if mcl_weather.rain.init_done == false then
mcl_weather.rain.raining = true mcl_weather.rain.raining = true
mcl_weather.rain.set_sky_box() mcl_weather.rain.set_sky_box()
mcl_weather.rain.set_particles_mode(mcl_weather.mode) mcl_weather.rain.set_particles_mode(mcl_weather.mode)
mcl_weather.rain.init_done = true mcl_weather.rain.init_done = true
end end
for _, player in ipairs(minetest.get_connected_players()) do 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 if (mcl_weather.is_underwater(player) or not mcl_worlds.has_weather(player:get_pos())) then
mcl_weather.rain.remove_sound(player) mcl_weather.rain.remove_sound(player)
return false return false
end end
mcl_weather.rain.add_player(player) mcl_weather.rain.add_player(player)
mcl_weather.rain.add_rain_particles(player) mcl_weather.rain.add_rain_particles(player)
mcl_weather.rain.update_sound(player) mcl_weather.rain.update_sound(player)
end end
end end
-- Switch the number of raindrops: "thunder" for many raindrops, otherwise for normal raindrops -- Switch the number of raindrops: "thunder" for many raindrops, otherwise for normal raindrops
mcl_weather.rain.set_particles_mode = function(mode) mcl_weather.rain.set_particles_mode = function(mode)
if mode == "thunder" then if mode == "thunder" then
mcl_weather.rain.particles_count = PARTICLES_COUNT_THUNDER mcl_weather.rain.particles_count = PARTICLES_COUNT_THUNDER
else else
mcl_weather.rain.particles_count = PARTICLES_COUNT_RAIN mcl_weather.rain.particles_count = PARTICLES_COUNT_RAIN
end end
end end
if mcl_weather.allow_abm then if mcl_weather.allow_abm then
@ -266,16 +266,16 @@ if mcl_weather.allow_abm then
end end
if mcl_weather.reg_weathers.rain == nil then if mcl_weather.reg_weathers.rain == nil then
mcl_weather.reg_weathers.rain = { mcl_weather.reg_weathers.rain = {
clear = mcl_weather.rain.clear, clear = mcl_weather.rain.clear,
light_factor = 0.6, light_factor = 0.6,
-- 10min - 20min -- 10min - 20min
min_duration = 600, min_duration = 600,
max_duration = 1200, max_duration = 1200,
transitions = { transitions = {
[65] = "none", [65] = "none",
[70] = "snow", [70] = "snow",
[100] = "thunder", [100] = "thunder",
} }
} }
end end

View File

@ -81,17 +81,16 @@ end)
-- register snow weather -- register snow weather
if mcl_weather.reg_weathers.snow == nil then if mcl_weather.reg_weathers.snow == nil then
mcl_weather.reg_weathers.snow = { mcl_weather.reg_weathers.snow = {
clear = mcl_weather.snow.clear, clear = mcl_weather.snow.clear,
light_factor = 0.6, light_factor = 0.6,
-- 10min - 20min -- 10min - 20min
min_duration = 600, min_duration = 600,
max_duration = 1200, max_duration = 1200,
transitions = { transitions = {
[65] = "none", [65] = "none",
[80] = "rain", [80] = "rain",
[100] = "thunder", [100] = "thunder",
} }
} }
end end

View File

@ -6,8 +6,8 @@ mcl_weather.state = "none"
-- player list for saving player meta info -- player list for saving player meta info
mcl_weather.players = {} mcl_weather.players = {}
-- default weather recalculation interval -- default weather check interval for global step
mcl_weather.check_interval = 300 mcl_weather.check_interval = 5
-- weather min duration -- weather min duration
mcl_weather.min_duration = 600 mcl_weather.min_duration = 600
@ -25,14 +25,14 @@ mcl_weather.reg_weathers = {}
mcl_weather.allow_abm = true mcl_weather.allow_abm = true
mcl_weather.reg_weathers["none"] = { mcl_weather.reg_weathers["none"] = {
min_duration = mcl_weather.min_duration, min_duration = mcl_weather.min_duration,
max_duration = mcl_weather.max_duration, max_duration = mcl_weather.max_duration,
light_factor = nil, light_factor = nil,
transitions = { transitions = {
[50] = "rain", [50] = "rain",
[100] = "snow", [100] = "snow",
}, },
clear = function() end, clear = function() end,
} }
local storage = minetest.get_mod_storage() local storage = minetest.get_mod_storage()
@ -45,211 +45,230 @@ end
minetest.register_on_shutdown(save_weather) minetest.register_on_shutdown(save_weather)
mcl_weather.get_rand_end_time = function(min_duration, max_duration) mcl_weather.get_rand_end_time = function(min_duration, max_duration)
local r local r
if min_duration ~= nil and max_duration ~= nil then if min_duration ~= nil and max_duration ~= nil then
r = math.random(min_duration, max_duration); r = math.random(min_duration, max_duration)
else else
r = math.random(mcl_weather.min_duration, mcl_weather.max_duration); r = math.random(mcl_weather.min_duration, mcl_weather.max_duration)
end end
return minetest.get_gametime() + r return minetest.get_gametime() + r
end end
mcl_weather.get_current_light_factor = function() mcl_weather.get_current_light_factor = function()
if mcl_weather.state == "none" then if mcl_weather.state == "none" then
return nil return nil
else else
return mcl_weather.reg_weathers[mcl_weather.state].light_factor return mcl_weather.reg_weathers[mcl_weather.state].light_factor
end end
end end
-- Returns true if pos is outdoor. -- Returns true if pos is outdoor.
-- Outdoor is defined as any node in the Overworld under open sky. -- 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. -- FIXME: Nodes below glass also count as “outdoor”, this should not be the case.
mcl_weather.is_outdoor = function(pos) mcl_weather.is_outdoor = function(pos)
local cpos = {x=pos.x, y=pos.y+1, z=pos.z} local cpos = {x=pos.x, y=pos.y+1, z=pos.z}
local dim = mcl_worlds.pos_to_dimension(cpos) local dim = mcl_worlds.pos_to_dimension(cpos)
if minetest.get_node_light(cpos, 0.5) == 15 and dim == "overworld" then if minetest.get_node_light(cpos, 0.5) == 15 and dim == "overworld" then
return true return true
end end
return false return false
end end
-- checks if player is undewater. This is needed in order to -- checks if player is undewater. This is needed in order to
-- turn off weather particles generation. -- turn off weather particles generation.
mcl_weather.is_underwater = function(player) mcl_weather.is_underwater = function(player)
local ppos = player:get_pos() local ppos = player:get_pos()
local offset = player:get_eye_offset() local offset = player:get_eye_offset()
local player_eye_pos = {x = ppos.x + offset.x, local player_eye_pos = {x = ppos.x + offset.x,
y = ppos.y + offset.y + 1.5, y = ppos.y + offset.y + 1.5,
z = ppos.z + offset.z} z = ppos.z + offset.z}
local node_level = minetest.get_node_level(player_eye_pos) local node_level = minetest.get_node_level(player_eye_pos)
if node_level == 8 or node_level == 7 then if node_level == 8 or node_level == 7 then
return true return true
end end
return false return false
end end
-- trying to locate position for particles by player look direction for performance reason. -- 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. -- 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) mcl_weather.get_random_pos_by_player_look_dir = function(player)
local look_dir = player:get_look_dir() local look_dir = player:get_look_dir()
local player_pos = player:get_pos() local player_pos = player:get_pos()
local random_pos_x = 0 local random_pos_x = 0
local random_pos_y = 0 local random_pos_y = 0
local random_pos_z = 0 local random_pos_z = 0
if look_dir.x > 0 then if look_dir.x > 0 then
if look_dir.z > 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_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) random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 5)
else else
random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 5) 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) random_pos_z = math.random() + math.random(player_pos.z - 5, player_pos.z + 2.5)
end end
else else
if look_dir.z > 0 then if look_dir.z > 0 then
random_pos_x = math.random() + math.random(player_pos.x - 5, player_pos.x + 2.5) 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) random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 5)
else else
random_pos_x = math.random() + math.random(player_pos.x - 5, player_pos.x + 2.5) 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) random_pos_z = math.random() + math.random(player_pos.z - 5, player_pos.z + 2.5)
end end
end end
random_pos_y = math.random() + math.random(player_pos.y + 10, player_pos.y + 15) 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 return random_pos_x, random_pos_y, random_pos_z
end end
local t, wci = 0, mcl_weather.check_interval
minetest.register_globalstep(function(dtime) minetest.register_globalstep(function(dtime)
if mcl_weather.end_time == nil then t = t + dtime
mcl_weather.end_time = mcl_weather.get_rand_end_time() if t < wci then return end
end t = 0
-- recalculate weather
if mcl_weather.end_time <= minetest.get_gametime() then if mcl_weather.end_time == nil then
local changeWeather = minetest.settings:get_bool("mcl_doWeatherCycle") mcl_weather.end_time = mcl_weather.get_rand_end_time()
if changeWeather == nil then end
changeWeather = true -- recalculate weather
end if mcl_weather.end_time <= minetest.get_gametime() then
if changeWeather then local changeWeather = minetest.settings:get_bool("mcl_doWeatherCycle")
mcl_weather.set_random_weather(mcl_weather.state, mcl_weather.reg_weathers[mcl_weather.state]) if changeWeather == nil then
else changeWeather = true
mcl_weather.end_time = mcl_weather.get_rand_end_time() end
end if changeWeather then
end 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) end)
-- Sets random weather (which could be 'none' (no weather)). -- Sets random weather (which could be 'none' (no weather)).
mcl_weather.set_random_weather = function(weather_name, weather_meta) mcl_weather.set_random_weather = function(weather_name, weather_meta)
if (weather_meta ~= nil) then if weather_meta == nil then return end
local transitions = weather_meta.transitions local transitions = weather_meta.transitions
local random_roll = math.random(0,100) local random_roll = math.random(0,100)
local new_weather local new_weather
for v, weather in pairs(transitions) do for v, weather in pairs(transitions) do
if random_roll < v then if random_roll < v then
new_weather = weather new_weather = weather
break break
end end
end end
if new_weather then if new_weather then
mcl_weather.change_weather(new_weather) mcl_weather.change_weather(new_weather)
end end
end
end end
-- Change weather to new_weather. -- Change weather to new_weather.
-- * explicit_end_time is OPTIONAL. If specified, explicitly set the -- * explicit_end_time is OPTIONAL. If specified, explicitly set the
-- gametime (minetest.get_gametime) in which the weather ends. -- gametime (minetest.get_gametime) in which the weather ends.
mcl_weather.change_weather = function(new_weather, explicit_end_time) -- * changer is OPTIONAL, for logging purposes.
if (mcl_weather.reg_weathers ~= nil and mcl_weather.reg_weathers[new_weather] ~= nil) then mcl_weather.change_weather = function(new_weather, explicit_end_time, changer_name)
if (mcl_weather.state ~= nil and mcl_weather.reg_weathers[mcl_weather.state] ~= nil) then local changer_name = changer_name or debug.getinfo(2).name.."()"
mcl_weather.reg_weathers[mcl_weather.state].clear()
end if (mcl_weather.reg_weathers ~= nil and mcl_weather.reg_weathers[new_weather] ~= nil) then
mcl_weather.state = new_weather if (mcl_weather.state ~= nil and mcl_weather.reg_weathers[mcl_weather.state] ~= nil) then
local weather_meta = mcl_weather.reg_weathers[mcl_weather.state] mcl_weather.reg_weathers[mcl_weather.state].clear()
if explicit_end_time then end
mcl_weather.end_time = explicit_end_time
else local old_weather = mcl_weather.state
mcl_weather.end_time = mcl_weather.get_rand_end_time(weather_meta.min_duration, weather_meta.max_duration)
end mcl_weather.state = new_weather
mcl_weather.skycolor.update_sky_color()
save_weather() if old_weather == "none" then
return true old_weather = "clear"
end end
return false 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 end
mcl_weather.get_weather = function() mcl_weather.get_weather = function()
return mcl_weather.state return mcl_weather.state
end end
minetest.register_privilege("weather_manager", { minetest.register_privilege("weather_manager", {
description = S("Gives ability to control weather"), description = S("Gives ability to control weather"),
give_to_singleplayer = false give_to_singleplayer = false
}) })
-- Weather command definition. Set -- Weather command definition. Set
minetest.register_chatcommand("weather", { minetest.register_chatcommand("weather", {
params = "(clear | rain | snow | thunder) [<duration>]", params = "(clear | rain | snow | thunder) [<duration>]",
description = S("Changes the weather to the specified parameter."), description = S("Changes the weather to the specified parameter."),
privs = {weather_manager = true}, privs = {weather_manager = true},
func = function(name, param) func = function(name, param)
if (param == "") then if (param == "") then
return false, S("Error: No weather specified.") return false, S("Error: No weather specified.")
end end
local new_weather, end_time local new_weather, end_time
local parse1, parse2 = string.match(param, "(%w+) ?(%d*)") local parse1, parse2 = string.match(param, "(%w+) ?(%d*)")
if parse1 then if parse1 then
if parse1 == "clear" then if parse1 == "clear" then
new_weather = "none" new_weather = "none"
else else
new_weather = parse1 new_weather = parse1
end end
else else
return false, S("Error: Invalid parameters.") return false, S("Error: Invalid parameters.")
end end
if parse2 then if parse2 then
if type(tonumber(parse2)) == "number" then if type(tonumber(parse2)) == "number" then
local duration = tonumber(parse2) local duration = tonumber(parse2)
if duration < 1 then if duration < 1 then
return false, S("Error: Duration can't be less than 1 second.") return false, S("Error: Duration can't be less than 1 second.")
end end
end_time = minetest.get_gametime() + duration end_time = minetest.get_gametime() + duration
end end
end end
local success = mcl_weather.change_weather(new_weather, end_time) local success = mcl_weather.change_weather(new_weather, end_time, name)
if success then if success then
return true return true
else else
return false, S("Error: Invalid weather specified. Use “clear”, “rain”, “snow” or “thunder”.") return false, S("Error: Invalid weather specified. Use “clear”, “rain”, “snow” or “thunder”.")
end end
end end
}) })
minetest.register_chatcommand("toggledownfall", { minetest.register_chatcommand("toggledownfall", {
params = "", params = "",
description = S("Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)"), description = S("Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)"),
privs = {weather_manager = true}, privs = {weather_manager = true},
func = function(name, param) func = function(name, param)
-- Currently rain/thunder/snow: Set weather to clear -- Currently rain/thunder/snow: Set weather to clear
if mcl_weather.state ~= "none" then if mcl_weather.state ~= "none" then
return mcl_weather.change_weather("none") return mcl_weather.change_weather("none", nil, name)
-- Currently clear: Set weather randomly to rain/thunder/snow -- Currently clear: Set weather randomly to rain/thunder/snow
else else
local new = { "rain", "thunder", "snow" } local new = { "rain", "thunder", "snow" }
local r = math.random(1, #new) local r = math.random(1, #new)
return mcl_weather.change_weather(new[r]) return mcl_weather.change_weather(new[r], nil, name)
end end
end end
}) })
-- Configuration setting which allows user to disable ABM for weathers (if they use it). -- 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. -- Weather mods expected to be use this flag before registering ABM.
local weather_allow_abm = minetest.settings:get_bool("weather_allow_abm") local weather_allow_abm = minetest.settings:get_bool("weather_allow_abm")
if weather_allow_abm ~= nil and weather_allow_abm == false then if weather_allow_abm ~= nil and weather_allow_abm == false then
mcl_weather.allow_abm = false mcl_weather.allow_abm = false
end end

View File

@ -1109,7 +1109,13 @@ if progressive_mode then
local name = player:get_player_name() local name = player:get_player_name()
local data = player_data[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 end
M.register_on_leaveplayer(function(player) M.register_on_leaveplayer(function(player)

View File

@ -77,6 +77,8 @@ hud_manager.add_hud = function(player,hud_name,def)
size = def.size, size = def.size,
offset = def.offset, offset = def.offset,
z_index = def.z_index, z_index = def.z_index,
alignment = def.alignment,
scale = def.scale,
}) })
-- create new 3d array here -- create new 3d array here
-- depends.txt is not needed -- depends.txt is not needed
@ -148,7 +150,7 @@ function mcl_experience.set_player_xp_level(player,level)
return return
end end
pool[name].level = level 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)}) hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(level)})
-- we may don't update the bar -- we may don't update the bar
end end
@ -161,27 +163,27 @@ minetest.register_on_joinplayer(function(player)
name = player:get_player_name() name = player:get_player_name()
temp_pool = pool[name] temp_pool = pool[name]
hud_manager.add_hud(player,"experience_bar", hud_manager.add_hud(player,"experience_bar",
{ {
hud_elem_type = "statbar", position = {x=0.5, y=1}, hud_elem_type = "image",
name = "experience bar", name = "experience bar",
text = "experience_bar.png", text = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",
text2 = "experience_bar_background.png", position = {x=0.5, y=1},
number = temp_pool.bar, item = 36, offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)},
direction = 0, scale = {x = 2.8, y = 3.0},
offset = {x = (-8 * 28) - 29, y = -(48 + 24 + 16)}, alignment = { x = 1, y = 1 },
size = { x=28, y=28 }, z_index = 11, z_index = 11,
}) })
hud_manager.add_hud(player,"xp_level", hud_manager.add_hud(player,"xp_level",
{ {
hud_elem_type = "text", position = {x=0.5, y=1}, hud_elem_type = "text", position = {x=0.5, y=1},
name = "xp_level", text = tostring(temp_pool.level), name = "xp_level", text = tostring(temp_pool.level),
number = 0xFFFFFF, number = 0x80FF20,
offset = {x = 0, y = -(48 + 24 + 24)}, offset = {x = 0, y = -(48 + 24 + 24)},
z_index = 12, z_index = 12,
}) })
end) end)
function mcl_experience.xp_to_level(xp) function mcl_experience.xp_to_level(xp)
@ -247,7 +249,7 @@ function mcl_experience.add_experience(player, experience)
end end
if old_bar ~= temp_pool.bar then 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 end
if experience > 0 and minetest.get_us_time()/1000000 - temp_pool.last_time > 0.01 then 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() name = player:get_player_name()
temp_pool = pool[name] temp_pool = pool[name]
xp_amount = temp_pool.xp xp_amount = temp_pool.xp
temp_pool.xp = 0 temp_pool.xp = 0
temp_pool.level = 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) 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 = "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) mcl_experience.throw_experience(player:get_pos(), xp_amount)
end) end)
@ -304,17 +306,17 @@ local function xp_step(self, dtime)
return return
end end
collector = minetest.get_player_by_name(self.collector) 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.object:set_acceleration(vector.new(0,0,0))
self.disable_physics(self) self.disable_physics(self)
--get the variables --get the variables
pos = self.object:get_pos() pos = self.object:get_pos()
pos2 = collector: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 pos2.y = pos2.y + 0.8
direction = vector.direction(pos,pos2) direction = vector.direction(pos,pos2)
distance = vector.distance(pos2,pos) distance = vector.distance(pos2,pos)
multiplier = distance multiplier = distance
@ -330,8 +332,79 @@ local function xp_step(self, dtime)
goal = velocity goal = velocity
acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z) acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
self.object:add_velocity(vector.add(acceleration,player_velocity)) self.object:add_velocity(vector.add(acceleration,player_velocity))
elseif distance < 0.4 then elseif distance < 0.8 then
mcl_experience.add_experience(collector, self._xp) 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() self.object:remove()
end end
return return
@ -341,7 +414,7 @@ local function xp_step(self, dtime)
end end
end end
self.age = self.age + dtime self.age = self.age + dtime
if self.age > max_orb_age then if self.age > max_orb_age then
self.object:remove() self.object:remove()
@ -526,11 +599,55 @@ function mcl_experience.throw_experience(pos, amount)
return false return false
end end
obj:set_velocity({ obj:set_velocity({
x=math.random(-2,2)*math.random(), x=math.random(-2,2)*math.random(),
y=math.random(2,5), y=math.random(2,5),
z=math.random(-2,2)*math.random() z=math.random(-2,2)*math.random()
}) })
i = i + xp i = i + xp
j = j + 1 j = j + 1
end end
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
})

View File

@ -0,0 +1,7 @@
# textdomain: mcl_experience
[[<player>] <xp>]=[[<Spieler>] <EP>]
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

View File

@ -1,3 +1,4 @@
# textdomain: mcl_experience
[[<player>] <xp>]=[[<игрок>] <xp>] [[<player>] <xp>]=[[<игрок>] <xp>]
Gives a player some XP=Даёт игроку XP Gives a player some XP=Даёт игроку XP
Error: Too many parameters!=Ошибка: слишком много параметров! Error: Too many parameters!=Ошибка: слишком много параметров!

View File

@ -1,3 +1,4 @@
# textdomain: mcl_experience
[[<player>] <xp>]=[[<joueur>] <xp>] [[<player>] <xp>]=[[<joueur>] <xp>]
Gives a player some XP=Donne de l'XP à un joueur Gives a player some XP=Donne de l'XP à un joueur
Error: Too many parameters!=Erreur: Trop de paramètres! Error: Too many parameters!=Erreur: Trop de paramètres!

View File

@ -1,7 +1,7 @@
# textdomain: mcl_experience
[[<player>] <xp>]= [[<player>] <xp>]=
Gives a player some XP= Gives a player some XP=
Error: Too many parameters!= Error: Too many parameters!=
Error: Incorrect value of XP= Error: Incorrect value of XP=
Error: Player not found= Error: Player not found=
Added @1 XP to @2, total: @3, experience level: @4= Added @1 XP to @2, total: @3, experience level: @4=
XP are disabled!=

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

View File

@ -16,6 +16,18 @@ for _, f in pairs(builtin_filter_ids) do
inventory_lists[f] = {} inventory_lists[f] = {}
end 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 --[[ 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 loaded after _mcl_autogroup for this to work, because it required certain
groups to be set. ]] groups to be set. ]]
@ -82,11 +94,33 @@ do
end end
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 for _, to_sort in pairs(inventory_lists) do
table.sort(to_sort) table.sort(to_sort)
replace_enchanted_books(to_sort)
end end
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 function set_inv_search(filter, player)
local playername = player:get_player_name() local playername = player:get_player_name()
local inv = minetest.get_inventory({type="detached", name="creative_"..playername}) 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 local lang = minetest.get_player_information(playername).lang_code
for name,def in pairs(minetest.registered_items) do 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 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) if filter_item(string.lower(def.name), def.description, lang, filter) then
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
table.insert(creative_list, name) table.insert(creative_list, name)
end end
end 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) table.sort(creative_list)
replace_enchanted_books(creative_list)
inv:set_size("main", #creative_list) inv:set_size("main", #creative_list)
inv:set_list("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) function minetest.handle_node_drops(pos, drops, digger)
if not digger or not digger:is_player() then if not digger or not digger:is_player() then
return for _,item in ipairs(drops) do
minetest.add_item(pos, item)
end
end end
local inv = digger:get_inventory() local inv = digger:get_inventory()
if inv then if inv then

View File

@ -141,7 +141,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
return_fields(player,"craft") return_fields(player,"craft")
return_fields(player,"enchanting_lapis") return_fields(player,"enchanting_lapis")
return_fields(player,"enchanting_item") 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 if not minetest.is_creative_enabled(player:get_player_name()) and (formname == "" or formname == "main") then
set_inventory(player) set_inventory(player)
end end

View File

@ -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)

View File

@ -24,7 +24,7 @@ local rules_up = {{ x = 0, y = -1, z = 0, spread = true }}
function mcl_observers.observer_activate(pos) function mcl_observers.observer_activate(pos)
minetest.after(mcl_vars.redstone_tick, function(pos) minetest.after(mcl_vars.redstone_tick, function(pos)
node = minetest.get_node(pos) local node = minetest.get_node(pos)
if not node then if not node then
return return
end end
@ -284,6 +284,7 @@ if realtime then
mcl_observers.set_node = minetest.set_node mcl_observers.set_node = minetest.set_node
mcl_observers.swap_node = minetest.swap_node mcl_observers.swap_node = minetest.swap_node
mcl_observers.remove_node = minetest.remove_node mcl_observers.remove_node = minetest.remove_node
mcl_observers.bulk_set_node = minetest.bulk_set_node
minetest.add_node=function(pos,node) minetest.add_node=function(pos,node)
mcl_observers.add_node(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}) mcl_observers.observer_activate({x=pos.x,y=pos.y+1,z=pos.z})
end end
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: else -- if realtime then ^^^ else:
minetest.register_abm({ minetest.register_abm({

View File

@ -145,7 +145,7 @@ function mesecon.mvps_get_stack(pos, dir, maximum, piston_pos)
if not node_replaceable(nn.name) then if not node_replaceable(nn.name) then
if #nodes >= maximum then return nil, false end 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 -- add connected nodes to frontiers, connected is a vector list
-- the vectors must be absolute positions -- the vectors must be absolute positions
@ -195,10 +195,9 @@ function mesecon.mvps_set_owner(pos, placer)
end end
end end
local function are_protected(positions, player_name) local function are_protected(nodes, player_name)
local name = player_name for _, node in pairs(nodes) do
for _, pos in pairs(positions) do if minetest.is_protected(node.pos, player_name) then
if is_protected(pos, name) then
return true return true
end end
end end

View File

@ -97,7 +97,7 @@ local piston_on = function (pos, node)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local success, stack, oldstack = mesecon.mvps_push(np, dir, PISTON_MAXIMUM_PUSH, meta:get_string("owner"), pos) local success, stack, oldstack = mesecon.mvps_push(np, dir, PISTON_MAXIMUM_PUSH, meta:get_string("owner"), pos)
if success then 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}) 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}) 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 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 piston_off = function (pos, node)
local pistonspec = minetest.registered_nodes[node.name].mesecons_piston 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) piston_remove_pusher (pos, node)
if not pistonspec.sticky then if not pistonspec.sticky then
return return

View File

@ -554,7 +554,7 @@ minetest.register_on_player_hpchange(function(player, hp_change, reason)
epf = epf + blast_protection_level * 2 epf = epf + blast_protection_level * 2
end end
local fire_protection_level = enchantments.fire_protection or 0 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 (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 epf = epf + fire_protection_level * 2
end end

View File

@ -223,7 +223,7 @@ minetest.register_node("mcl_armor_stand:armor_stand", {
after_destruct = function(pos) after_destruct = function(pos)
update_entity(pos) update_entity(pos)
end, end,
on_blast = function(pos) on_blast = function(pos, _, do_drop)
local object = get_stand_object(pos) local object = get_stand_object(pos)
if object then if object then
object:remove() object:remove()
@ -231,6 +231,10 @@ minetest.register_node("mcl_armor_stand:armor_stand", {
minetest.after(1, function(pos) minetest.after(1, function(pos)
update_entity(pos) update_entity(pos)
end, pos) end, pos)
minetest.remove_node(pos)
if do_drop then
minetest.add_item(pos, "mcl_armor_stand:armor_stand")
end
end, end,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_FACE then if mode == screwdriver.ROTATE_FACE then

View File

@ -1,23 +1,24 @@
local S = minetest.get_translator("mcl_beds") local S = minetest.get_translator("mcl_beds")
local reverse = true local function destruct_bed(pos, oldnode)
local node = oldnode or minetest.get_node(pos)
local function destruct_bed(pos, is_top) if not node then return end
local node = minetest.get_node(pos)
local other
local dir = minetest.facedir_to_dir(node.param2) local dir = minetest.facedir_to_dir(node.param2)
if is_top then local pos2, node2
other = vector.subtract(pos, dir) if string.sub(node.name, -4) == "_top" then
else pos2 = vector.subtract(pos, dir)
other = vector.add(pos, dir) node2 = minetest.get_node(pos2)
end if node2 and string.sub(node2.name, -7) == "_bottom" then
minetest.remove_node(pos2)
if reverse then end
reverse = not reverse minetest.check_for_falling(pos)
minetest.remove_node(other) elseif string.sub(node.name, -7) == "_bottom" then
minetest.check_for_falling(other) minetest.add_item(pos, node.name)
else pos2 = vector.add(pos, dir)
reverse = not reverse node2 = minetest.get_node(pos2)
if node2 and string.sub(node2.name, -4) == "_top" then
minetest.remove_node(pos2)
end
end end
end end
@ -86,6 +87,7 @@ function mcl_beds.register_bed(name, def)
node_box = node_box_bottom, node_box = node_box_bottom,
selection_box = selection_box_bottom, selection_box = selection_box_bottom,
collision_box = collision_box_bottom, collision_box = collision_box_bottom,
drop = "",
on_place = function(itemstack, placer, pointed_thing) on_place = function(itemstack, placer, pointed_thing)
local under = pointed_thing.under local under = pointed_thing.under
@ -139,10 +141,9 @@ function mcl_beds.register_bed(name, def)
return itemstack return itemstack
end, end,
on_destruct = function(pos) after_destruct = destruct_bed,
destruct_bed(pos, false)
kick_player_after_destruct(pos) on_destruct = kick_player_after_destruct,
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
mcl_beds.on_rightclick(pos, clicker, false) mcl_beds.on_rightclick(pos, clicker, false)
@ -205,7 +206,7 @@ function mcl_beds.register_bed(name, def)
_mcl_hardness = 0.2, _mcl_hardness = 0.2,
_mcl_blast_resistance = 1, _mcl_blast_resistance = 1,
sounds = def.sounds or default_sounds, sounds = def.sounds or default_sounds,
drop = name .. "_bottom", drop = "",
node_box = node_box_top, node_box = node_box_top,
selection_box = selection_box_top, selection_box = selection_box_top,
collision_box = collision_box_top, collision_box = collision_box_top,
@ -214,10 +215,7 @@ function mcl_beds.register_bed(name, def)
return itemstack return itemstack
end, end,
on_rotate = false, on_rotate = false,
on_destruct = function(pos) after_destruct = destruct_bed,
destruct_bed(pos, true)
kick_player_after_destruct(pos)
end,
}) })
minetest.register_alias(name, name .. "_bottom") minetest.register_alias(name, name .. "_bottom")

View File

@ -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_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} 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 -- No sleeping if too far away
if vector.distance(bed_pos, pos) > 2 and vector.distance(bed_pos2, pos) > 2 then 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, S("You can't sleep, the bed's too far away!")
return false
end end
for _, other_pos in pairs(mcl_beds.bed_pos) do for _, other_pos in pairs(mcl_beds.bed_pos) do
if vector.distance(bed_pos, other_pos) < 0.1 then if vector.distance(bed_pos, other_pos) < 0.1 then
minetest.chat_send_player(name, S("This bed is already occupied!")) return false, S("This bed is already occupied!")
return false
end end
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 -- FIXME: Velocity threshold should be 0.01 but Minetest 5.3.0
-- sometimes reports incorrect Y speed. A velocity threshold -- sometimes reports incorrect Y speed. A velocity threshold
-- of 0.125 still seems good enough. -- of 0.125 still seems good enough.
if vector.length(player:get_player_velocity()) > 0.125 then if vector.length(player:get_velocity() or player:get_player_velocity()) > 0.125 then
minetest.chat_send_player(name, S("You have to stop moving before going to bed!")) return false, S("You have to stop moving before going to bed!")
return false
end end
-- No sleeping if monsters nearby. -- No sleeping if monsters nearby.
@ -109,9 +111,8 @@ local function lay_down(player, pos, bed_pos, state, skip)
-- Approximation of monster detection range -- 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 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 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 end
return false
end end
end 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 def1 = minetest.registered_nodes[n1.name]
local def2 = minetest.registered_nodes[n2.name] local def2 = minetest.registered_nodes[n2.name]
if def1.walkable or def2.walkable then if def1.walkable or def2.walkable then
minetest.chat_send_player(name, S("You can't sleep, the bed is obstructed!")) return false, S("You can't sleep, the bed is obstructed!")
return false
elseif (def1.damage_per_second ~= nil and def1.damage_per_second > 0) or (def2.damage_per_second ~= nil and def2.damage_per_second > 0) then 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, 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)
end end
-- Check day of time and weather -- Check day of time and weather
local tod = minetest.get_timeofday() * 24000 local tod = minetest.get_timeofday() * 24000
-- Values taken from Minecraft Wiki with offset of +6000 -- 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 tod < 18541 and tod > 5458 and (not weather_mod or (mcl_weather.get_weather() ~= "thunder")) then
if spawn_changed then return false, S("You can only sleep at night or during a thunderstorm.")
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!"))
end end
mcl_beds.player[name] = 1 mcl_beds.player[name] = 1
@ -329,13 +314,17 @@ function mcl_beds.on_rightclick(pos, player, is_top)
-- move to bed -- move to bed
if not mcl_beds.player[name] then if not mcl_beds.player[name] then
local success, message
if is_top then if is_top then
lay_down(player, ppos, pos) success, message = lay_down(player, ppos, pos)
else else
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
local dir = minetest.facedir_to_dir(node.param2) local dir = minetest.facedir_to_dir(node.param2)
local other = vector.add(pos, dir) 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 end
else else
lay_down(player, nil, nil, false) lay_down(player, nil, nil, false)
@ -362,15 +351,15 @@ minetest.register_on_joinplayer(function(player)
-- Make player awake on joining server -- Make player awake on joining server
meta:set_string("mcl_beds:sleeping", "false") meta:set_string("mcl_beds:sleeping", "false")
end end
playerphysics.remove_physics_factor(player, "speed", "mcl_beds:sleeping") playerphysics.remove_physics_factor(player, "speed", "mcl_beds:sleeping")
playerphysics.remove_physics_factor(player, "jump", "mcl_beds:sleeping") playerphysics.remove_physics_factor(player, "jump", "mcl_beds:sleeping")
update_formspecs(false) update_formspecs(false)
end) end)
minetest.register_on_leaveplayer(function(player) 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 players = minetest.get_connected_players()
local name = player:get_player_name()
for n, player in ipairs(players) do for n, player in ipairs(players) do
if player:get_player_name() == name then if player:get_player_name() == name then
players[n] = nil players[n] = nil

View File

@ -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."), 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."), _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", 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) _on_dispense = function(itemstack, dispenserpos, droppos, dropnode, dropdir)
-- Shoot arrow -- Shoot arrow
local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51)) local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51))
@ -83,6 +83,7 @@ local ARROW_ENTITY={
textures = {"mcl_bows:arrow_box"}, textures = {"mcl_bows:arrow_box"},
collisionbox = {-0.19, -0.125, -0.19, 0.19, 0.125, 0.19}, collisionbox = {-0.19, -0.125, -0.19, 0.19, 0.125, 0.19},
collide_with_objects = false, collide_with_objects = false,
_fire_damage_resistant = true,
_lastpos={}, _lastpos={},
_startpos=nil, _startpos=nil,
@ -105,6 +106,7 @@ local spawn_item = function(self, pos)
item:set_velocity({x=0, y=0, z=0}) item:set_velocity({x=0, y=0, z=0})
item:set_yaw(self.object:get_yaw()) item:set_yaw(self.object:get_yaw())
end end
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
end end
@ -131,6 +133,8 @@ local damage_particles = function(pos, is_critical)
end end
ARROW_ENTITY.on_step = function(self, dtime) ARROW_ENTITY.on_step = function(self, dtime)
mcl_burning.tick(self.object, dtime)
local pos = self.object:get_pos() local pos = self.object:get_pos()
local dpos = table.copy(pos) -- digital pos local dpos = table.copy(pos) -- digital pos
dpos = vector.round(dpos) dpos = vector.round(dpos)
@ -140,6 +144,7 @@ ARROW_ENTITY.on_step = function(self, dtime)
self._stucktimer = self._stucktimer + dtime self._stucktimer = self._stucktimer + dtime
self._stuckrechecktimer = self._stuckrechecktimer + dtime self._stuckrechecktimer = self._stuckrechecktimer + dtime
if self._stucktimer > ARROW_TIMEOUT then if self._stucktimer > ARROW_TIMEOUT then
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return return
end end
@ -161,7 +166,7 @@ ARROW_ENTITY.on_step = function(self, dtime)
local objects = minetest.get_objects_inside_radius(pos, 1) local objects = minetest.get_objects_inside_radius(pos, 1)
for _,obj in ipairs(objects) do for _,obj in ipairs(objects) do
if obj:is_player() then 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 if obj:get_inventory():room_for_item("main", "mcl_bows:arrow") then
obj:get_inventory():add_item("main", "mcl_bows:arrow") obj:get_inventory():add_item("main", "mcl_bows:arrow")
minetest.sound_play("item_drop_pickup", { minetest.sound_play("item_drop_pickup", {
@ -171,6 +176,7 @@ ARROW_ENTITY.on_step = function(self, dtime)
}, true) }, true)
end end
end end
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return return
end end
@ -232,6 +238,7 @@ ARROW_ENTITY.on_step = function(self, dtime)
local def = minetest.registered_nodes[nn] local def = minetest.registered_nodes[nn]
if (not def) or def.walkable then if (not def) or def.walkable then
-- There's a node in the way. Delete arrow without damage -- There's a node in the way. Delete arrow without damage
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return return
end end
@ -244,6 +251,9 @@ ARROW_ENTITY.on_step = function(self, dtime)
armor.last_damage_types[obj:get_player_name()] = "projectile" armor.last_damage_types[obj:get_player_name()] = "projectile"
end end
damage_particles(self.object:get_pos(), self._is_critical) 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, { obj:punch(self.object, 1.0, {
full_punch_interval=1.0, full_punch_interval=1.0,
damage_groups={fleshy=self._damage}, damage_groups={fleshy=self._damage},
@ -271,6 +281,7 @@ ARROW_ENTITY.on_step = function(self, dtime)
end end
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true) minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
end end
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return return
end end
@ -403,6 +414,7 @@ ARROW_ENTITY.on_activate = function(self, staticdata, dtime_s)
-- If yes, delete it. -- If yes, delete it.
self._stucktimer = minetest.get_gametime() - data.stuckstarttime self._stucktimer = minetest.get_gametime() - data.stuckstarttime
if self._stucktimer > ARROW_TIMEOUT then if self._stucktimer > ARROW_TIMEOUT then
mcl_burning.extinguish(self.object)
self.object:remove() self.object:remove()
return return
end end

View File

@ -33,7 +33,7 @@ local bow_load = {}
-- Another player table, this one stores the wield index of the bow being charged -- Another player table, this one stores the wield index of the bow being charged
local bow_index = {} 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") local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, arrow_item.."_entity")
if power == nil then if power == nil then
power = BOW_MAX_SPEED --19 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 if enchantments.punch then
knockback = enchantments.punch * 3 knockback = enchantments.punch * 3
end end
if enchantments.flame then
mcl_burning.set_on_fire(obj, math.huge)
end
end end
obj:set_velocity({x=dir.x*power, y=dir.y*power, z=dir.z*power}) obj:set_velocity({x=dir.x*power, y=dir.y*power, z=dir.z*power})
obj:set_acceleration({x=0, y=-GRAVITY, z=0}) 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._is_critical = is_critical
le._startpos = pos le._startpos = pos
le._knockback = knockback le._knockback = knockback
le._collectable = collectable
minetest.sound_play("mcl_bows_bow_shoot", {pos=pos, max_hear_distance=16}, true) minetest.sound_play("mcl_bows_bow_shoot", {pos=pos, max_hear_distance=16}, true)
if shooter ~= nil and shooter:is_player() then if shooter ~= nil and shooter:is_player() then
if obj:get_luaentity().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_stack, arrow_stack_id = get_arrow(player)
local arrow_itemstring local arrow_itemstring
local has_infinity_enchantment = mcl_enchanting.has_enchantment(player:get_wielded_item(), "infinity") 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 minetest.is_creative_enabled(player:get_player_name()) then
if arrow_stack then if arrow_stack then
@ -100,7 +105,9 @@ local player_shoot_arrow = function(itemstack, player, power, damage, is_critica
return false return false
end end
arrow_itemstring = arrow_stack:get_name() 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() arrow_stack:take_item()
end end
local inv = player:get_inventory() 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 dir = player:get_look_dir()
local yaw = player:get_look_horizontal() 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 return true
end end
@ -131,7 +138,26 @@ S("The speed and damage of the arrow increases the longer you charge. The regula
range = 4, range = 4,
-- Trick to disable digging as well -- Trick to disable digging as well
on_use = function() return end, 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}, 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 -- 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 inv = player:get_inventory()
local list = inv:get_list("main") local list = inv:get_list("main")
for place, stack in pairs(list) do 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:set_name("mcl_bows:bow")
stack:get_meta():set_string("active", "")
list[place] = stack 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 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:set_name("mcl_bows:bow_enchanted")
stack:get_meta():set_string("active", "")
list[place] = stack list[place] = stack
end end
end end
@ -176,6 +206,7 @@ for level=0, 2 do
on_use = function() return end, on_use = function() return end,
on_drop = function(itemstack, dropper, pos) on_drop = function(itemstack, dropper, pos)
reset_bow_state(dropper) reset_bow_state(dropper)
itemstack:get_meta():set_string("active", "")
if mcl_enchanting.is_enchanted(itemstack:get_name()) then if mcl_enchanting.is_enchanted(itemstack:get_name()) then
itemstack:set_name("mcl_bows:bow_enchanted") itemstack:set_name("mcl_bows:bow_enchanted")
else else
@ -189,6 +220,7 @@ for level=0, 2 do
on_place = function(itemstack) on_place = function(itemstack)
return itemstack return itemstack
end, end,
_mcl_uses = 385,
}) })
end 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 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 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 has_shot = false
local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name()) local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name())
local speed, damage local speed, damage
local p_load = bow_load[player:get_player_name()] local p_load = bow_load[player:get_player_name()]
@ -240,13 +272,13 @@ controls.register_on_release(function(player, key, time)
end end
has_shot = player_shoot_arrow(wielditem, player, speed, damage, is_critical) has_shot = player_shoot_arrow(wielditem, player, speed, damage, is_critical)
if enchanted then if enchanted then
wielditem:set_name("mcl_bows:bow_enchanted") wielditem:set_name("mcl_bows:bow_enchanted")
else else
wielditem:set_name("mcl_bows:bow") wielditem:set_name("mcl_bows:bow")
end end
if has_shot and not minetest.is_creative_enabled(player:get_player_name()) then if has_shot and not minetest.is_creative_enabled(player:get_player_name()) then
local durability = BOW_DURABILITY local durability = BOW_DURABILITY
local unbreaking = mcl_enchanting.get_enchantment(wielditem, "unbreaking") local unbreaking = mcl_enchanting.get_enchantment(wielditem, "unbreaking")
@ -268,7 +300,7 @@ controls.register_on_hold(function(player, key, time)
end end
local inv = minetest.get_inventory({type="player", name=name}) local inv = minetest.get_inventory({type="player", name=name})
local wielditem = player:get_wielded_item() 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()) local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name())
if enchanted then if enchanted then
wielditem:set_name("mcl_bows:bow_0_enchanted") wielditem:set_name("mcl_bows:bow_0_enchanted")

View File

@ -82,7 +82,7 @@ local register_filled_cauldron = function(water_level, description, river_water)
drawtype = "nodebox", drawtype = "nodebox",
paramtype = "light", paramtype = "light",
is_ground_content = false, 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], node_box = cauldron_nodeboxes[water_level],
collision_box = cauldron_nodeboxes[0], collision_box = cauldron_nodeboxes[0],
selection_box = { type = "regular" }, selection_box = { type = "regular" },
@ -122,3 +122,20 @@ minetest.register_craft({
{ "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" }, { "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
})

View File

@ -1,10 +1,162 @@
local S = minetest.get_translator("mcl_chests") local S = minetest.get_translator("mcl_chests")
local mod_doc = minetest.get_modpath("doc") 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 local no_rotate, simple_rotate
if minetest.get_modpath("screwdriver") then if minetest.get_modpath("screwdriver") then
no_rotate = screwdriver.disallow 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 end
--[[ List of open chests. --[[ List of open chests.
@ -13,9 +165,23 @@ Value:
If player is using a chest: { pos = <chest node position> } If player is using a chest: { pos = <chest node position> }
Otherwise: nil ]] Otherwise: nil ]]
local open_chests = {} 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 -- To be called if a player opened a chest
local player_chest_open = function(player, pos) local player_chest_open = function(player, pos, node_name, textures, param2, double, sound, mesh, shulker)
open_chests[player:get_player_name()] = { pos = pos } 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 end
-- Simple protection checking functions -- Simple protection checking functions
@ -44,11 +210,13 @@ local trapped_chest_mesecons_rules = mesecon.rules.pplate
local chest_update_after_close = function(pos) local chest_update_after_close = function(pos)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if node.name == "mcl_chests:trapped_chest_on" then if node.name == "mcl_chests:trapped_chest_on_small" then
minetest.swap_node(pos, {name="mcl_chests:trapped_chest", param2 = node.param2}) 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) mesecon.receptor_off(pos, trapped_chest_mesecons_rules)
elseif node.name == "mcl_chests:trapped_chest_on_left" then elseif node.name == "mcl_chests:trapped_chest_on_left" then
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) 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) mesecon.receptor_off(pos, trapped_chest_mesecons_rules)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") 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") 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}) 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) mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules)
end end
end end
@ -67,11 +236,14 @@ end
-- To be called if a player closed a chest -- To be called if a player closed a chest
local player_chest_close = function(player) local player_chest_close = function(player)
local name = player:get_player_name() local name = player:get_player_name()
if open_chests[name] == nil then local open_chest = open_chests[name]
if open_chest == nil then
return return
end end
local pos = open_chests[name].pos if animate_chests then
chest_update_after_close(pos) 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 open_chests[name] = nil
end end
@ -146,18 +318,82 @@ local on_chest_blast = function(pos)
minetest.remove_node(pos) minetest.remove_node(pos)
end 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, { minetest.register_node("mcl_chests:"..basename, {
description = desc, description = desc,
_tt_help = tt_help, _tt_help = tt_help,
_doc_items_longdesc = longdesc, _doc_items_longdesc = longdesc,
_doc_items_usagehelp = usagehelp, _doc_items_usagehelp = usagehelp,
_doc_items_hidden = hidden, _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", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
stack_max = 64, stack_max = 64,
drop = drop, 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, is_ground_content = false,
sounds = mcl_sounds.node_sound_wood_defaults(), sounds = mcl_sounds.node_sound_wood_defaults(),
on_construct = function(pos) on_construct = function(pos)
@ -185,16 +421,19 @@ minetest.register_node("mcl_chests:"..basename, {
-- BEGIN OF LISTRING WORKAROUND -- BEGIN OF LISTRING WORKAROUND
inv:set_size("input", 1) inv:set_size("input", 1)
-- END OF LISTRING WORKAROUND -- 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}) minetest.swap_node(pos, {name="mcl_chests:"..canonical_basename.."_right",param2=param2})
local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right")
minetest.swap_node(p, { name = "mcl_chests:"..canonical_basename.."_left", param2 = param2 }) 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}) 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") local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left")
minetest.swap_node(p, { name = "mcl_chests:"..canonical_basename.."_right", param2 = param2 }) minetest.swap_node(p, { name = "mcl_chests:"..canonical_basename.."_right", param2 = param2 })
else 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
end, end,
after_place_node = function(pos, placer, itemstack, pointed_thing) after_place_node = function(pos, placer, itemstack, pointed_thing)
@ -253,23 +492,31 @@ minetest.register_node("mcl_chests:"..basename, {
if on_rightclick_addendum then if on_rightclick_addendum then
on_rightclick_addendum(pos, node, clicker) on_rightclick_addendum(pos, node, clicker)
end end
player_chest_open(clicker, pos, small_name, small_textures, node.param2, false, "default_chest", "mcl_chests_chest")
end, end,
on_destruct = function(pos) on_destruct = function(pos)
local players = minetest.get_connected_players() close_forms(canonical_basename, pos)
for p=1, #players do
minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z)
end
end, end,
mesecons = mesecons, mesecons = mesecons,
on_rotate = simple_rotate, on_rotate = simple_rotate,
}) })
minetest.register_node("mcl_chests:"..basename.."_left", { minetest.register_node(left_name, {
tiles = tiles_table.left, 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", paramtype = "light",
paramtype2 = "facedir", 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, drop = drop,
is_ground_content = false, is_ground_content = false,
sounds = mcl_sounds.node_sound_wood_defaults(), sounds = mcl_sounds.node_sound_wood_defaults(),
@ -278,33 +525,31 @@ minetest.register_node("mcl_chests:"..basename.."_left", {
local param2 = n.param2 local param2 = n.param2
local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") 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 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) minetest.swap_node(pos, n)
end end
create_entity(pos, left_name, left_textures, param2, true, "default_chest", "mcl_chests_chest", "chest")
end, end,
after_place_node = function(pos, placer, itemstack, pointed_thing) after_place_node = function(pos, placer, itemstack, pointed_thing)
minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name"))
end, end,
on_destruct = function(pos) on_destruct = function(pos)
local n = minetest.get_node(pos) local n = minetest.get_node(pos)
if n.name == "mcl_chests:"..basename then if n.name == small_name then
return return
end end
local players = minetest.get_connected_players() close_forms(canonical_basename, pos)
for p=1, #players do
minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z)
end
local param2 = n.param2 local param2 = n.param2
local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "left") 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 if not p or minetest.get_node(p).name ~= "mcl_chests:"..basename.."_right" then
return return
end end
for pl=1, #players do close_forms(canonical_basename, p)
minetest.close_formspec(players[pl]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..p.x.."_"..p.y.."_"..p.z)
end minetest.swap_node(p, { name = small_name, param2 = param2 })
minetest.swap_node(p, { name = "mcl_chests:"..basename, param2 = param2 }) create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest")
end, end,
after_dig_node = drop_items_chest, after_dig_node = drop_items_chest,
on_blast = on_chest_blast, on_blast = on_chest_blast,
@ -318,17 +563,19 @@ minetest.register_node("mcl_chests:"..basename.."_left", {
-- BEGIN OF LISTRING WORKAROUND -- BEGIN OF LISTRING WORKAROUND
elseif listname == "input" then elseif listname == "input" then
local inv = minetest.get_inventory({type="node", pos=pos}) 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 return -1
else 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 if other_inv:room_for_item("main", stack) then
return -1 return -1
else else
return 0 return 0
end end
end end]]--
-- END OF LISTRING WORKAROUND -- END OF LISTRING WORKAROUND
else else
return stack:get_count() 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_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}) 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) double_chest_add_item(inv, other_inv, "main", stack)
end end
-- END OF LISTRING WORKAROUND -- END OF LISTRING WORKAROUND
@ -399,16 +648,23 @@ minetest.register_node("mcl_chests:"..basename.."_left", {
if on_rightclick_addendum_left then if on_rightclick_addendum_left then
on_rightclick_addendum_left(pos, node, clicker) on_rightclick_addendum_left(pos, node, clicker)
end end
player_chest_open(clicker, pos, left_name, left_textures, node.param2, true, "default_chest", "mcl_chests_chest")
end, end,
mesecons = mesecons, mesecons = mesecons,
on_rotate = no_rotate, on_rotate = no_rotate,
}) })
minetest.register_node("mcl_chests:"..basename.."_right", { minetest.register_node("mcl_chests:"..basename.."_right", {
tiles = tiles_table.right, drawtype = "nodebox",
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", 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, drop = drop,
is_ground_content = false, is_ground_content = false,
sounds = mcl_sounds.node_sound_wood_defaults(), sounds = mcl_sounds.node_sound_wood_defaults(),
@ -417,7 +673,7 @@ minetest.register_node("mcl_chests:"..basename.."_right", {
local param2 = n.param2 local param2 = n.param2
local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") 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 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) minetest.swap_node(pos, n)
end end
end, end,
@ -426,24 +682,21 @@ minetest.register_node("mcl_chests:"..basename.."_right", {
end, end,
on_destruct = function(pos) on_destruct = function(pos)
local n = minetest.get_node(pos) local n = minetest.get_node(pos)
if n.name == "mcl_chests:"..basename then if n.name == small_name then
return return
end end
local players = minetest.get_connected_players() close_forms(canonical_basename, pos)
for p=1, #players do
minetest.close_formspec(players[p]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..pos.x.."_"..pos.y.."_"..pos.z)
end
local param2 = n.param2 local param2 = n.param2
local p = mcl_util.get_double_container_neighbor_pos(pos, param2, "right") 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 if not p or minetest.get_node(p).name ~= "mcl_chests:"..basename.."_left" then
return return
end end
for pl=1, #players do close_forms(canonical_basename, p)
minetest.close_formspec(players[pl]:get_player_name(), "mcl_chests:"..canonical_basename.."_"..p.x.."_"..p.y.."_"..p.z)
end minetest.swap_node(p, { name = small_name, param2 = param2 })
minetest.swap_node(p, { name = "mcl_chests:"..basename, param2 = param2 }) create_entity(p, small_name, small_textures, param2, false, "default_chest", "mcl_chests_chest", "chest")
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
end, end,
after_dig_node = drop_items_chest, after_dig_node = drop_items_chest,
@ -459,16 +712,17 @@ minetest.register_node("mcl_chests:"..basename.."_right", {
elseif listname == "input" then elseif listname == "input" then
local other_pos = mcl_util.get_double_container_neighbor_pos(pos, minetest.get_node(pos).param2, "right") 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}) 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 return -1
else else
local inv = minetest.get_inventory({type="node", pos=pos})
if inv:room_for_item("main", stack) then if inv:room_for_item("main", stack) then
return -1 return -1
else else
return 0 return 0
end end
end end--]]
return limit_put(stack, other_inv, inv)
-- END OF LISTRING WORKAROUND -- END OF LISTRING WORKAROUND
else else
return stack:get_count() 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 other_inv = minetest.get_inventory({type="node", pos=other_pos})
local inv = minetest.get_inventory({type="node", pos=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) double_chest_add_item(other_inv, inv, "main", stack)
end end
-- END OF LISTRING WORKAROUND -- END OF LISTRING WORKAROUND
@ -540,14 +796,16 @@ minetest.register_node("mcl_chests:"..basename.."_right", {
if on_rightclick_addendum_right then if on_rightclick_addendum_right then
on_rightclick_addendum_right(pos, node, clicker) on_rightclick_addendum_right(pos, node, clicker)
end end
player_chest_open(clicker, pos_other, left_name, left_textures, node.param2, true, "default_chest", "mcl_chests_chest")
end, end,
mesecons = mesecons, mesecons = mesecons,
on_rotate = no_rotate, on_rotate = no_rotate,
}) })
if mod_doc then if mod_doc then
doc.add_entry_alias("nodes", "mcl_chests:"..basename, "nodes", "mcl_chests:"..basename.."_left") doc.add_entry_alias("nodes", small_name, "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.."_right")
end end
-- END OF register_chest FUNCTION BODY -- END OF register_chest FUNCTION BODY
@ -561,29 +819,33 @@ register_chest("chest",
chestusage, chestusage,
S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"), 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_right.png", "mcl_chests_chest_left.png",
"mcl_chests_chest_back.png", "default_chest_front.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", "mcl_chests_chest_right.png", "mcl_chests_chest_left.png",
"default_chest_side_big.png^[transformFX", "default_chest_front_big.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", right = {"default_chest_top_big.png^[transformFX", "default_chest_top_big.png^[transformFX",
"mcl_chests_chest_right.png", "mcl_chests_chest_left.png", "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 false
) )
local traptiles = { 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_right.png", "mcl_chests_chest_trapped_left.png",
"mcl_chests_chest_trapped_back.png", "mcl_chests_chest_trapped_front.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_right.png", "mcl_chests_chest_trapped_left.png",
"mcl_chests_chest_trapped_side_big.png^[transformFX", "mcl_chests_chest_trapped_front_big.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", 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_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", register_chest("trapped_chest",
@ -598,22 +860,21 @@ register_chest("trapped_chest",
rules = trapped_chest_mesecons_rules, rules = trapped_chest_mesecons_rules,
}}, }},
function(pos, node, clicker) 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) mesecon.receptor_on(pos, trapped_chest_mesecons_rules)
player_chest_open(clicker, pos)
end, end,
function(pos, node, clicker) function(pos, node, clicker)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_int("players", 1) meta:set_int("players", 1)
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on_left", param2 = node.param2}) 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) mesecon.receptor_on(pos, trapped_chest_mesecons_rules)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") 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}) minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_on_right", param2 = node.param2})
mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules)
player_chest_open(clicker, pos)
end, end,
function(pos, node, clicker) function(pos, node, clicker)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right") 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) mesecon.receptor_on(pos, trapped_chest_mesecons_rules)
minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_on_left", param2 = node.param2}) 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) mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules)
player_chest_open(clicker, pos)
end end
) )
@ -634,15 +894,7 @@ register_chest("trapped_chest_on",
state = mesecon.state.on, state = mesecon.state.on,
rules = trapped_chest_mesecons_rules, rules = trapped_chest_mesecons_rules,
}}, }},
function(pos, node, clicker) nil, nil, nil,
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,
"trapped_chest", "trapped_chest",
"trapped_chest" "trapped_chest"
) )
@ -650,13 +902,15 @@ register_chest("trapped_chest_on",
local function close_if_trapped_chest(pos, player) local function close_if_trapped_chest(pos, player)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if node.name == "mcl_chests:trapped_chest_on" then if node.name == "mcl_chests:trapped_chest_on_small" then
minetest.swap_node(pos, {name="mcl_chests:trapped_chest", param2 = node.param2}) 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) mesecon.receptor_off(pos, trapped_chest_mesecons_rules)
player_chest_close(player) player_chest_close(player)
elseif node.name == "mcl_chests:trapped_chest_on_left" then elseif node.name == "mcl_chests:trapped_chest_on_left" then
minetest.swap_node(pos, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) 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) mesecon.receptor_off(pos, trapped_chest_mesecons_rules)
local pos_other = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "left") 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") 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}) 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) mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules)
player_chest_close(player) player_chest_close(player)
end end
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) 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 if fields.quit then
player_chest_close(player) player_chest_close(player)
end end
@ -710,6 +965,26 @@ minetest.register_craft({
burntime = 15 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]".. local formspec_ender_chest = "size[9,8.75]"..
"label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Ender Chest"))).."]".. "label[0,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Ender Chest"))).."]"..
"list[current_player;enderchest;0,0.5;9,3;]".. "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]" "listring[current_player;main]"
minetest.register_node("mcl_chests:ender_chest", { minetest.register_node("mcl_chests:ender_chest_small", {
description = S("Ender Chest"), description = S("Ender Chest"),
_tt_help = S("27 interdimensional inventory slots") .. "\n" .. S("Put items inside, retrieve them from any 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_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."), _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_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 -- Note: The “container” group is missing here because the ender chest does not
-- have an inventory on its own -- 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, is_ground_content = false,
paramtype = "light", paramtype = "light",
light_source = 7, light_source = 7,
@ -743,23 +1028,22 @@ minetest.register_node("mcl_chests:ender_chest", {
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("formspec", formspec_ender_chest) 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, end,
_mcl_blast_resistance = 3000, _mcl_blast_resistance = 3000,
_mcl_hardness = 22.5, _mcl_hardness = 22.5,
_mcl_silk_touch_drop = true, _mcl_silk_touch_drop = {"mcl_chests:ender_chest"},
on_rotate = simple_rotate, 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) minetest.register_on_joinplayer(function(player)
local inv = player:get_inventory() local inv = player:get_inventory()
inv:set_size("enderchest", 9*3) inv:set_size("enderchest", 9*3)
@ -852,6 +1136,8 @@ for color, desc in pairs(boxtypes) do
end end
end end
local small_name = "mcl_chests:"..color.."_shulker_box_small"
minetest.register_node("mcl_chests:"..color.."_shulker_box", { minetest.register_node("mcl_chests:"..color.."_shulker_box", {
description = desc, description = desc,
_tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"), _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_entry_name = entry_name,
_doc_items_longdesc = longdesc, _doc_items_longdesc = longdesc,
_doc_items_usagehelp = usagehelp, _doc_items_usagehelp = usagehelp,
tiles = { tiles = {mob_texture},
"mcl_chests_"..color.."_shulker_box_top.png", -- top drawtype = "mesh",
mesh = "mcl_chests_shulker.obj",
--[["mcl_chests_"..color.."_shulker_box_top.png", -- top
"[combine:16x16:-32,-28="..mob_texture, -- bottom "[combine:16x16:-32,-28="..mob_texture, -- bottom
"[combine:16x16:0,-36="..mob_texture..":0,-16="..mob_texture, -- side "[combine:16x16:0,-36="..mob_texture..":0,-16="..mob_texture, -- side
"[combine:16x16:-32,-36="..mob_texture..":-32,-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:-16,-36="..mob_texture..":-16,-16="..mob_texture, -- side
"[combine:16x16:-48,-36="..mob_texture..":-48,-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, old_shulker_box_node=1},
groups = {handy=1,pickaxey=1, container=3, deco_block=1, dig_by_piston=1, shulker_box=1},
is_ground_content = false, is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(), sounds = mcl_sounds.node_sound_stone_defaults(),
stack_max = 1, stack_max = 1,
drop = "", drop = "",
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", 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) on_construct = function(pos)
local meta = minetest.get_meta(pos) local node = minetest.get_node(pos)
meta:set_string("formspec", formspec_shulker_box(nil)) node.name = small_name
local inv = meta:get_inventory() minetest.set_node(pos, node)
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
end, end,
after_place_node = function(pos, placer, itemstack, pointed_thing) after_place_node = function(pos, placer, itemstack, pointed_thing)
local nmeta = minetest.get_meta(pos) local nmeta = minetest.get_meta(pos)
@ -916,6 +1185,79 @@ for color, desc in pairs(boxtypes) do
return nil return nil
end end
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) on_destruct = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
@ -961,6 +1303,7 @@ for color, desc in pairs(boxtypes) do
if mod_doc and not is_canonical then 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", "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 end
minetest.register_craft({ minetest.register_craft({
@ -1001,6 +1344,40 @@ minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv
end end
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({ minetest.register_lbm({
-- Disable active/open trapped chests when loaded because nobody could -- Disable active/open trapped chests when loaded because nobody could
@ -1008,7 +1385,7 @@ minetest.register_lbm({
-- Fixes redstone weirdness. -- Fixes redstone weirdness.
label = "Disable active trapped chests", label = "Disable active trapped chests",
name = "mcl_chests:reset_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, run_at_every_load = true,
action = function(pos, node) action = function(pos, node)
minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " ..minetest.pos_to_string(pos)) minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " ..minetest.pos_to_string(pos))
@ -1016,17 +1393,6 @@ minetest.register_lbm({
end, 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({ minetest.register_lbm({
label = "Update shulker box formspecs (0.60.0)", label = "Update shulker box formspecs (0.60.0)",
name = "mcl_chests:update_shulker_box_formspecs_0_60_0", name = "mcl_chests:update_shulker_box_formspecs_0_60_0",

Binary file not shown.

View File

@ -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

Binary file not shown.

View File

@ -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

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -97,7 +97,7 @@ for _, row in ipairs(block.dyes) do
_doc_items_create_entry = create_entry, _doc_items_create_entry = create_entry,
_doc_items_entry_name = ename_cp, _doc_items_entry_name = ename_cp,
tiles = {"mcl_colorblocks_concrete_powder_"..name..".png"}, 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, stack_max = 64,
is_ground_content = false, is_ground_content = false,
sounds = mcl_sounds.node_sound_sand_defaults(), sounds = mcl_sounds.node_sound_sand_defaults(),
@ -208,11 +208,20 @@ minetest.register_abm({
neighbors = {"group:water"}, neighbors = {"group:water"},
action = function(pos, node) action = function(pos, node)
local harden_to = minetest.registered_nodes[node.name]._mcl_colorblocks_harden_to 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 -- 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! -- the ABM on the new concrete node, which isn't part of this ABM!
if harden_to then if harden_to then
node.name = harden_to node.name = harden_to
minetest.set_node(pos, node) --Fix "float" group not lowering concrete into the water by 1.
end 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, end,
}) })

View File

@ -2,4 +2,3 @@ mcl_core
mcl_worlds mcl_worlds
mesecons mesecons
doc? doc?
mcl_enchanting

View File

@ -57,9 +57,6 @@ minetest.register_globalstep(function(dtime)
if minetest.get_item_group(stack:get_name(), "compass") ~= 0 and if minetest.get_item_group(stack:get_name(), "compass") ~= 0 and
minetest.get_item_group(stack:get_name(), "compass")-1 ~= compass_image then minetest.get_item_group(stack:get_name(), "compass")-1 ~= compass_image then
local itemname = "mcl_compass:"..compass_image local itemname = "mcl_compass:"..compass_image
if mcl_enchanting.is_enchanted(stack:get_name()) then
itemname = itemname .. "_enchanted"
end
stack:set_name(itemname) stack:set_name(itemname)
player:get_inventory():set_stack("main", j, stack) player:get_inventory():set_stack("main", j, stack)
end end
@ -98,7 +95,7 @@ for i,img in ipairs(images) do
inventory_image = img, inventory_image = img,
wield_image = img, wield_image = img,
stack_max = 64, 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 -- Help aliases. Makes sure the lookup tool works correctly

Some files were not shown because too many files have changed in this diff Show More