Compare commits

..

1 Commits

Author SHA1 Message Date
Mikita Wiśniewski 7f53c24303 Make flowers buildable_to 2024-06-17 11:41:20 +07:00
290 changed files with 6063 additions and 6520 deletions

View File

@ -20,8 +20,10 @@
* epCode
* chmodsayshello
* MrRar
* FossFanatic
* SmokeyDope
* Faerraven / Michieal
* Codiac
* rudzik8
* teknomunk
@ -34,8 +36,6 @@
* NO11
* SumianVoice
* PrairieWind
* FossFanatic
* Codiac
## Contributors
* RandomLegoBrick
@ -140,7 +140,6 @@
* SOS-Games
* Bram
* qoheniac
* WillConker
## Music
* Jordach for the jukebox music compilation from Big Freaking Dig

View File

@ -46,9 +46,6 @@ Armor trim models were created by Aeonix_Aeon
Source: <https://www.curseforge.com/minecraft/texture-packs/ozocraft-remix>
License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)
Charcoal block texture was created by [blitzdoughnuts](https://gitlab.com/ApplemunchFromDaDead), based on the Pixel Perfection coal block.
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
The main menu images are released under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
All other files, unless mentioned otherwise, fall under:

View File

@ -83,7 +83,7 @@ The VoxeLibre repository is hosted at Mesehub. To contribute or report issues, h
* Discord: <https://discord.gg/xE4z8EEpDC>
* YouTube: <https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A>
* ContentDB: <https://content.minetest.net/packages/wuzzy/mineclone2/>
* OpenCollective: <https://opencollective.com/voxelibre>
* OpenCollective: <https://opencollective.com/mineclone2>
* Mastodon: <https://fosstodon.org/@VoxeLibre>
* Lemmy: <https://lemm.ee/c/voxelibre>
* Matrix space: <https://app.element.io/#/room/#voxelibre:matrix.org>

View File

@ -117,6 +117,10 @@ end
-- Array of unique hardness values for each group which affects dig time.
local hardness_values = get_hardness_values_for_groups()
-- Map indexed by hardness values which return the index of that value in
-- hardness_value. Used for quick lookup.
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
--[[local function compute_creativetimes(group)
local creativetimes = {}
@ -182,7 +186,6 @@ local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
local mult = capsdef.speed or 1
local uses = capsdef.uses
local def = mcl_autogroup.registered_diggroups[g]
assert(def, toolname .. " has unknown diggroup '" .. dump(g) .. "'")
local max_level = def.levels and #def.levels or 1
assert(capsdef.level, toolname .. ' is missing level for ' .. g)
@ -310,13 +313,6 @@ function mcl_autogroup.get_wear(toolname, diggroup)
end
local function overwrite()
-- Refresh, now that all groups are known.
hardness_values = get_hardness_values_for_groups()
-- Map indexed by hardness values which return the index of that value in
-- hardness_value. Used for quick lookup.
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
for nname, ndef in pairs(minetest.registered_nodes) do
local newgroups = table.copy(ndef.groups)
if (nname ~= "ignore" and ndef.diggable) then
@ -378,5 +374,4 @@ local function overwrite()
end
end
-- Make sure all tools and groups are registered
minetest.register_on_mods_loaded(overwrite)
overwrite()

View File

@ -1,4 +1,3 @@
name = _mcl_autogroup
depends = mcl_autogroup
author = ryvnf
description = VoxeLibre core mod which automatically adds groups to all items. Very important for digging times.

View File

@ -2,33 +2,33 @@
Simple flow functions.
## flowlib.is_touching(realpos, nodepos, radius)
Return true if a sphere of `radius` at `realpos` collide with node at `nodepos`.
Return true if a sphere of <radius> at <realpos> collide with node at <nodepos>.
* realpos: position
* nodepos: position
* radius: number
## flowlib.is_water(pos)
Return true if node at `pos` is water, false otherwise.
Return true if node at <pos> is water, false overwise.
* pos: position
## flowlib.node_is_water(node)
Return true if `node` is water, false otherwise.
Return true if <node> is water, false overwise.
* node: node
## flowlib.is_lava(pos)
Return true if node at `pos` is lava, false otherwise.
Return true if node at <pos> is lava, false overwise.
* pos: position
## flowlib.node_is_lava(node)
Return true if `node` is lava, false otherwise.
Return true if <node> is lava, false overwise.
* node: node
## flowlib.is_liquid(pos)
Return true if node at `pos` is liquid, false otherwise.
Return true if node at <pos> is liquid, false overwise.
* pos: position
## flowlib.node_is_liquid(node)
Return true if `node` is liquid, false otherwise.
Return true if <node> is liquid, false overwise.
* node: node
## flowlib.quick_flow(pos, node)
@ -37,9 +37,9 @@ Return direction where the water is flowing (to be use to push mobs, items...).
* node: node
## flowlib.move_centre(pos, realpos, node, radius)
Return the pos of the nearest not water block near from `pos` in a sphere of `radius` at `realpos`.
WARNING: This function is never used in VL, use at your own risk. The informations described here may be wrong.
Return the pos of the nearest not water block near from <pos> in a sphere of <radius> at <realpos>.
WARNING: This function is never used in mcl2, use at your own risk. The informations described here may be wrong.
* pos: position
* realpos: position, position of the entity
* node: node
* radius: number
* radius: number

View File

@ -1,8 +1,8 @@
# mcl_autogroup
This mod emulates digging times from MC.
This mod emulate digging times from mc.
## mcl_autogroup.can_harvest(nodename, toolname, player)
Return true if `nodename` can be dig with `toolname` by <player>.
Return true if <nodename> can be dig with <toolname> by <player>.
* nodename: string, valid nodename
* toolname: (optional) string, valid toolname
* player: (optinal) ObjectRef, valid player
@ -14,7 +14,7 @@ WARNING: This function can only be called after mod initialization.
* efficiency: (optional) integer, the efficiency level the tool is enchanted with (default 0)
## mcl_autogroup.get_wear(toolname, diggroup)
Return the max wear of `toolname` with `diggroup`
Return the max wear of <toolname> with <diggroup>
WARNING: This function can only be called after mod initialization.
* toolname: string, name of the tool used
* diggroup: string, the name of the diggroup the tool is used on
@ -25,4 +25,4 @@ WARNING: This function can only be called after mod initialization.
* level: (optional) string, if specified it is an array containing the names of the different digging levels the digging group supports
## mcl_autogroup.registered_diggroups
List of registered diggroups, indexed by name.
List of registered diggroups, indexed by name.

View File

@ -1,5 +1,5 @@
# mcl_colors
Mod providing global table containing legacy Minecraft colors to be used in mods.
Mod providing global table containing legacity minecraft colors to be used in mods.
## mcl_colors.*
Colors by upper name, in hex value.

View File

@ -6,10 +6,10 @@ WARNING: Not using it inside your mods may cause strange bugs (using the native
## Callbacks
To modify the amount of damage dealt by something:
To modify the amount of damage made by something:
```lua
--obj: an ObjectRef
mcl_damage.register_modifier(function(obj, damage, reason)
end, 0)
```
```

View File

@ -1,13 +1,9 @@
# mcl_events
## mcl_events
### Registering Events
`mlc_events.register_event("name",def)`
## Registering Events
`mcl_events.register_event("name", def)`
### Event Definition
```
{
#### Event Definition
{
stage = 0,
max_stage = 1,
percent = 100,
@ -26,8 +22,6 @@
cond_complete = function(event) end,
--return true if event finished successfully
}
```
## Debugging
* /event_start `event` -- starts the given event at the current player coordinates
### Debugging
* /event_start <event> -- starts the given event at the current player coordinates

View File

@ -3,13 +3,13 @@ This mod provide helper functions to create explosions.
## mcl_explosions.explode(pos, strength, info, puncher)
* pos: position, initial position of the explosion
* strength: number, radius of the explosion
* strenght: number, radius of the explosion
* info: table, explosion informations:
* drop_chance: number, if specified becomes the drop chance of all nodes in the explosion (default: 1.0 / strength)
* max_blast_resistance: int, if specified the explosion will treat all non-indestructible nodes as having a blast resistance of no more than this value
* sound: bool, if true, the explosion will play a sound (default: true)
* particles: bool, if true, the explosion will create particles (default: true)
* fire: bool, if true, 1/3 of nodes become fire (default: false)
* fire: bool, if true, 1/3 nodes become fire (default: false)
* griefing: bool, if true, the explosion will destroy nodes (default: true)
* grief_protected: bool, if true, the explosion will also destroy nodes which have been protected (default: false)
* puncher: (optional) entity, will be used as source for damage done by the explosion
* puncher: (optional) entity, will be used as source for damage done by the explosion

View File

@ -48,20 +48,6 @@ function table.pairs_by_keys(t, f)
return iter
end
-- Removes one element randomly selected from the array section of the table and
-- returns it, or nil if there are no elements in the array section of the table
function table.remove_random_element(table)
local count = #table
if count == 0 then return nil end
local idx = math.random(count)
local res = table[idx]
table[idx] = table[count]
table[count] = nil
count = count - 1
return res
end
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default", false)
local LOG_MODULE = "[MCL2]"
function mcl_util.mcl_log(message, module, bypass_default_logger)
@ -1117,39 +1103,3 @@ function mcl_util.is_it_christmas()
return false
end
end
function mcl_util.to_bool(val)
if not val then return false end
return true
end
if not vector.in_area then
-- backport from minetest 5.8, can be removed when the minimum version is 5.8
vector.in_area = function(pos, min, max)
return (pos.x >= min.x) and (pos.x <= max.x) and
(pos.y >= min.y) and (pos.y <= max.y) and
(pos.z >= min.z) and (pos.z <= max.z)
end
end
-- Traces along a line of nodes vertically to find the next possition that isn't an allowed node
---@param pos The position to start tracing from
---@param dir The direction to trace in. 1 is up, -1 is down, all other values are not allowed.
---@param allowed_nodes A set of node names to trace along.
---@param limit The maximum number of steps to make. Defaults to 16 if nil or missing
---@return Three return values:
--- the position of the next node that isn't allowed or nil if no such node was found,
--- the distance from the start where that node was found,
--- the node table if a node was found
function mcl_util.trace_nodes(pos, dir, allowed_nodes, limit)
if (dir ~= -1) and (dir ~= 1) then return nil, 0, nil end
limit = limit or 16
for i = 1,limit do
pos = vector.offset(pos, 0, dir, 0)
local node = minetest.get_node(pos)
if not allowed_nodes[node.name] then return pos, i, node end
end
return nil, limit, nil
end

View File

@ -5,21 +5,20 @@ This mod provides utility functions about positions and dimensions.
This function returns:
* true, true: if pos is in deep void (deadly)
* true, false: if the pos is in void (non-deadly)
* false, false: otherwise
* true, false: if the pos is in void (non deadly)
* false, false: owerwise
Params:
* pos: position
## mcl_worlds.y_to_layer(y)
This function is used to calculate the Minetest y layer and dimension of the given y Minecraft layer.
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
Mainly used for ore generation.
Takes a Y coordinate as input and returns:
* The corresponding Minecraft layer (can be `nil` if void)
* The corresponding Minecraft dimension ("overworld", "nether" or "end") or "void" if y is in the void
Takes an Y coordinate as input and returns:
* The corresponding Minecraft layer (can be nil if void)
* The corresponding Minecraft dimension ("overworld", "nether" or "end") or "void" if <y> is in the void
If the Y coordinate is not located in any dimension, it will return: nil, "void"
Params:
@ -27,7 +26,7 @@ Params:
* y: int
## mcl_worlds.pos_to_dimension(pos)
This function return the Minecraft dimension of pos ("overworld", "nether" or "end") or "void" if y is in the void.
This function return the Minecraft dimension of <pos> ("overworld", "nether" or "end") or "void" if <y> is in the void.
* pos: position
@ -39,32 +38,31 @@ mc_dimension can be "overworld", "nether", "end" (default: "overworld").
* mc_dimension: string
## mcl_worlds.has_weather(pos)
Returns true if pos can have weather, false otherwise.
Returns true if <pos> can have weather, false owerwise.
Weather can be only in the overworld.
* pos: position
## mcl_worlds.has_dust(pos)
Returns true if pos can have nether dust, false otherwise.
Returns true if <pos> can have nether dust, false owerwise.
Nether dust can be only in the nether.
* pos: position
## mcl_worlds.compass_works(pos)
Returns true if compasses are working at pos, false otherwise.
In MC, you cant use compass in the nether and the end.
Returns true if compasses are working at <pos>, false owerwise.
In mc, you cant use compass in the nether and the end.
* pos: position
## mcl_worlds.compass_works(pos)
Returns true if clock are working at pos, false otherwise.
In MC, you cant use clock in the nether and the end.
Returns true if clock are working at <pos>, false owerwise.
In mc, you cant use clock in the nether and the end.
* pos: position
## mcl_worlds.register_on_dimension_change(function(player, dimension, last_dimension))
Register a callback function func(player, dimension).
It will be called whenever a player changes between dimensions.
The void counts as dimension.
@ -77,7 +75,7 @@ The void counts as dimension.
Table containing all function registered with mcl_worlds.register_on_dimension_change()
## mcl_worlds.dimension_change(player, dimension)
Notify this mod of a dimension change of player to dimension
Notify this mod of a dimension change of <player> to <dimension>
* player: player, player who changed the dimension
* dimension: string, new dimension ("overworld", "nether", "end", "void")

View File

@ -10,14 +10,3 @@ Rightclick on a water source to place the boat. Rightclick the boat to enter it.
Spruce Boat=Fichtenboot
Water vehicle=Wasserfahrzeug
Sneak to dismount=Zum Aussteigen schleichen
Obsidian Boat=Obsidianboot
Mangrove Boat=
Cherry Boat=Kirschholzboot
Oak Chest Boat=Eichentruhenboot
Spruce Chest Boat=
Birch Chest Boat=Birkentruhenboot
Jungle Chest Boat=Dschungeltruhenboot
Acacia Chest Boat=
Dark Oak Chest Boat=
Mangrove Chest Boat=
Cherry Chest Boat=

View File

@ -29,7 +29,7 @@ local function make_drop(pos, liquid, sound, interval, texture)
pt.expirationtime = t
pt.texture = "[combine:2x2:" ..
math.random(-14, 0) .. "," .. math.random(-14, 0) .. "=" .. texture
-math.random(1, 16) .. "," .. -math.random(1, 16) .. "=" .. texture
minetest.add_particle(pt)

View File

@ -136,7 +136,6 @@ local function try_object_pickup(player, inv, object, checkpos)
-- Destroy entity
-- This just prevents this section to be run again because object:remove() doesn't remove the item immediately.
le.target = checkpos
le.itemstring = ""
le._removed = true
-- Stop the object
@ -321,7 +320,6 @@ function minetest.handle_node_drops(pos, drops, digger)
if tool and nodedef._mcl_fortune_drop and enchantments.fortune then
local fortune_level = enchantments.fortune
local fortune_drop = nodedef._mcl_fortune_drop
local simple_drop = nodedef._mcl_fortune_drop.drop_without_fortune
if fortune_drop.discrete_uniform_distribution then
local min_count = fortune_drop.min_count
local max_count = fortune_drop.max_count + fortune_level * (fortune_drop.factor or 1)
@ -337,12 +335,6 @@ function minetest.handle_node_drops(pos, drops, digger)
local drop = get_fortune_drops(fortune_drop, fortune_level)
drops = get_drops(drop, tool:get_name(), dug_node.param2, nodedef.paramtype2)
end
if simple_drop then
for _, item in pairs(simple_drop) do
table.insert(drops, item)
end
end
end
if digger and mcl_experience.throw_xp and not silk_touch_drop then
@ -965,7 +957,6 @@ minetest.register_entity(":__builtin:item", {
self.random_velocity = 0
self:set_item(own_stack:to_string())
entity.itemstring = ""
entity._removed = true
object:remove()
return true

View File

@ -18,7 +18,7 @@ mcl_mobs.invis = {}
local remove_far = true
local mobs_debug = minetest.settings:get_bool("mobs_debug", false) -- Shows helpful debug info above each mob
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn",true)
local MAPGEN_LIMIT = mcl_vars.mapgen_limit
local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90
@ -150,11 +150,6 @@ function mob_class:mob_activate(staticdata, def, dtime)
local tmp = minetest.deserialize(staticdata)
if tmp then
-- Patch incorrectly converted mobs
if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then
mcl_mobs.strip_staticdata(tmp)
end
for _,stat in pairs(tmp) do
self[_] = stat
end
@ -388,7 +383,7 @@ end
local function on_step_work(self, dtime, moveresult)
local function on_step_work (self, dtime)
local pos = self.object:get_pos()
if not pos then return end
@ -402,7 +397,7 @@ local function on_step_work(self, dtime, moveresult)
-- Do we abandon out of here now?
end
if self:falling(pos, moveresult) then return end
if self:falling(pos) then return end
if self:step_damage (dtime, pos) then return end
if self.state == "die" then return end
@ -502,11 +497,11 @@ end
-- main mob function
function mob_class:on_step(dtime, moveresult)
function mob_class:on_step(dtime)
if not DEVELOPMENT then
-- Removed as bundled Lua (5.1 doesn't support xpcall)
--local status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime)
local status, retVal = pcall(on_step_work, self, dtime, moveresult)
local status, retVal = pcall(on_step_work, self, dtime)
if status then
return retVal
else
@ -521,7 +516,7 @@ function mob_class:on_step(dtime, moveresult)
log_error (dump(retVal), dump(pos), dump(self))
end
else
return on_step_work (self, dtime, moveresult)
return on_step_work (self, dtime)
end
end

View File

@ -285,7 +285,6 @@ function mob_class:check_breeding()
end
local child = mcl_mobs.spawn_child(pos, parent1.name)
if not child then return end
local ent_c = child:get_luaentity()

View File

@ -5,7 +5,6 @@ local validate_vector = mcl_util.validate_vector
local active_particlespawners = {}
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
local DEFAULT_FALL_SPEED = -9.81*1.5
local PI_THIRD = math.pi / 3 -- 60 degrees
local PATHFINDING = "gowp"
@ -295,66 +294,86 @@ function mcl_mobs:set_animation(self, anim)
self:set_animation(anim)
end
local function who_are_you_looking_at (self, dtime)
if self.order == "sleep" then
self._locked_object = nil
return
end
local function dir_to_pitch(dir)
--local dir2 = vector.normalize(dir)
local xz = math.abs(dir.x) + math.abs(dir.z)
return -math.atan2(-dir.y, xz)
end
local function who_are_you_looking_at (self, dtime)
local pos = self.object:get_pos()
local stop_look_at_player_chance = math.random(833/self.curiosity)
-- was 10000 - div by 12 for avg entities as outside loop
local stop_look_at_player = math.random() * 833 <= self.curiosity
local stop_look_at_player = stop_look_at_player_chance == 1
if self.attack then
self._locked_object = not self.target_time_lost and self.attack or nil
if not self.target_time_lost then
self._locked_object = self.attack
else
self._locked_object = nil
end
elseif self.following then
self._locked_object = self.following
elseif self._locked_object then
if stop_look_at_player then self._locked_object = nil end
if stop_look_at_player then
--minetest.log("Stop look: ".. self.name)
self._locked_object = nil
end
elseif not self._locked_object then
if mcl_util.check_dtime_timer(self, dtime, "step_look_for_someone", 0.2) then
local pos = self.object:get_pos()
--minetest.log("Change look check: ".. self.name)
-- For the wither this was 20/60=0.33, so probably need to rebalance and divide rates.
-- but frequency of check isn't good as it is costly. Making others too infrequent requires testing
local chance = 150/self.curiosity
if chance < 1 then chance = 1 end
local look_at_player_chance = math.random(chance)
-- was 5000 but called in loop based on entities. so div by 12 as estimate avg of entities found,
-- then div by 20 as less freq lookup
local look_at_player = look_at_player_chance == 1
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 8)) do
if obj:is_player() and vector.distance(pos, obj:get_pos()) < 4 then
if obj:is_player() and vector.distance(pos,obj:get_pos()) < 4 then
--minetest.log("Change look to player: ".. self.name)
self._locked_object = obj
break
elseif obj:is_player() or (obj:get_luaentity() and self ~= obj:get_luaentity() and obj:get_luaentity().name == self.name) then
-- For the wither this was 20/60=0.33, so probably need to rebalance and divide rates.
-- but frequency of check isn't good as it is costly. Making others too infrequent requires testing
-- was 5000 but called in loop based on entities. so div by 12 as estimate avg of entities found,
-- then div by 20 as less freq lookup
if math.random() * 150 <= self.curiosity then
elseif obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity()) then
if look_at_player then
--minetest.log("Change look to mob: ".. self.name)
self._locked_object = obj
break
end
end
end
end
end
end
function mob_class:check_head_swivel(dtime)
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
who_are_you_looking_at(self, dtime)
local newr, oldp, oldr = vector.zero(), nil, nil
if self.object.get_bone_override then -- minetest >= 5.9
local ov = self.object:get_bone_override(self.head_swivel)
oldp, oldr = ov.position.vec, ov.rotation.vec
else -- minetest < 5.9
oldp, oldr = self.object:get_bone_position(self.head_swivel)
oldr = vector.apply(oldr, math.rad) -- old API uses radians
end
who_are_you_looking_at (self, dtime)
local locked_object = self._locked_object
if locked_object and (locked_object:is_player() or locked_object:get_luaentity()) and locked_object:get_hp() > 0 then
local final_rotation = vector.zero()
local oldp,oldr = self.object:get_bone_position(self.head_swivel)
if self._locked_object and (self._locked_object:is_player() or self._locked_object:get_luaentity()) and self._locked_object:get_hp() > 0 then
local _locked_object_eye_height = 1.5
if locked_object:is_player() then
_locked_object_eye_height = locked_object:get_properties().eye_height
elseif locked_object:get_luaentity() then
_locked_object_eye_height = locked_object:get_luaentity().head_eye_height
if self._locked_object:get_luaentity() then
_locked_object_eye_height = self._locked_object:get_luaentity().head_eye_height
end
if self._locked_object:is_player() then
_locked_object_eye_height = self._locked_object:get_properties().eye_height
end
if _locked_object_eye_height then
local self_rot = self.object:get_rotation()
-- If a mob is attached, should we really be messing with what they are looking at?
-- Should this be excluded?
@ -362,48 +381,40 @@ function mob_class:check_head_swivel(dtime)
self_rot = self.object:get_attach():get_rotation()
end
local ps = self.object:get_pos()
ps.y = ps.y + self.head_eye_height * .7
local pt = locked_object:get_pos()
pt.y = pt.y + _locked_object_eye_height
local dir = vector.direction(ps, pt)
local mob_yaw = self_rot.y + math.atan2(dir.x, dir.z) + self.head_yaw_offset
local mob_pitch = math.asin(-dir.y) * self.head_pitch_multiplier
local player_pos = self._locked_object:get_pos()
local direction_player = vector.direction(vector.add(self.object:get_pos(), vector.new(0, self.head_eye_height*.7, 0)), vector.add(player_pos, vector.new(0, _locked_object_eye_height, 0)))
local mob_yaw = math.deg(-(-(self_rot.y)-(-minetest.dir_to_yaw(direction_player))))+self.head_yaw_offset
local mob_pitch = math.deg(-dir_to_pitch(direction_player))*self.head_pitch_multiplier
if (mob_yaw < -PI_THIRD or mob_yaw > PI_THIRD) and not (self.attack and self.state == "attack" and not self.runaway) then
newr = vector.multiply(oldr, 0.9)
if (mob_yaw < -60 or mob_yaw > 60) and not (self.attack and self.state == "attack" and not self.runaway) then
final_rotation = vector.multiply(oldr, 0.9)
elseif self.attack and self.state == "attack" and not self.runaway then
if self.head_yaw == "y" then
newr = vector.new(mob_pitch, mob_yaw, 0)
final_rotation = vector.new(mob_pitch, mob_yaw, 0)
elseif self.head_yaw == "z" then
newr = vector.new(mob_pitch, 0, -mob_yaw)
final_rotation = vector.new(mob_pitch, 0, -mob_yaw)
end
else
if self.head_yaw == "y" then
newr = vector.new((mob_pitch-oldr.x)*.3+oldr.x, (mob_yaw-oldr.y)*.3+oldr.y, 0)
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, ((mob_yaw-oldr.y)*.3)+oldr.y, 0)
elseif self.head_yaw == "z" then
newr = vector.new((mob_pitch-oldr.x)*.3+oldr.x, 0, ((mob_yaw-oldr.y)*.3+oldr.y)*-3)
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, 0, -(((mob_yaw-oldr.y)*.3)+oldr.y)*3)
end
end
end
elseif not locked_object and math.abs(oldr.y) > 0.05 and math.abs(oldr.x) < 0.05 then
newr = vector.multiply(oldr, 0.9)
end
-- 0.02 is about 1.14 degrees tolerance, to update less often
local newp = vector.new(0, self.bone_eye_height, self.horizontal_head_height)
if math.abs(oldr.x-newr.x) + math.abs(oldr.y-newr.y) + math.abs(oldr.z-newr.z) < 0.02 and vector.equals(oldp, newp) then return end
if self.object.get_bone_override then -- minetest >= 5.9
self.object:set_bone_override(self.head_swivel, {
position = { vec = newp, absolute = true },
rotation = { vec = newr, absolute = true } })
else -- minetest < 5.9
-- old API uses degrees not radians
self.object:set_bone_position(self.head_swivel, newp, vector.apply(newr, math.deg))
elseif not self._locked_object and math.abs(oldr.y) > 3 and math.abs(oldr.x) < 3 then
final_rotation = vector.multiply(oldr, 0.9)
else
--final_rotation = vector.new(0,0,0)
end
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horizontal_head_height), final_rotation)
end
function mob_class:set_animation_speed()
local v = self.object:get_velocity()
if v then

View File

@ -141,7 +141,7 @@ function mcl_mobs.register_mob(name, def)
local final_def = {
use_texture_alpha = def.use_texture_alpha,
head_swivel = def.head_swivel or nil, -- bool to activate this function
head_yaw_offset = math.rad(def.head_yaw_offset or 0), -- for wonkey model bones
head_yaw_offset = def.head_yaw_offset or 0, -- for wonkey model bones
head_pitch_multiplier = def.head_pitch_multiplier or 1, --for inverted pitch
bone_eye_height = def.bone_eye_height or 1.4, -- head bone offset
head_eye_height = def.head_eye_height or def.bone_eye_height or 0, -- how hight aproximatly the mobs head is fromm the ground to tell the mob how high to look up at the player
@ -342,33 +342,13 @@ function mcl_mobs.register_mob(name, def)
minetest.register_entity(name, setmetatable(final_def,mcl_mobs.mob_class_meta))
end -- END mcl_mobs.register_mob function
local STRIP_FIELDS = { "mesh", "base_size", "textures", "base_mesh", "base_texture" }
function mcl_mobs.strip_staticdata(unpacked_staticdata)
-- Strip select fields from the staticdata to prevent conversion issues
for i = 1,#STRIP_FIELDS do
unpacked_staticdata[STRIP_FIELDS[i]] = nil
end
end
function mcl_mobs.register_conversion(old_name, new_name)
minetest.register_entity(old_name, {
on_activate = function(self, staticdata, dtime)
local unpacked_staticdata = minetest.deserialize(staticdata)
mcl_mobs.strip_staticdata(unpacked_staticdata)
staticdata = minetest.serialize(unpacked_staticdata)
local old_object = self.object
if not old_object then return end
local pos = old_object:get_pos()
if not pos then return end
old_object:remove()
local new_object = minetest.add_entity(pos, new_name, staticdata)
if not new_object then return end
local hook = (new_object:get_luaentity() or {})._on_after_convert
if hook then hook(new_object) end
local obj = minetest.add_entity(self.object:get_pos(), new_name, staticdata)
local hook = (obj:get_luaentity() or {})._on_after_convert
if hook then hook(obj) end
self.object:remove()
end,
_convert_to = new_name,
})
@ -528,7 +508,7 @@ end
-- Note: This also introduces the “spawn_egg” group:
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, addegg, no_creative)
function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addegg, no_creative)
local grp = {spawn_egg = 1}
@ -539,7 +519,7 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")"
if old_spawn_icons then
local mobname = mob_id:gsub("mobs_mc:","")
local mobname = mob:gsub("mobs_mc:","")
local fn = "mobs_mc_spawn_icon_"..mobname..".png"
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then
invimg = fn
@ -551,7 +531,7 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
end
-- register old stackable mob egg
minetest.register_craftitem(mob_id, {
minetest.register_craftitem(mob, {
description = desc,
inventory_image = invimg,
@ -561,6 +541,7 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
_doc_items_usagehelp = S("Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns."),
on_place = function(itemstack, placer, pointed_thing)
local pos = pointed_thing.above
-- am I clicking on something with existing on_rightclick function?
@ -570,12 +551,11 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
return def.on_rightclick(pointed_thing.under, under, placer, itemstack)
end
local mob_name = itemstack:get_name()
if pos and within_limits(pos, 0) and not minetest.is_protected(pos, placer:get_player_name()) then
local name = placer:get_player_name()
local privs = minetest.get_player_privs(name)
if under.name == "mcl_mobspawners:spawner" then
if minetest.is_protected(pointed_thing.under, name) then
minetest.record_protection_violation(pointed_thing.under, name)
@ -592,35 +572,26 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
--minetest.log("min light: " .. mob_light_lvl[1])
--minetest.log("max light: " .. mob_light_lvl[2])
-- Handle egg conversion
local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to
if convert_to then mob_name = convert_to end
mcl_mobspawners.setup_spawner(pointed_thing.under, mob_name, mob_light_lvl[1], mob_light_lvl[2])
mcl_mobspawners.setup_spawner(pointed_thing.under, itemstack:get_name(), mob_light_lvl[1], mob_light_lvl[2])
if not minetest.is_creative_enabled(name) then
itemstack:take_item()
end
return itemstack
end
if not minetest.registered_entities[mob_name] then
if not minetest.registered_entities[mob] then
return itemstack
end
if minetest.settings:get_bool("only_peaceful_mobs", false)
and minetest.registered_entities[mob_name].type == "monster" then
and minetest.registered_entities[mob].type == "monster" then
minetest.chat_send_player(name, S("Only peaceful mobs allowed!"))
return itemstack
end
pos.y = pos.y - 1
local mob = mcl_mobs.spawn(pos, mob_name)
if not mob then
pos.y = pos.y + 1
mob = mcl_mobs.spawn(pos, mob_name)
if not mob then return end
end
pos.y = pos.y - 0.5
local mob = minetest.add_entity(pos, mob)
local entityname = itemstack:get_name()
minetest.log("action", "Player " ..name.." spawned "..entityname.." at "..minetest.pos_to_string(pos))
local ent = mob:get_luaentity()
@ -651,4 +622,5 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
return itemstack
end,
})
end

View File

@ -258,18 +258,6 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
end
end
-- Stop!
local s = get_sign(entity.v)
entity.v = entity.v - 0.02 * s
if s ~= get_sign(entity.v) then
entity.object:set_velocity({x = 0, y = 0, z = 0})
entity.v = 0
return
end
-- if not moving then set animation and return
if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
@ -285,6 +273,18 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
mcl_mobs:set_animation(entity, moving_anim)
end
-- Stop!
local s = get_sign(entity.v)
entity.v = entity.v - 0.02 * s
if s ~= get_sign(entity.v) then
entity.object:set_velocity({x = 0, y = 0, z = 0})
entity.v = 0
return
end
-- enforce speed limit forward and reverse
local max_spd = entity.max_speed_reverse

View File

@ -362,7 +362,7 @@ function mob_class:env_danger_movement_checks(player_in_active_range)
self.state = "stand"
self:set_animation( "stand")
end
yaw = yaw + math.random() - 0.5
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
return
end
@ -788,9 +788,9 @@ function mob_class:flop()
if self.object:get_velocity().y < 0.1 then
self:mob_sound("flop")
self.object:set_velocity({
x = (math.random()-0.5) * 2 * FLOP_HOR_SPEED,
x = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED),
y = FLOP_HEIGHT,
z = (math.random()-0.5) * 2 * FLOP_HOR_SPEED,
z = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED),
})
end
end
@ -920,7 +920,7 @@ function mob_class:do_states_walk()
-- Randomly turn
if math.random(1, 100) <= 30 then
yaw = yaw + math.random() - 0.5
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
end
end
@ -929,7 +929,7 @@ function mob_class:do_states_walk()
-- otherwise randomly turn
elseif math.random(1, 100) <= 30 then
yaw = yaw + math.random() - 0.5
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
end
@ -989,7 +989,7 @@ function mob_class:do_states_stand(player_in_active_range)
if lp.x > s.x then yaw = yaw +math.pi end
else
yaw = yaw + math.random() - 0.5
yaw = yaw + math.random(-0.5, 0.5)
end
yaw = self:set_yaw( yaw, 8)

View File

@ -692,10 +692,14 @@ function mob_class:do_env_damage()
local nodef3 = minetest.registered_nodes[self.standing_under]
-- rain
if self.rain_damage > 0 and mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
self.health = self.health - self.rain_damage
if self:check_for_death("rain", {type = "environment", pos = pos, node = self.standing_in}) then
return true
if self.rain_damage > 0 then
if mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
self.health = self.health - self.rain_damage
if self:check_for_death("rain", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end
end
@ -703,10 +707,14 @@ function mob_class:do_env_damage()
-- water damage
if self.water_damage > 0 and nodef.groups.water then
self.health = self.health - self.water_damage
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png", nil, nil, 1, nil)
if self:check_for_death("water", {type = "environment", pos = pos, node = self.standing_in}) then
return true
if self.water_damage ~= 0 then
self.health = self.health - self.water_damage
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png", nil, nil, 1, nil)
if self:check_for_death("water", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end
elseif self.lava_damage > 0 and (nodef.groups.lava) then
-- lava damage
@ -722,69 +730,91 @@ function mob_class:do_env_damage()
end
elseif self.fire_damage > 0 and (nodef2.groups.fire) then
-- magma damage
self.health = self.health - self.fire_damage
if self:check_for_death("fire", {type = "environment", pos = pos, node = self.standing_in}) then
return true
if self.fire_damage ~= 0 then
self.health = self.health - self.fire_damage
if self:check_for_death("fire", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end
elseif self.fire_damage > 0 and (nodef.groups.fire) then
-- fire damage
self.health = self.health - self.fire_damage
mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
mcl_burning.set_on_fire(self.object, 5)
if self:check_for_death("fire", {type = "environment", pos = pos, node = self.standing_in}) then
return true
if self.fire_damage ~= 0 then
self.health = self.health - self.fire_damage
mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
mcl_burning.set_on_fire(self.object, 5)
if self:check_for_death("fire", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end
elseif nodef.damage_per_second ~= 0 and not nodef.groups.lava and not nodef.groups.fire then
-- damage_per_second node check
self.health = self.health - nodef.damage_per_second
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png")
if self:check_for_death("dps", {type = "environment", pos = pos, node = self.standing_in}) then
if self:check_for_death("dps", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end
-- Cactus damage
if self.standing_on == "mcl_core:cactus" or self.standing_in == "mcl_core:cactus" or self.standing_under == "mcl_core:cactus" then
self:damage_mob("cactus", 2)
if self:check_for_death("cactus", {type = "environment", pos = pos, node = self.standing_in}) then
return true
local near = minetest.find_node_near(pos, 1, "mcl_core:cactus", true)
if not near and near ~= nil then
near = find_node_near({x=pos.x, y=pos.y-1, z=pos.z}, 1, "mcl_core:cactus", true)
end
if near then
-- is mob touching the cactus?
local dist = vector.distance(pos, near)
local dist_feet = vector.distance({x=pos.x, y=pos.y-1, z=pos.z}, near)
local large_mob = false
local medium_mob = false
if self.name == "mobs_mc:ender_dragon" or
self.name == "mobs_mc:ghast" or
self.name == "mobs_mc:guardian_elder" or
self.name == "mobs_mc:slime_big" or
self.name == "mobs_mc:magma_cube_big" or
self.name == "mobs_mc:wither" then
large_mob = true
elseif self.name == "mobs_mc:hoglin" or
self.name == "mobs_mc:zoglin" or
self.name == "mobs_mc:horse" or
self.name == "mobs_mc:skeleton_horse" or
self.name == "mobs_mc:zombie_horse" or
self.name == "mobs_mc:donkey" or
self.name == "mobs_mc:mule" or
self.name == "mobs_mc:iron_golem" or
self.name == "mobs_mc:polar_bear" or
self.name == "mobs_mc:spider" or
self.name == "mobs_mc:cave_spider" or
self.name == "mobs_mc:strider" then
medium_mob = true
end
else
local near = minetest.find_node_near(pos, 1, "mcl_core:cactus")
if near then
-- is mob touching the cactus?
local dist = vector.distance(pos, near)
local threshold = 1.04 -- small mobs
-- medium mobs
if self.name == "mobs_mc:spider" or
self.name == "mobs_mc:iron_golem" or
self.name == "mobs_mc:horse" or
self.name == "mobs_mc:donkey" or
self.name == "mobs_mc:mule" or
self.name == "mobs_mc:polar_bear" or
self.name == "mobs_mc:cave_spider" or
self.name == "mobs_mc:skeleton_horse" or
self.name == "mobs_mc:zombie_horse" or
self.name == "mobs_mc:strider" or
self.name == "mobs_mc:hoglin" or
self.name == "mobs_mc:zoglin" then
threshold = 1.165
elseif self.name == "mobs_mc:slime_big" or
self.name == "mobs_mc:magma_cube_big" or
self.name == "mobs_mc:ghast" or
self.name == "mobs_mc:guardian_elder" or
self.name == "mobs_mc:wither" or
self.name == "mobs_mc:ender_dragon" then
threshold = 1.25
end
if dist < threshold then
if (not large_mob and not medium_mob and (dist < 1.03 or dist_feet < 1.6)) or (medium_mob and (dist < 1.165 or dist_feet < 1.73)) or (large_mob and (dist < 1.25 or dist_feet < 1.9)) then
if self.health ~= 0 then
self:damage_mob("cactus", 2)
if self:check_for_death("cactus", {type = "environment", pos = pos, node = self.standing_in}) then
if self:check_for_death("cactus", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end
end
end
-- is mob standing on the cactus?
if self.standing_on == "mcl_core:cactus" or self.standing_in == "mcl_core:cactus" or self.standing_under == "mcl_core:cactus" then
self:damage_mob("cactus", 2)
if self:check_for_death("cactus", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end
-- Drowning damage
if self.breath_max ~= -1 then
@ -927,7 +957,8 @@ end
-- falling and fall damage
-- returns true if mob died
function mob_class:falling(pos, moveresult)
function mob_class:falling(pos)
if self.fly and self.state ~= "die" then
return
end
@ -950,13 +981,7 @@ function mob_class:falling(pos, moveresult)
new_acceleration = vector.new(0, DEFAULT_FALL_SPEED, 0)
elseif v.y <= 0 and v.y > self.fall_speed then
-- fall downwards at set speed
if moveresult and moveresult.touching_ground then
-- when touching ground, retain a minimal gravity to keep the touching_ground flag
-- but also to not get upwards acceleration with large dtime when on bouncy ground
new_acceleration = vector.new(0, self.fall_speed * 0.01, 0)
else
new_acceleration = vector.new(0, self.fall_speed, 0)
end
new_acceleration = vector.new(0, self.fall_speed, 0)
else
-- stop accelerating once max fall speed hit
new_acceleration =vector.zero()

View File

@ -10,22 +10,20 @@ local overworld_sky_threshold = tonumber(minetest.settings:get("mcl_mobs_overwor
local overworld_passive_threshold = tonumber(minetest.settings:get("mcl_mobs_overworld_passive_threshold")) or 7
local get_node = minetest.get_node
local get_item_group = minetest.get_item_group
local get_node_light = minetest.get_node_light
local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
local mt_get_biome_name = minetest.get_biome_name
local get_objects_inside_radius = minetest.get_objects_inside_radius
local get_connected_players = minetest.get_connected_players
local registered_nodes = minetest.registered_nodes
local math_min = math.min
local math_max = math.max
local math_random = math.random
local math_floor = math.floor
local math_ceil = math.ceil
local math_cos = math.cos
local math_sin = math.sin
local math_round = function(x) return (x > 0) and math_floor(x + 0.5) or math_ceil(x - 0.5) end
local math_sqrt = math.sqrt
local math_abs = math.abs
local vector_distance = vector.distance
local vector_new = vector.new
@ -35,15 +33,19 @@ local table_copy = table.copy
local table_remove = table.remove
local pairs = pairs
local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
local function mcl_log(message, property)
if property then message = message .. ": " .. dump(property) end
mcl_util.mcl_log(message, "[Mobs spawn]", true)
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_spawning", false)
local function mcl_log (message, property)
if LOGGING_ON then
if property then
message = message .. ": " .. dump(property)
end
mcl_util.mcl_log (message, "[Mobs spawn]", true)
end
end
if not logging then mcl_log = function() end end
local dbg_spawn_attempts = 0
local dbg_spawn_succ = 0
local dbg_spawn_counts = {}
local remove_far = true
@ -52,10 +54,8 @@ local FIND_SPAWN_POS_RETRIES = 16
local FIND_SPAWN_POS_RETRIES_SUCCESS_RESPIN = 8
local MOB_SPAWN_ZONE_INNER = 24
local MOB_SPAWN_ZONE_INNER_SQ = MOB_SPAWN_ZONE_INNER^2 -- squared
local MOB_SPAWN_ZONE_MIDDLE = 32
local MOB_SPAWN_ZONE_OUTER = 128
local MOB_SPAWN_ZONE_OUTER_SQ = MOB_SPAWN_ZONE_OUTER^2 -- squared
-- range for mob count
local MOB_CAP_INNER_RADIUS = 32
@ -95,6 +95,170 @@ mcl_log("Percentage of hostile spawns are group: " .. hostile_group_percentage_s
--do mobs spawn?
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false
local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn",true)
-- THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
-- Also used for missing parameter
-- Please update the list when adding new biomes!
local list_of_all_biomes = {
-- underground:
"FlowerForest_underground",
"JungleEdge_underground",
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
"MangroveSwamp_underground",
-- ocean:
"RoofedForest_ocean",
"JungleEdgeM_ocean",
"BirchForestM_ocean",
"BirchForest_ocean",
"IcePlains_deep_ocean",
"Jungle_deep_ocean",
"Savanna_ocean",
"MesaPlateauF_ocean",
"ExtremeHillsM_deep_ocean",
"Savanna_deep_ocean",
"SunflowerPlains_ocean",
"Swampland_deep_ocean",
"Swampland_ocean",
"MegaSpruceTaiga_deep_ocean",
"ExtremeHillsM_ocean",
"JungleEdgeM_deep_ocean",
"SunflowerPlains_deep_ocean",
"BirchForest_deep_ocean",
"IcePlainsSpikes_ocean",
"Mesa_ocean",
"StoneBeach_ocean",
"Plains_deep_ocean",
"JungleEdge_deep_ocean",
"SavannaM_deep_ocean",
"Desert_deep_ocean",
"Mesa_deep_ocean",
"ColdTaiga_deep_ocean",
"Plains_ocean",
"MesaPlateauFM_ocean",
"Forest_deep_ocean",
"JungleM_deep_ocean",
"FlowerForest_deep_ocean",
"MushroomIsland_ocean",
"MegaTaiga_ocean",
"StoneBeach_deep_ocean",
"IcePlainsSpikes_deep_ocean",
"ColdTaiga_ocean",
"SavannaM_ocean",
"MesaPlateauF_deep_ocean",
"MesaBryce_deep_ocean",
"ExtremeHills+_deep_ocean",
"ExtremeHills_ocean",
"MushroomIsland_deep_ocean",
"Forest_ocean",
"MegaTaiga_deep_ocean",
"JungleEdge_ocean",
"MesaBryce_ocean",
"MegaSpruceTaiga_ocean",
"ExtremeHills+_ocean",
"Jungle_ocean",
"RoofedForest_deep_ocean",
"IcePlains_ocean",
"FlowerForest_ocean",
"ExtremeHills_deep_ocean",
"MesaPlateauFM_deep_ocean",
"Desert_ocean",
"Taiga_ocean",
"BirchForestM_deep_ocean",
"Taiga_deep_ocean",
"JungleM_ocean",
"MangroveSwamp_ocean",
"MangroveSwamp_deep_ocean",
-- water or beach?
"MesaPlateauFM_sandlevel",
"MesaPlateauF_sandlevel",
"MesaBryce_sandlevel",
"Mesa_sandlevel",
-- beach:
"FlowerForest_beach",
"Forest_beach",
"StoneBeach",
"ColdTaiga_beach_water",
"Taiga_beach",
"Savanna_beach",
"Plains_beach",
"ExtremeHills_beach",
"ColdTaiga_beach",
"Swampland_shore",
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore",
"BambooJungleM_shore",
"BambooJungle_shore",
"MangroveSwamp_shore",
-- dimension biome:
"Nether",
"BasaltDelta",
"CrimsonForest",
"WarpedForest",
"SoulsandValley",
"End",
-- Overworld regular:
"Mesa",
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"ExtremeHillsM",
"ExtremeHills+_snowtop",
"Jungle",
"Savanna",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"Desert",
"ColdTaiga",
"MushroomIsland",
"IcePlainsSpikes",
"SunflowerPlains",
"IcePlains",
"RoofedForest",
"ExtremeHills+_snowtop",
"MesaPlateauFM_grasstop",
"JungleEdgeM",
"JungleM",
"BirchForestM",
"MesaPlateauF",
"MesaPlateauFM",
"MesaPlateauF_grasstop",
"MesaBryce",
"JungleEdge",
"SavannaM",
"MangroveSwamp",
"BambooJungle",
"BambooJungleEdge",
"BambooJungleEdgeM",
"BambooJungleM",
}
-- count how many mobs are in an area
local function count_mobs(pos,r,mob_type)
@ -123,7 +287,11 @@ local function count_mobs_total(mob_type)
end
local function count_mobs_add_entry (mobs_list, mob_cat)
mobs_list[mob_cat] = (mobs_list[mob_cat] or 0) + 1
if mobs_list[mob_cat] then
mobs_list[mob_cat] = mobs_list[mob_cat] + 1
else
mobs_list[mob_cat] = 1
end
end
--categorise_by can be name or type or spawn_class
@ -282,7 +450,7 @@ function mcl_mobs:spawn_setup(def)
local dimension = def.dimension or "overworld"
local type_of_spawning = def.type_of_spawning or "ground"
local biomes = def.biomes or nil
local biomes = def.biomes or list_of_all_biomes
local min_light = def.min_light or 0
local max_light = def.max_light or (minetest.LIGHT_MAX + 1)
local chance = def.chance or 1000
@ -433,40 +601,71 @@ function mcl_mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_
spawn_dictionary[key]["check_position"] = check_position
end
-- Calculate the inverse of a piecewise linear function f(x). Line segments are represented as two
-- adjacent points specified as { x, f(x) }. At least 2 points are required. If there are most solutions,
-- the one with a lower x value will be chosen.
local function inverse_pwl(fx, f)
if fx < f[1][2] then
return f[1][1]
end
for i=2,#f do
local x0,fx0 = unpack(f[i-1])
local x1,fx1 = unpack(f[i ])
if fx < fx1 then
return (fx - fx0) * (x1 - x0) / (fx1 - fx0) + x0
end
end
return f[#f][1]
end
local SPAWN_DISTANCE_CDF_PWL = {
{0.000,0.00},
{0.083,0.40},
{0.416,0.75},
{1.000,1.00},
}
local two_pi = 2 * math.pi
local function get_next_mob_spawn_pos(pos)
-- Select a distance such that distances closer to the player are selected much more often than
-- those further away from the player. This does produce a concentration at INNER (24 blocks)
local distance = math_random()^2 * (MOB_SPAWN_ZONE_OUTER - MOB_SPAWN_ZONE_INNER) + MOB_SPAWN_ZONE_INNER
-- those further away from the player.
local fx = (math_random(1,10000)-1) / 10000
local x = inverse_pwl(fx, SPAWN_DISTANCE_CDF_PWL)
local distance = x * (MOB_SPAWN_ZONE_OUTER - MOB_SPAWN_ZONE_INNER) + MOB_SPAWN_ZONE_INNER
--print("Using spawn distance of "..tostring(distance).." fx="..tostring(fx)..",x="..tostring(x))
-- Choose a random direction. Rejection sampling is simple and fast (1-2 tries usually)
local xoff, yoff, zoff, dd
repeat
xoff, yoff, zoff = math_random() * 2 - 1, math_random() * 2 - 1, math_random() * 2 - 1
dd = xoff*xoff + yoff*yoff + zoff*zoff
until (dd <= 1 and dd >= 1e-6) -- outside of uniform ball, retry
dd = distance / math_sqrt(dd) -- distance scaling factor
xoff, yoff, zoff = xoff * dd, yoff * dd, zoff * dd
-- TODO Floor xoff and zoff and add 0.5 so it tries to spawn in the middle of the square. Less failed attempts.
-- Use spherical coordinates https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
local theta = math_random() * two_pi
local phi = math_random() * two_pi
local xoff = math_round(distance * math_sin(theta) * math_cos(phi))
local yoff = math_round(distance * math_cos(theta))
local zoff = math_round(distance * math_sin(theta) * math_sin(phi))
local goal_pos = vector.offset(pos, xoff, yoff, zoff)
if not (math_abs(goal_pos.x) <= SPAWN_MAPGEN_LIMIT and math_abs(goal_pos.y) <= SPAWN_MAPGEN_LIMIT and math_abs(goal_pos.z) <= SPAWN_MAPGEN_LIMIT) then
if not ( math.abs(goal_pos.x) <= SPAWN_MAPGEN_LIMIT and math.abs(pos.y) <= SPAWN_MAPGEN_LIMIT and math.abs(goal_pos.z) <= SPAWN_MAPGEN_LIMIT ) then
mcl_log("Pos outside mapgen limits: " .. minetest.pos_to_string(goal_pos))
return nil
end
-- Calculate upper/lower y limits
local d2 = xoff*xoff + zoff*zoff -- squared distance in x,z plane only
local y1 = math_sqrt(MOB_SPAWN_ZONE_OUTER_SQ - d2) -- absolue value of distance to outer sphere
local R1 = MOB_SPAWN_ZONE_OUTER
local d = vector_distance( pos, vector.new( goal_pos.x, pos.y, goal_pos.z ) ) -- distance from player to projected point on horizontal plane
local y1 = math_sqrt( R1*R1 - d*d ) -- absolue value of distance to outer sphere
local y_min, y_max
if d2 >= MOB_SPAWN_ZONE_INNER_SQ then
local y_min
local y_max
if d >= MOB_SPAWN_ZONE_INNER then
-- Outer region, y range has both ends on the outer sphere
y_min = pos.y - y1
y_max = pos.y + y1
else
-- Inner region, y range spans between inner and outer spheres
local y2 = math_sqrt(MOB_SPAWN_ZONE_INNER_SQ - d2)
if goal_pos.y > pos.y then
local R2 = MOB_SPAWN_ZONE_INNER
local y2 = math_sqrt( R2*R2 - d*d )
if goal_pos.y > pos. y then
-- Upper hemisphere
y_min = pos.y + y2
y_max = pos.y + y1
@ -476,9 +675,16 @@ local function get_next_mob_spawn_pos(pos)
y_max = pos.y - y2
end
end
y_min = math_round(y_min)
y_max = math_round(y_max)
-- Limit total range of check to 32 nodes (maximum of 3 map blocks)
y_min = math_max(math_floor(y_min), goal_pos.y - 16)
y_max = math_min(math_ceil(y_max), goal_pos.y + 16)
if y_max > goal_pos.y + 16 then
y_max = goal_pos.y + 16
end
if y_min < goal_pos.y - 16 then
y_min = goal_pos.y - 16
end
-- Ask engine for valid spawn locations
local spawning_position_list = find_nodes_in_area_under_air(
@ -510,12 +716,12 @@ end
--a simple helper function for mob_spawn
local function biome_check(biome_list, biome_goal)
if not biome_goal then return false end
for _, data in pairs(biome_list) do
if data == biome_goal then
return true
end
end
return false
end
@ -530,8 +736,8 @@ local function get_water_spawn(p)
end
end
local function has_room(self, pos)
local cb = self.spawnbox or self.collisionbox
local function has_room(self,pos)
local cb = self.collisionbox
local nodes = {}
if self.fly_in then
local t = type(self.fly_in)
@ -542,74 +748,18 @@ local function has_room(self, pos)
end
end
table.insert(nodes,"air")
-- Calculate area to check for room
local cb_height = cb[5] - cb[2]
local p1 = vector.new(
math.round(pos.x + cb[1]),
math.floor(pos.y),
math.round(pos.z + cb[3]))
local p2 = vector.new(
math.round(pos.x + cb[4]),
math.ceil(p1.y + cb_height) - 1,
math.round(pos.z + cb[6]))
-- Check if the entire spawn volume is free
local dx = p2.x - p1.x + 1
local dy = p2.y - p1.y + 1
local dz = p2.z - p1.z + 1
local found_nodes = minetest.find_nodes_in_area(p1,p2,nodes) or 0
local n = #found_nodes
if n == dx * dy * dz then
return true
local x = cb[4] - cb[1]
local y = cb[5] - cb[2]
local z = cb[6] - cb[3]
local r = math.ceil(x * y * z)
local p1 = vector.offset(pos,cb[1],cb[2],cb[3])
local p2 = vector.offset(pos,cb[4],cb[5],cb[6])
local n = #minetest.find_nodes_in_area(p1,p2,nodes) or 0
if r > n then
minetest.log("warning","[mcl_mobs] No room for mob "..self.name.." at "..minetest.pos_to_string(vector.round(pos)))
return false
end
-- If we don't have an implementation of get_node_boxes, we can't check for sub-node space
if not minetest.get_node_boxes then return false end
-- Check if it's possible for a sub-node space check to succeed
local needed_in_bottom_section = dx * ( dy - 1) * dz
if n < needed_in_bottom_section then return false end
-- Make sure the entire volume except for the top level is free before checking the top layer
if dy > 1 then
-- Remove nodes in the top layer from the count
for i = 1,#found_nodes do
if found_nodes[i].y == p2.y then
n = n - 1
end
end
-- If the entire volume except the top layer isn't air (or nodes) then we can't spawn this mob here
if n < needed_in_bottom_section then return false end
end
-- Check the top layer to see if we have enough space to spawn in
local top_layer_height = 1
local processed = {}
for x = p1.x,p2.x do
for z = p1.z,p2.z do
local test_pos = vector.new(x,p2.y,z)
local node = minetest.get_node(test_pos) or { name = "ignore" }
local cache_name = string.format("%s-%d", node.name, node.param2)
if not processed[cache_name] then
-- Calculate node bounding box and select the lowest y value
local boxes = minetest.get_node_boxes("collision_box", test_pos, node)
for i = 1,#boxes do
local box = boxes[i]
local y_test = box[2] + 0.5
if y_test < top_layer_height then top_layer_height = y_test end
local y_test = box[5] + 0.5
if y_test < top_layer_height then top_layer_height = y_test end
end
end
end
end
if top_layer_height + dy - 1 >= cb_height then return true end
-- We don't have room
return false
return true
end
mcl_mobs.custom_biomecheck = nil
@ -618,92 +768,119 @@ function mcl_mobs.register_custom_biomecheck(custom_biomecheck)
mcl_mobs.custom_biomecheck = custom_biomecheck
end
local function get_biome_name(pos)
if mcl_mobs.custom_biomecheck then return mcl_mobs.custom_biomecheck(pos) end
local gotten_biome = minetest.get_biome_data(pos)
return gotten_biome and mt_get_biome_name(gotten_biome.biome)
if mcl_mobs.custom_biomecheck then
return mcl_mobs.custom_biomecheck (pos)
else
local gotten_biome = minetest.get_biome_data(pos)
if not gotten_biome then
return
end
gotten_biome = mt_get_biome_name(gotten_biome.biome)
--minetest.log ("biome: " .. dump(gotten_biome))
return gotten_biome
end
end
local function spawn_check(pos, spawn_def)
if not spawn_def or not pos then return end
dbg_spawn_attempts = dbg_spawn_attempts + 1
local dimension = mcl_worlds.pos_to_dimension(pos)
if spawn_def.dimension ~= dimension then return end -- wrong dimension
-- find ground node below spawn position
local node_name = get_node(pos).name
local node_def = registered_nodes[node_name]
if node_def and not node_def.groups.solid then -- try node one below instead
pos.y = pos.y - 1
node_name = get_node(pos).name
node_def = registered_nodes[node_name]
end
if not node_def or not node_def.groups then return end
-- do not spawn on bedrock
if node_name == "mcl_core:bedrock" then return end
pos.y = pos.y + 1
-- check spawn height
if pos.y < spawn_def.min_height or pos.y > spawn_def.max_height then return end
mcl_log("spawn_check#1 position checks passed")
-- do not spawn ground mobs on leaves
if spawn_def.type_of_spawning == "ground" and (not node_def.groups.solid or node_def.groups.leaves) then return end
-- water mobs only on water
if spawn_def.type_of_spawning == "water" and not node_def.groups.water then return end
-- lava mobs only on lava
if spawn_def.type_of_spawning == "lava" and not node_def.groups.lava then return end
-- farm animals on grass only
if is_farm_animal(spawn_def.name) and not node_def.groups.grass_block then return end
---- More expensive calls:
-- check the biome
if spawn_def.biomes and not biome_check(spawn_def.biomes, get_biome_name(pos)) then return end
-- check if there is enough room
local mob_def = minetest.registered_entities[spawn_def.name]
if not has_room(mob_def,pos) then return end
-- additional checks (slime etc.)
if spawn_def.check_position and not spawn_def.check_position(pos) then return end
if spawn_protected and minetest.is_protected(pos, "") then return end
mcl_log("spawn_check#2 advanced checks passed")
local mob_type = mob_def.type
local gotten_node = get_node(pos).name
if not gotten_node then return end
-- check light thresholds
local gotten_light = get_node_light(pos)
-- old lighting
if not modern_lighting then return gotten_light >= spawn_def.min_light and gotten_light <= spawn_def.max_light end
local biome_name = get_biome_name(pos)
if not biome_name then return end
local sky_light = minetest.get_natural_light(pos)
local art_light = minetest.get_artificial_light(get_node(pos).param1)
if mob_def.spawn_check then
return mob_def.spawn_check(pos, gotten_light, art_light, sky_light)
local is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
if not is_ground then
pos.y = pos.y - 1
gotten_node = get_node(pos).name
is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
end
if mob_def.type == "monster" then
if dimension == "nether" then
if art_light <= nether_threshold then
return true
end
elseif dimension == "end" then
if art_light <= end_threshold then
return true
end
elseif dimension == "overworld" then
if art_light <= overworld_threshold and sky_light <= overworld_sky_threshold then
return true
pos.y = pos.y + 1
local is_water = get_item_group(gotten_node, "water") ~= 0
local is_lava = get_item_group(gotten_node, "lava") ~= 0
local is_leaf = get_item_group(gotten_node, "leaves") ~= 0
local is_bedrock = gotten_node == "mcl_core:bedrock"
local is_grass = minetest.get_item_group(gotten_node,"grass_block") ~= 0
if pos.y >= spawn_def.min_height
and pos.y <= spawn_def.max_height
and spawn_def.dimension == dimension
and biome_check(spawn_def.biomes, biome_name) then
mcl_log("Spawn level 1 check - Passed")
if (is_ground or spawn_def.type_of_spawning ~= "ground")
and (spawn_def.type_of_spawning ~= "ground" or not is_leaf)
and (not is_farm_animal(spawn_def.name) or is_grass)
and (spawn_def.type_of_spawning ~= "water" or is_water)
and not is_bedrock
and has_room(mob_def,pos)
and (spawn_def.check_position and spawn_def.check_position(pos) or spawn_def.check_position == nil)
and ( not spawn_protected or not minetest.is_protected(pos, "") ) then
mcl_log("Spawn level 2 check - Passed")
local gotten_light = get_node_light(pos)
if modern_lighting then
local my_node = get_node(pos)
local sky_light = minetest.get_natural_light(pos)
local art_light = minetest.get_artificial_light(my_node.param1)
if mob_def.spawn_check then
return mob_def.spawn_check(pos, gotten_light, art_light, sky_light)
elseif mob_type == "monster" then
if dimension == "nether" then
if art_light <= nether_threshold then
return true
end
elseif dimension == "end" then
if art_light <= end_threshold then
return true
end
elseif dimension == "overworld" then
if art_light <= overworld_threshold and sky_light <= overworld_sky_threshold then
return true
end
end
else
-- passive threshold is apparently the same in all dimensions ...
if gotten_light > overworld_passive_threshold then
return true
end
end
else
if gotten_light >= spawn_def.min_light and gotten_light <= spawn_def.max_light then
return true
end
end
end
return false
end
-- passive threshold is apparently the same in all dimensions ...
return gotten_light > overworld_passive_threshold
return false
end
function mcl_mobs.spawn(pos,id)
if not pos or not id then return false end
local def = minetest.registered_entities[id] or minetest.registered_entities["mobs_mc:"..id] or minetest.registered_entities["extra_mobs:"..id]
if not def or not def.is_mob or (def.can_spawn and not def.can_spawn(pos)) then return false end
if not has_room(def, pos) then return false end
if not def or (def.can_spawn and not def.can_spawn(pos)) or not def.is_mob then
return false
end
if not dbg_spawn_counts[def.name] then
dbg_spawn_counts[def.name] = 1
else
dbg_spawn_counts[def.name] = dbg_spawn_counts[def.name] + 1
end
return minetest.add_entity(pos, def.name)
end
local function spawn_group(p,mob,spawn_on,amount_to_spawn)
local nn= minetest.find_nodes_in_area_under_air(vector.offset(p,-5,-3,-5),vector.offset(p,5,3,5),spawn_on)
local o
@ -820,7 +997,7 @@ if mobs_spawn then
mob_total_wide = 0
end
local cap_space_wide = math_max(type_cap - mob_total_wide, 0)
local cap_space_wide = math.max(type_cap - mob_total_wide, 0)
mcl_log("mob_type", mob_type)
mcl_log("cap_space_wide", cap_space_wide)
@ -828,10 +1005,10 @@ if mobs_spawn then
local cap_space_available = 0
if mob_type == "hostile" then
mcl_log("cap_space_global", cap_space_hostile)
cap_space_available = math_min(cap_space_hostile, cap_space_wide)
cap_space_available = math.min(cap_space_hostile, cap_space_wide)
else
mcl_log("cap_space_global", cap_space_non_hostile)
cap_space_available = math_min(cap_space_non_hostile, cap_space_wide)
cap_space_available = math.min(cap_space_non_hostile, cap_space_wide)
end
local mob_total_close = mob_counts_close[mob_type]
@ -840,8 +1017,8 @@ if mobs_spawn then
mob_total_close = 0
end
local cap_space_close = math_max(close_zone_cap - mob_total_close, 0)
cap_space_available = math_min(cap_space_available, cap_space_close)
local cap_space_close = math.max(close_zone_cap - mob_total_close, 0)
cap_space_available = math.min(cap_space_available, cap_space_close)
mcl_log("cap_space_close", cap_space_close)
mcl_log("cap_space_available", cap_space_available)
@ -968,7 +1145,7 @@ if mobs_spawn then
local amount_to_spawn = math.random(group_min, spawn_in_group)
mcl_log("Spawning quantity: " .. amount_to_spawn)
amount_to_spawn = math_min(amount_to_spawn, cap_space_available)
amount_to_spawn = math.min(amount_to_spawn, cap_space_available)
mcl_log("throttled spawning quantity: " .. amount_to_spawn)
if logging then
@ -1019,8 +1196,8 @@ if mobs_spawn then
local players = get_connected_players()
local total_mobs, total_non_hostile, total_hostile = count_mobs_total_cap()
local cap_space_hostile = math_max(mob_cap.global_hostile - total_hostile, 0)
local cap_space_non_hostile = math_max(mob_cap.global_non_hostile - total_non_hostile, 0)
local cap_space_hostile = math.max(mob_cap.global_hostile - total_hostile, 0)
local cap_space_non_hostile = math.max(mob_cap.global_non_hostile - total_non_hostile, 0)
mcl_log("global cap_space_hostile", cap_space_hostile)
mcl_log("global cap_space_non_hostile", cap_space_non_hostile)
@ -1097,6 +1274,7 @@ end
minetest.register_chatcommand("mobstats",{
privs = { debug = true },
func = function(n,param)
--minetest.chat_send_player(n,dump(dbg_spawn_counts))
local pos = minetest.get_player_by_name(n):get_pos()
minetest.chat_send_player(n,"mobs: within 32 radius of player/total loaded :"..count_mobs(pos,MOB_CAP_INNER_RADIUS) .. "/" .. count_mobs_total())
minetest.chat_send_player(n,"spawning attempts since server start:" .. dbg_spawn_succ .. "/" .. dbg_spawn_attempts)

View File

@ -72,24 +72,18 @@ local axolotl = {
fly = true,
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
breathes_in_water = true,
jump = false, -- would get them out of the water too often
jump = true,
damage = 2,
reach = 2,
attack_type = "dogfight",
attack_animals = true,
specific_attack = {
"mobs_mc:cod",
"mobs_mc:glow_squid",
"mobs_mc:salmon",
"mobs_mc:tropical_fish",
"mobs_mc:squid",
"mobs_mc:zombie", -- todo: only drowned?
"mobs_mc:baby_zombie",
"mobs_mc:husk",
"mobs_mc:baby_husk",
"mobs_mc:guardian_elder",
"mobs_mc:guardian",
},
"extra_mobs_cod",
"extra_mobs_glow_squid",
"extra_mobs_salmon",
"extra_mobs_tropical_fish",
"mobs_mc_squid"
},
runaway = true,
}

View File

@ -95,8 +95,8 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
end
local pos = self.object:get_pos()
minetest.add_particle({
pos = {x=pos.x+(math.random()*0.7-0.35)*math.random(),y=pos.y+0.7+math.random()*0.5,z=pos.z+(math.random()*0.7-0.35)*math.random()},
velocity = {x=0, y=1, z=0},
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
velocity = {x=0, y=math.random(1,1), z=0},
expirationtime = math.random(),
size = math.random(1, 4),
collisiondetection = true,
@ -110,8 +110,8 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
},
})
minetest.add_particle({
pos = {x=pos.x+(math.random()*0.7-0.35)*math.random(),y=pos.y+0.7+math.random()*0.5,z=pos.z+(math.random()*0.7-0.35)*math.random()},
velocity = {x=0, y=1, z=0},
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
velocity = {x=0, y=math.random(1,1), z=0},
expirationtime = math.random(),
size = math.random(1, 4),
collisiondetection = true,
@ -125,8 +125,8 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
},
})
minetest.add_particle({
pos = {x=pos.x+(math.random()*0.7-0.35)*math.random(),y=pos.y+0.7+math.random()*0.5,z=pos.z+(math.random()*0.7-0.35)*math.random()},
velocity = {x=0, y=1, z=0},
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
velocity = {x=0, y=math.random(1,1), z=0},
expirationtime = math.random(),
size = math.random(1, 4),
collisiondetection = true,

View File

@ -84,7 +84,7 @@ local cod = {
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
if self.object:get_velocity().y < 5 then
self.object:add_velocity({ x = 0 , y = math.random()*.014-.007, z = 0 })
self.object:add_velocity({ x = 0 , y = math.random(-.007, .007), z = 0 })
end
end
--]]

View File

@ -81,16 +81,16 @@ local dolphin = {
reach = 2,
damage = 2.5,
attack_type = "dogfight",
--[[ this is supposed to make them jump out the water but doesn't appear to work very well
do_custom = function(self,dtime)
--[[ this is supposed to make them jump out the water but doesn't appear to work very well
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
if self.object:get_velocity().y < 5 then
self.object:add_velocity({ x = 0 , y = math.random()*.014-.007, z = 0 })
self.object:add_velocity({ x = 0 , y = math.random(-.007, .007), z = 0 })
end
end
--]]
end,
--]]
}
mcl_mobs.register_mob("mobs_mc:dolphin", dolphin)

View File

@ -4,17 +4,21 @@
local S = minetest.get_translator("mobs_mc")
local BEAM_CHECK_FREQUENCY = 1
local BEAM_CHECK_FREQUENCY = 2
local POS_CHECK_FREQUENCY = 15
local HEAL_INTERVAL = 1
local HEAL_AMOUNT = 2
local HEAL_AMMOUNT = 37
local function heal(self)
local o = self.object
self.health = math.min(self.hp_max,self.health + HEAL_AMMOUNT)
end
local function check_beam(self)
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
local luaentity = obj:get_luaentity()
if luaentity and luaentity.name == "mcl_end:crystal" then
if luaentity.beam then
if luaentity.beam == self.beam then
heal(self)
break
end
else
@ -102,6 +106,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
},
ignores_nametag = true,
do_custom = function(self,dtime)
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
if self._pos_timer == nil or self._pos_timer > POS_CHECK_FREQUENCY then
self._pos_timer = 0
check_pos(self)
@ -110,20 +115,8 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
self._beam_timer = 0
check_beam(self)
end
self._beam_timer = self._beam_timer + dtime
self._pos_timer = self._pos_timer + dtime
if self.beam ~= nil then
-- heal
self._heal_timer = (self._heal_timer or 0) + dtime
if self._heal_timer > HEAL_INTERVAL then
self.health = math.min(self.hp_max,self.health + HEAL_AMOUNT)
self._heal_timer = self._heal_timer - HEAL_INTERVAL
end
end
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
end,
on_die = function(self, pos, cmi_cause)
if self._portal_pos then

View File

@ -83,8 +83,8 @@ mcl_mobs.register_mob("mobs_mc:iron_golem", {
stand_speed = 15, walk_speed = 15, run_speed = 25, punch_speed = 15,
stand_start = 0, stand_end = 0,
walk_start = 0, walk_end = 40,
run_start = 40, run_end = 80,
punch_start = 80, punch_end = 90,
run_start = 0, run_end = 40,
punch_start = 40, punch_end = 50,
},
jump = true,
do_custom = function(self, dtime)

View File

@ -1,5 +1,5 @@
name = mobs_mc
author = maikerumine
description = Adds Minecraft-like monsters and animals.
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core, mcl_util, mcl_entity_invs
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core, mcl_util
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, doc_items, mcl_worlds

View File

@ -360,7 +360,7 @@ piglin_brute.xp_min = 20
piglin_brute.xp_max = 20
piglin_brute.hp_min = 50
piglin_brute.hp_max = 50
piglin_brute.fire_resistant = false
piglin_brute.fire_resistant = 1
piglin_brute.do_custom = function()
return
end
@ -371,8 +371,8 @@ piglin_brute.on_rightclick = function()
return
end
piglin_brute.attacks_monsters = true
piglin_brute.lava_damage = 4
piglin_brute.fire_damage = 2
piglin_brute.lava_damage = 0
piglin_brute.fire_damage = 0
piglin_brute.attack_animals = true
piglin_brute.mesh = "extra_mobs_sword_piglin.b3d"
piglin_brute.textures = {"extra_mobs_piglin_brute.png", "default_tool_goldaxe.png", "extra_mobs_trans.png"}

View File

@ -52,7 +52,7 @@ local salmon = {
makes_footstep_sound = false,
swim = true,
fly = true,
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
fly_in = "mcl_core:water_source",
breathes_in_water = true,
jump = false,
view_range = 16,

View File

@ -103,7 +103,6 @@ local skeleton = {
return true
end,
ignited_by_sunlight = true,
floats = 0,
view_range = 16,
fear_height = 4,
attack_type = "dogshoot",

View File

@ -94,7 +94,6 @@ mcl_mobs.register_mob("mobs_mc:witherskeleton", {
dogshoot_switch = 1,
dogshoot_count_max =0.5,
fear_height = 4,
floats = 0,
harmed_by_heal = true,
fire_resistant = true,
dealt_effect = {

View File

@ -1,31 +1,112 @@
--License for code WTFPL and otherwise stated in readmes
local S = minetest.get_translator("mobs_mc")
local MAPBLOCK_SIZE = 16 -- size for slime chunk logic
local SEED_OFFSET = 362 -- module specific seed
local world_seed = (minetest.get_mapgen_setting("seed") + SEED_OFFSET) % 4294967296
-- slime density, where default N=10 is every 10th chunk
local slime_ratio = tonumber(minetest.settings:get("slime_ratio")) or 10
-- use 3D chunking instead of 2d chunks
local slime_3d_chunks = minetest.settings:get_bool("slime_3d_chunks", false)
-- maximum light level, for slimes in caves only, not magma/swamps
local slime_max_light = (tonumber(minetest.settings:get("slime_max_light")) or minetest.LIGHT_MAX) + 1
-- maximum light level for swamp spawning
local swamp_light_max = 7
-- maximum height to spawn in slime chunks
local slime_chunk_spawn_max = mcl_worlds.layer_to_y(40)
local MAPBLOCK_SIZE = 16
local floor = math.floor
local max = math.max
local seed = minetest.get_mapgen_setting("seed")
local slime_chunk_match
local slime_chunk_spawn_max = mcl_worlds.layer_to_y(40)
local x_modifier
local z_modifier
local function split_by_char (inputstr, sep, limit)
if sep == nil then
sep = "%d"
end
local t = {}
local i = 0
for str in string.gmatch(inputstr, "(["..sep.."])") do
i = i --+ 1
table.insert(t, tonumber(str))
if limit and i >= limit then
break
end
end
return t
end
--Seed: "16002933932875202103" == random seed
--Seed: "1807191622654296300" == cheese
--Seed: "1" = 1
local function process_seed (seed)
--minetest.log("seed: " .. seed)
local split_chars = split_by_char(tostring(seed), nil, 10)
slime_chunk_match = split_chars[1]
x_modifier = split_chars[2]
z_modifier = split_chars[3]
--minetest.log("x_modifier: " .. tostring(x_modifier))
--minetest.log("z_modifier: " .. tostring(z_modifier))
--minetest.log("slime_chunk_match: " .. tostring(slime_chunk_match))
end
local processed = process_seed (seed)
local function convert_to_chunk_value (co_ord, modifier)
local converted = math.floor(math.abs(co_ord) / MAPBLOCK_SIZE)
if modifier then
converted = (converted + modifier)
end
converted = converted % 10
--minetest.log("co_ord: " .. co_ord)
--minetest.log("converted: " .. converted)
return converted
end
assert(convert_to_chunk_value(-16) == 1, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(-15) == 0, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(-1) == 0, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(0) == 0, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(1) == 0, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(15) == 0, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(16) == 1, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(31) == 1, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(32) == 2, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(1599) == 9, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(1600) == 0, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(0,9) == 9, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(16,5) == 6, "Incorrect convert_to_chunk_value result")
assert(convert_to_chunk_value(1599,4) == 3, "Incorrect convert_to_chunk_value result")
local function calculate_chunk_value (pos, x_mod, z_mod)
local chunk_val = math.abs(convert_to_chunk_value(pos.x, x_mod) - convert_to_chunk_value(pos.z, z_mod)) % 10
return chunk_val
end
assert(calculate_chunk_value(vector.new(0,0,0)) == 0, "calculate_chunk_value failed")
assert(calculate_chunk_value(vector.new(0,0,0), 1, 1) == 0, "calculate_chunk_value failed")
assert(calculate_chunk_value(vector.new(0,0,0), 2, 1) == 1, "calculate_chunk_value failed")
assert(calculate_chunk_value(vector.new(64,0,16)) == (4-1), "calculate_chunk_value failed")
assert(calculate_chunk_value(vector.new(16,0,64)) == (3), "calculate_chunk_value failed")
assert(calculate_chunk_value(vector.new(-160,0,-160)) == 0, "calculate_chunk_value failed")
local function is_slime_chunk(pos)
if not pos then return end -- no position given
if slime_ratio == 0 then return end -- no slime chunks
if slime_ratio <= 1 then return true end -- slime everywhere
local bpos = vector.new(floor(pos.x / MAPBLOCK_SIZE), slime_3d_chunks and floor(pos.y / MAPBLOCK_SIZE) or 0, floor(pos.z / MAPBLOCK_SIZE))
return PcgRandom(minetest.hash_node_position(bpos) + world_seed):next(0,1e9)/1e9 * slime_ratio < 1
if not pos then return end
local chunk_val = calculate_chunk_value (pos, x_modifier, z_modifier)
local slime_chunk = chunk_val == slime_chunk_match
--minetest.log("x: " ..pos.x .. ", z:" .. pos.z)
--minetest.log("seed slime_chunk_match: " .. tostring(slime_chunk_match))
--minetest.log("chunk_val: " .. tostring(chunk_val))
--minetest.log("Is slime chunk: " .. tostring(slime_chunk))
return slime_chunk
end
local check_position = function (pos)
return is_slime_chunk(pos)
end
-- Returns a function that spawns children in a circle around pos.
-- To be used as on_die callback.
-- self: mob reference
@ -35,15 +116,19 @@ end
-- eject_speed: Initial speed of child mob away from "mother" mob
local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed)
return function(self, pos)
eject_speed = eject_speed or 1
local posadd, newpos, dir
if not eject_speed then
eject_speed = 1
end
local mndef = minetest.registered_nodes[minetest.get_node(pos).name]
local mother_stuck = mndef and mndef.walkable
local angle = math.random() * math.pi * 2
local angle = math.random(0, math.pi*2)
local children = {}
local spawn_count = math.random(2, 4)
for i = 1, spawn_count do
local dir = vector.new(math.cos(angle), 0, math.sin(angle))
local newpos = pos + dir * spawn_distance
dir = vector.new(math.cos(angle), 0, math.sin(angle))
posadd = vector.normalize(dir) * spawn_distance
newpos = pos + posadd
-- If child would end up in a wall, use position of the "mother", unless
-- the "mother" was stuck as well
if not mother_stuck then
@ -53,14 +138,12 @@ local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed)
eject_speed = eject_speed * 0.5
end
end
local mob = mcl_mobs.spawn(newpos, child_mob)
if mob then
if not mother_stuck then
mob:set_velocity(dir * eject_speed)
end
mob:set_yaw(angle - math.pi/2)
table.insert(children, mob)
local mob = minetest.add_entity(newpos, child_mob)
if not mother_stuck then
mob:set_velocity(dir * eject_speed)
end
mob:set_yaw(angle - math.pi/2)
table.insert(children, mob)
angle = angle + (math.pi*2) / spawn_count
end
-- If mother was murdered, children attack the killer after 1 second
@ -79,12 +162,16 @@ local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed)
end
end
-- two different rules, underground slime chunks and regular swamp spawning
local swamp_light_max = 7
local function slime_spawn_check(pos, environmental_light, artificial_light, sky_light)
local maxlight = swamp_light_max
if pos.y <= slime_chunk_spawn_max and is_slime_chunk(pos) then
return max(artificial_light, sky_light) <= slime_max_light
maxlight = minetest.LIGHT_MAX + 1
end
return max(artificial_light, sky_light) <= swamp_light_max
return math.max(artificial_light, sky_light) <= maxlight
end
-- Slime
@ -235,13 +322,13 @@ mcl_mobs:spawn_specific(
"ground",
cave_biomes,
0,
slime_max_light,
minetest.LIGHT_MAX+1,
30,
1000,
4,
cave_min,
cave_max,
nil, nil, is_slime_chunk)
nil, nil, check_position)
mcl_mobs:spawn_specific(
"mobs_mc:slime_tiny",
@ -262,13 +349,13 @@ mcl_mobs:spawn_specific(
"ground",
cave_biomes,
0,
slime_max_light,
minetest.LIGHT_MAX+1,
30,
1000,
4,
cave_min,
cave_max,
nil, nil, is_slime_chunk)
nil, nil, check_position)
mcl_mobs:spawn_specific(
"mobs_mc:slime_small",
@ -289,13 +376,13 @@ mcl_mobs:spawn_specific(
"ground",
cave_biomes,
0,
slime_max_light,
minetest.LIGHT_MAX+1,
30,
1000,
4,
cave_min,
cave_max,
nil, nil, is_slime_chunk)
nil, nil, check_position)
mcl_mobs:spawn_specific(
"mobs_mc:slime_big",
@ -472,4 +559,3 @@ mcl_mobs:non_spawn_specific("mobs_mc:magma_cube_big","overworld",0, minetest.LIG
mcl_mobs.register_egg("mobs_mc:slime_big", S("Slime"), "#52a03e", "#7ebf6d")
-- FIXME: add spawn eggs for small and tiny slimes and magma cubes

View File

@ -67,7 +67,6 @@ local spider = {
curiosity = 10,
head_yaw="z",
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.89, 0.7},
spawnbox = {-1.2, -0.01, -1.2, 1.2, 0.89, 1.2},
visual = "mesh",
mesh = "mobs_mc_spider.b3d",
textures = {

View File

@ -7,13 +7,12 @@ local S = minetest.get_translator("mobs_mc")
--###################
local function get_texture(self, prev)
local standing_on = minetest.registered_nodes[self.standing_on]
-- TODO: we do not have access to param2 here (color palette index) yet
local function get_texture(self)
local on_name = self.standing_on
local texture
local texture_suff = ""
if standing_on and (standing_on.walkable or standing_on.groups.liquid) then
local tiles = standing_on.tiles
if on_name and on_name ~= "air" then
local tiles = minetest.registered_nodes[on_name].tiles
if tiles then
local tile = tiles[1]
local color
@ -26,7 +25,7 @@ local function get_texture(self, prev)
texture = tile
end
if not color then
color = minetest.colorspec_to_colorstring(standing_on.color)
color = minetest.colorspec_to_colorstring(minetest.registered_nodes[on_name].color)
end
if color then
texture_suff = "^[multiply:" .. color .. "^[hsl:0:0:20"
@ -34,19 +33,14 @@ local function get_texture(self, prev)
end
end
if not texture or texture == "" then
-- try to keep last texture when, e.g., falling
if prev and (not (not self.attack)) == (string.find(prev, "vl_mobs_stalker_overlay_angry.png") ~= nil) then
return prev
end
texture = "vl_stalker_default.png"
else
texture = texture:gsub("([\\^:\\[])", "\\%1") -- escape texture modifiers
texture = "(vl_stalker_default.png^[combine:16x24:0,0=(" .. texture .. "):0,16=(" .. texture .. ")" .. texture_suff .. ")"
end
texture = texture:gsub("([\\^:\\[])","\\%1") -- escape texture modifiers
texture = "([combine:16x24:0,0=(" .. texture .. "):0,16=(" .. texture ..")".. texture_suff
if self.attack then
texture = texture .. "^vl_mobs_stalker_overlay_angry.png"
texture = texture .. ")^vl_mobs_stalker_overlay_angry.png"
else
texture = texture .. "^vl_mobs_stalker_overlay.png"
texture = texture .. ")^vl_mobs_stalker_overlay.png"
end
return texture
end
@ -138,7 +132,7 @@ mcl_mobs.register_mob("mobs_mc:stalker", {
self:boom(mcl_util.get_object_center(self.object), self.explosion_strength)
end
end
local new_texture = get_texture(self, self._stalker_texture)
local new_texture = get_texture(self)
if self._stalker_texture ~= new_texture then
self.object:set_properties({textures={new_texture, "mobs_mc_empty.png"}})
self._stalker_texture = new_texture

View File

@ -97,7 +97,7 @@ local tropical_fish = {
makes_footstep_sound = false,
swim = true,
fly = true,
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
fly_in = "mcl_core:water_source",
breathes_in_water = true,
jump = false,
view_range = 16,

View File

@ -818,7 +818,7 @@ local function find_closest_bed (self)
if (owned_by and owned_by == self._id) then
mcl_log("Clear as already owned by me.")
bed_meta:set_string("villager", "")
bed_meta:set_string("villager", nil)
owned_by = nil
end
@ -1049,18 +1049,14 @@ local function has_summon_participants(self)
end
local function summon_golem(self)
local pos = self.object:get_pos()
local p1 = vector.offset(pos, -10, -10, -10)
local p2 = vector.offset(pos, 10, 10, 10)
local nn = minetest.find_nodes_in_area_under_air(p1, p2,{"group:solid","group:water"})
while #nn > 0 do
local n = table.remove_random_element(nn)
n.y = n.y + 1
local summon = mcl_mobs.spawn(n, "mobs_mc:iron_golem")
if summon then
vector.offset(self.object:get_pos(),-10,-10,-10)
local nn = minetest.find_nodes_in_area_under_air(vector.offset(self.object:get_pos(),-10,-10,-10),vector.offset(self.object:get_pos(),10,10,10),{"group:solid","group:water"})
table.shuffle(nn)
for _,n in pairs(nn) do
local up = minetest.find_nodes_in_area(vector.offset(n,0,1,0),vector.offset(n,0,3,0),{"air"})
if up and #up >= 3 then
minetest.sound_play("mcl_portals_open_end_portal", {pos=n, gain=0.5, max_hear_distance = 16}, true)
return summon
return minetest.add_entity(vector.offset(n,0,1,0),"mobs_mc:iron_golem")
end
end
end
@ -1283,7 +1279,7 @@ local function validate_jobsite(self)
mcl_log("Jobsite far, so resettle: " .. tostring(resettle))
if resettle then
local m = minetest.get_meta(self._jobsite)
m:set_string("villager", "")
m:set_string("villager", nil)
remove_job (self)
return false
end
@ -1425,7 +1421,7 @@ local function validate_bed(self)
mcl_log("Bed far, so resettle: " .. tostring(resettle))
if resettle then
mcl_log("Resettled. Ditch bed.")
m:set_string("villager", "")
m:set_string("villager", nil)
self._bed = nil
bed_valid = false
return false
@ -1435,7 +1431,7 @@ local function validate_bed(self)
mcl_log("Player owner: " .. owned_by_player)
if owned_by_player ~= "" then
mcl_log("Player owns this. Villager won't take this.")
m:set_string("villager", "")
m:set_string("villager", nil)
self._bed = nil
bed_valid = false
return false
@ -2304,13 +2300,13 @@ mcl_mobs.register_mob("mobs_mc:villager", {
local bed = self._bed
if bed then
local bed_meta = minetest.get_meta(bed)
bed_meta:set_string("villager", "")
bed_meta:set_string("villager", nil)
mcl_log("Died, so bye bye bed")
end
local jobsite = self._jobsite
if jobsite then
local jobsite_meta = minetest.get_meta(jobsite)
jobsite_meta:set_string("villager", "")
jobsite_meta:set_string("villager", nil)
mcl_log("Died, so bye bye jobsite")
end

View File

@ -55,16 +55,14 @@ mcl_mobs.register_mob("mobs_mc:evoker", {
basepos.y = basepos.y + 1
for i=1, r do
local spawnpos = vector.add(basepos, minetest.yaw_to_dir(pr:next(0,360)))
local vex = mcl_mobs.spawn(spawnpos, "mobs_mc:vex")
if vex then
local ent = vex:get_luaentity()
local vex = minetest.add_entity(spawnpos, "mobs_mc:vex")
local ent = vex:get_luaentity()
-- Mark vexes as summoned and start their life clock (they take damage it reaches 0)
ent._summoned = true
ent._lifetimer = pr:next(33, 108)
-- Mark vexes as summoned and start their life clock (they take damage it reaches 0)
ent._summoned = true
ent._lifetimer = pr:next(33, 108)
table.insert(spawned_vexes[self],ent)
end
table.insert(spawned_vexes[self],ent)
end
end,
passive = false,

View File

@ -134,7 +134,6 @@ mcl_mobs.register_mob("mobs_mc:villager_zombie", {
end,
sunlight_damage = 2,
ignited_by_sunlight = true,
floats = 0,
view_range = 16,
fear_height = 4,
harmed_by_heal = true,

View File

@ -96,7 +96,6 @@ local zombie = {
},
ignited_by_sunlight = true,
sunlight_damage = 2,
floats = 0,
view_range = 16,
attack_type = "dogfight",
harmed_by_heal = true,

View File

@ -19,6 +19,7 @@ local set_node = minetest.set_node
local sound_play = minetest.sound_play
local add_particlespawner = minetest.add_particlespawner
local after = minetest.after
local add_entity = minetest.add_entity
local get_objects_inside_radius = minetest.get_objects_inside_radius
local get_item_group = minetest.get_item_group
@ -164,7 +165,7 @@ function lightning.strike_func(pos, pos2, objects)
-- Events caused by the lightning strike: Fire, damage, mob transformations, rare skeleton spawn
pos2.y = pos2.y + 1
pos2.y = pos2.y + 1/2
local skeleton_lightning = false
if rng:next(1,100) <= 3 then
skeleton_lightning = true
@ -173,14 +174,14 @@ function lightning.strike_func(pos, pos2, objects)
if get_node(pos2).name == "air" then
-- Low chance for a lightning to spawn skeleton horse + skeletons
if skeleton_lightning then
mcl_mobs.spawn(pos2, "mobs_mc:skeleton_horse")
add_entity(pos2, "mobs_mc:skeleton_horse")
local angle, posadd
angle = math.random() * math.pi * 2
angle = math.random(0, math.pi*2)
for i=1,3 do
posadd = { x=math.cos(angle),y=0,z=math.sin(angle) }
posadd = vector.normalize(posadd)
local mob = mcl_mobs.spawn(vector.add(pos2, posadd), "mobs_mc:skeleton")
local mob = add_entity(vector.add(pos2, posadd), "mobs_mc:skeleton")
if mob then
mob:set_yaw(angle-math.pi/2)
end

View File

@ -1,4 +1,4 @@
name = mcl_void_damage
author = Wuzzy
description = Deal damage to entities stuck in the deep void
depends = mcl_worlds, mcl_spawn
depends = mcl_worlds

View File

@ -1,5 +1,5 @@
name = mcl_weather
author = xeranas
description = Weather and sky handling: Rain, snow, thunderstorm, End and Nether ambience
depends = mcl_init, mcl_worlds, mcl_playerinfo, mcl_util
depends = mcl_init, mcl_worlds
optional_depends = lightning

View File

@ -60,15 +60,13 @@ end
-- set skybox based on time (uses skycolor api)
function mcl_weather.rain.set_sky_box()
if mcl_weather.state == "rain" then
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-rain-sky" then
mcl_weather.skycolor.add_layer(
"weather-pack-rain-sky",
{{r=0, g=0, b=0},
{r=85, g=86, b=98},
{r=135, g=135, b=151},
{r=85, g=86, b=98},
{r=0, g=0, b=0}})
end
mcl_weather.skycolor.add_layer(
"weather-pack-rain-sky",
{{r=0, g=0, b=0},
{r=85, g=86, b=98},
{r=135, g=135, b=151},
{r=85, g=86, b=98},
{r=0, g=0, b=0}})
mcl_weather.skycolor.active = true
for _, player in pairs(get_connected_players()) do
player:set_clouds({color="#5D5D5FE8"})
@ -157,7 +155,6 @@ function mcl_weather.rain.clear()
mcl_weather.rain.remove_sound(player)
mcl_weather.rain.remove_player(player)
mcl_weather.remove_spawners_player(player)
player:set_clouds({color="#FFF0EF"})
end
end

View File

@ -1,20 +1,15 @@
-- Constants
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local NIGHT_VISION_RATIO = 0.45
local DEBUG = false
-- Settings
local minimum_update_interval = { 250e3 }
-- Module state
local mods_loaded = false
local NIGHT_VISION_RATIO = 0.45
local MINIMUM_LIGHT_LEVEL = 0.2
local water_color = "#3F76E4"
local mg_name = minetest.get_mapgen_setting("mg_name")
function mcl_weather.set_sky_box_clear(player, sky, fog)
-- Make sure the player's head isn't in water before changing the skybox
local node_head = mcl_playerinfo[player:get_player_name()].node_head
if minetest.get_item_group(node_head, "water") ~= 0 then return end
local pos = player:get_pos()
if minetest.get_item_group(minetest.get_node(vector.new(pos.x,pos.y+1.5,pos.z)).name, "water") ~= 0 then return end
local sc = {
day_sky = "#7BA4FF",
day_horizon = "#C0D8FF",
@ -43,10 +38,8 @@ function mcl_weather.set_sky_box_clear(player, sky, fog)
end
function mcl_weather.set_sky_color(player, def)
-- Make sure the player's head isn't in water before changing the skybox
local node_head = mcl_playerinfo[player:get_player_name()].node_head
if minetest.get_item_group(node_head, "water") ~= 0 then return end
local pos = player:get_pos()
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1.5, 0)).name, "water") ~= 0 then return end
player:set_sky({
type = def.type,
sky_color = def.sky_color,
@ -54,7 +47,25 @@ function mcl_weather.set_sky_color(player, def)
})
end
local skycolor = {
-- Function to work out light modifier at different times
-- Noon is brightest, midnight is darkest, 0600 and 18000 is in the middle of this
local function get_light_modifier(time)
-- 0.1 = 0.2
-- 0.4 = 0.8
-- 0.5 = 1
-- 0.6 = 0.8
-- 0.9 = 0.2
local light_multiplier = time * 2
if time > 0.5 then
light_multiplier = 2 * (1 - time)
else
light_multiplier = time / 0.5
end
return light_multiplier
end
mcl_weather.skycolor = {
-- Should be activated before do any effect.
active = true,
@ -74,226 +85,319 @@ local skycolor = {
-- number of colors while constructing gradient of user given colors
max_val = 1000,
NIGHT_VISION_RATIO = NIGHT_VISION_RATIO,
-- Table for tracking layer order
layer_names = {},
utils = {},
-- To layer to colors table
add_layer = function(layer_name, layer_color, instant_update)
mcl_weather.skycolor.colors[layer_name] = layer_color
table.insert(mcl_weather.skycolor.layer_names, layer_name)
mcl_weather.skycolor.force_update = true
end,
current_layer_name = function()
return mcl_weather.skycolor.layer_names[#mcl_weather.skycolor.layer_names]
end,
-- Retrieve layer from colors table
retrieve_layer = function()
local last_layer = mcl_weather.skycolor.current_layer_name()
return mcl_weather.skycolor.colors[last_layer]
end,
-- Remove layer from colors table
remove_layer = function(layer_name)
for k, name in pairs(mcl_weather.skycolor.layer_names) do
if name == layer_name then
table.remove(mcl_weather.skycolor.layer_names, k)
mcl_weather.skycolor.force_update = true
return
end
end
end,
-- Wrapper for updating day/night ratio that respects night vision
override_day_night_ratio = function(player, ratio)
local meta = player:get_meta()
local has_night_vision = meta:get_int("night_vision") == 1
local has_darkness = meta:get_int("darkness") == 1
local is_visited_shepherd = meta:get_int("mcl_shepherd:special") == 1
local arg
if has_darkness and not is_visited_shepherd then
if has_night_vision then arg = 0.1
else arg = 0 end
else
-- Apply night vision only for dark sky
local is_dark = minetest.get_timeofday() > 0.8 or minetest.get_timeofday() < 0.2 or mcl_weather.state ~= "none"
local pos = player:get_pos()
local dim = mcl_worlds.pos_to_dimension(pos)
if (has_night_vision or is_visited_shepherd) and is_dark and dim ~= "nether" and dim ~= "end" then
if ratio == nil then
arg = NIGHT_VISION_RATIO
else
arg = math.max(ratio, NIGHT_VISION_RATIO)
end
else
arg = ratio
end
end
player:override_day_night_ratio(arg)
end,
-- Update sky color. If players not specified update sky for all players.
update_sky_color = function(players)
-- Override day/night ratio as well
players = mcl_weather.skycolor.utils.get_players(players)
for _, player in ipairs(players) do
local pos = player:get_pos()
local dim = mcl_worlds.pos_to_dimension(pos)
local has_weather = (mcl_worlds.has_weather(pos) and (mcl_weather.state == "snow" or mcl_weather.state =="rain" or mcl_weather.state == "thunder") and mcl_weather.has_snow(pos)) or ((mcl_weather.state =="rain" or mcl_weather.state == "thunder") and mcl_weather.has_rain(pos))
local checkname = minetest.get_node(vector.new(pos.x,pos.y+1.5,pos.z)).name
if minetest.get_item_group(checkname, "water") ~= 0 then
local biome_index = minetest.get_biome_data(player:get_pos()).biome
local biome_name = minetest.get_biome_name(biome_index)
local biome = minetest.registered_biomes[biome_name]
if biome then water_color = biome._mcl_waterfogcolor end
if not biome then water_color = "#3F76E4" end
if checkname == "mclx_core:river_water_source" or checkname == "mclx_core:river_water_flowing" then water_color = "#0084FF" end
player:set_sky({ type = "regular",
sky_color = {
day_sky = water_color,
day_horizon = water_color,
dawn_sky = water_color,
dawn_horizon = water_color,
night_sky = water_color,
night_horizon = water_color,
indoors = water_color,
fog_sun_tint = water_color,
fog_moon_tint = water_color,
fog_tint_type = "custom"
},
clouds = false,
})
end
if dim == "overworld" then
local biomesky
local biomefog
if mg_name ~= "v6" and mg_name ~= "singlenode" then
local biome_index = minetest.get_biome_data(player:get_pos()).biome
local biome_name = minetest.get_biome_name(biome_index)
local biome = minetest.registered_biomes[biome_name]
if biome then
--minetest.log("action", string.format("Biome found for number: %s in biome: %s", tostring(biome_index), biome_name))
biomesky = biome._mcl_skycolor
biomefog = biome._mcl_fogcolor
else
--minetest.log("action", string.format("No biome for number: %s in biome: %s", tostring(biome_index), biome_name))
end
end
if (mcl_weather.state == "none") then
-- Clear weather
mcl_weather.set_sky_box_clear(player,biomesky,biomefog)
player:set_sun({visible = true, sunrise_visible = true})
player:set_moon({visible = true})
player:set_stars({visible = true})
mcl_weather.skycolor.override_day_night_ratio(player, nil)
elseif not has_weather then
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.15)
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.27)
local night_color = mcl_weather.skycolor.get_sky_layer_color(0.1)
mcl_weather.set_sky_color(player, {
type = "regular",
sky_color = {
day_sky = day_color,
day_horizon = day_color,
dawn_sky = dawn_color,
dawn_horizon = dawn_color,
night_sky = night_color,
night_horizon = night_color,
},
clouds = true,
})
player:set_sun({visible = false, sunrise_visible = false})
player:set_moon({visible = false})
player:set_stars({visible = false})
elseif has_weather then
-- Weather skies
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5)
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.75)
local night_color = mcl_weather.skycolor.get_sky_layer_color(0)
mcl_weather.set_sky_color(player, {
type = "regular",
sky_color = {
day_sky = day_color,
day_horizon = day_color,
dawn_sky = dawn_color,
dawn_horizon = dawn_color,
night_sky = night_color,
night_horizon = night_color,
},
clouds = true,
})
player:set_sun({visible = false, sunrise_visible = false})
player:set_moon({visible = false})
player:set_stars({visible = false})
local light_factor = mcl_weather.get_current_light_factor()
if mcl_weather.skycolor.current_layer_name() == "lightning" then
mcl_weather.skycolor.override_day_night_ratio(player, 1)
elseif light_factor then
local time = minetest.get_timeofday()
local light_multiplier = get_light_modifier(time)
local new_light = math.max(light_factor * light_multiplier, MINIMUM_LIGHT_LEVEL)
mcl_weather.skycolor.override_day_night_ratio(player, new_light)
else
mcl_weather.skycolor.override_day_night_ratio(player, nil)
end
end
elseif dim == "end" then
local biomesky = "#000000"
local biomefog = "#A080A0"
if mg_name ~= "v6" and mg_name ~= "singlenode" then
local biome_index = minetest.get_biome_data(player:get_pos()).biome
local biome_name = minetest.get_biome_name(biome_index)
local biome = minetest.registered_biomes[biome_name]
if biome then
--minetest.log("action", string.format("Biome found for number: %s in biome: %s", tostring(biome_index), biome_name))
biomesky = biome._mcl_skycolor
biomefog = biome._mcl_fogcolor -- The End biomes seemingly don't use the fog colour, despite having this value according to the wiki. The sky colour is seemingly used for both sky and fog?
else
--minetest.log("action", string.format("No biome for number: %s in biome: %s", tostring(biome_index), biome_name))
end
end
local t = "mcl_playerplus_end_sky.png"
player:set_sky({ type = "skybox",
base_color = biomesky,
textures = {t,t,t,t,t,t},
clouds = false,
})
player:set_sun({visible = false , sunrise_visible = false})
player:set_moon({visible = false})
player:set_stars({visible = false})
mcl_weather.skycolor.override_day_night_ratio(player, 0.5)
elseif dim == "nether" then
local biomesky = "#6EB1FF"
local biomefog = "#330808"
if mg_name ~= "v6" and mg_name ~= "singlenode" then
local biome_index = minetest.get_biome_data(player:get_pos()).biome
local biome_name = minetest.get_biome_name(biome_index)
local biome = minetest.registered_biomes[biome_name]
if biome then
--minetest.log("action", string.format("Biome found for number: %s in biome: %s", tostring(biome_index), biome_name))
biomesky = biome._mcl_skycolor -- The Nether biomes seemingly don't use the sky colour, despite having this value according to the wiki. The fog colour is used for both sky and fog.
biomefog = biome._mcl_fogcolor
else
--minetest.log("action", string.format("No biome for number: %s in biome: %s", tostring(biome_index), biome_name))
end
end
mcl_weather.set_sky_color(player, {
type = "regular",
sky_color = {
day_sky = biomefog,
day_horizon = biomefog,
dawn_sky = biomefog,
dawn_horizon = biomefog,
night_sky = biomefog,
night_horizon = biomefog,
indoors = biomefog,
fog_sun_tint = biomefog,
fog_moon_tint = biomefog,
fog_tint_type = "custom"
},
clouds = false,
})
player:set_sun({visible = false , sunrise_visible = false})
player:set_moon({visible = false})
player:set_stars({visible = false})
mcl_weather.skycolor.override_day_night_ratio(player, nil)
elseif dim == "void" then
player:set_sky({ type = "plain",
base_color = "#000000",
clouds = false,
})
player:set_sun({visible = false, sunrise_visible = false})
player:set_moon({visible = false})
player:set_stars({visible = false})
end
end
end,
-- Returns current layer color in {r, g, b} format
get_sky_layer_color = function(timeofday)
if #mcl_weather.skycolor.layer_names == 0 then
return nil
end
-- min timeofday value 0; max timeofday value 1. So sky color gradient range will be between 0 and 1 * mcl_weather.skycolor.max_val.
local rounded_time = math.floor(timeofday * mcl_weather.skycolor.max_val)
local color = mcl_weather.skycolor.utils.convert_to_rgb(mcl_weather.skycolor.min_val, mcl_weather.skycolor.max_val, rounded_time, mcl_weather.skycolor.retrieve_layer())
return color
end,
utils = {
convert_to_rgb = function(minval, maxval, current_val, colors)
local max_index = #colors - 1
local val = (current_val-minval) / (maxval-minval) * max_index + 1.0
local index1 = math.floor(val)
local index2 = math.min(math.floor(val)+1, max_index + 1)
local f = val - index1
local c1 = colors[index1]
local c2 = colors[index2]
return {r=math.floor(c1.r + f*(c2.r - c1.r)), g=math.floor(c1.g + f*(c2.g-c1.g)), b=math.floor(c1.b + f*(c2.b - c1.b))}
end,
-- Simply getter. Ether returns user given players list or get all connected players if none provided
get_players = function(players)
if players == nil or #players == 0 then
if mods_loaded then
players = minetest.get_connected_players()
elseif players == nil then
players = {}
end
end
return players
end,
-- Returns first player sky color. I assume that all players are in same color layout.
get_current_bg_color = function()
local players = mcl_weather.skycolor.utils.get_players(nil)
if players[1] then
return players[1]:get_sky(true).sky_color
end
return nil
end
},
}
mcl_weather.skycolor = skycolor
local skycolor_utils = skycolor.utils
-- Add layer to colors table
function skycolor.add_layer(layer_name, layer_color, instant_update)
skycolor.colors[layer_name] = layer_color
table.insert(skycolor.layer_names, layer_name)
skycolor.force_update = true
end
function skycolor.current_layer_name()
return skycolor.layer_names[#skycolor.layer_names]
end
-- Retrieve layer from colors table
function skycolor.retrieve_layer()
local last_layer = skycolor.current_layer_name()
return skycolor.colors[last_layer]
end
-- Remove layer from colors table
function skycolor.remove_layer(layer_name)
for k, name in pairs(skycolor.layer_names) do
if name == layer_name then
table.remove(skycolor.layer_names, k)
skycolor.force_update = true
return
end
end
end
-- Wrapper for updating day/night ratio that respects night vision
function skycolor.override_day_night_ratio(player, ratio)
player._skycolor_day_night_ratio = ratio
skycolor.update_player_sky_color(player)
player._skycolor_day_night_ratio = nil
end
local skycolor_filters = {}
skycolor.filters = skycolor_filters
dofile(modpath.."/skycolor/water.lua")
dofile(modpath.."/skycolor/dimensions.lua")
dofile(modpath.."/skycolor/effects.lua")
local function get_skycolor_info(player)
local player_name = player:get_player_name()
local info = mcl_playerinfo[player_name] or {}
local skycolor_data = info.skycolor
if not skycolor_data then
skycolor_data = {}
info.skycolor = skycolor_data
end
return skycolor_data
end
local water_sky = skycolor.water_sky
function skycolor.update_player_sky_color(player)
-- Don't update more than once every 250 milliseconds
local skycolor_data = get_skycolor_info(player)
local last_update = skycolor_data.last_update or 0
local now_us = minetest.get_us_time()
if (now_us - last_update) < minimum_update_interval[1] then return end
skycolor_data.last_update = now_us
local sky_data = {
day_night_ratio = player._skycolor_day_night_ratio
}
for i = 1,#skycolor_filters do
skycolor_filters[i](player, sky_data)
end
assert(sky_data.sky)
player:set_sky(sky_data.sky)
if sky_data.sun then player:set_sun(sky_data.sun) end
if sky_data.moon then player:set_moon(sky_data.moon) end
if sky_data.stars then player:set_stars(sky_data.stars) end
player:override_day_night_ratio(sky_data.day_night_ratio)
end
-- Update sky color. If players not specified update sky for all players.
function skycolor.update_sky_color(players)
-- Override day/night ratio as well
players = skycolor_utils.get_players(players)
local update = skycolor.update_player_sky_color
for _, player in ipairs(players) do
update(player)
end
end
-- Returns current layer color in {r, g, b} format
function skycolor.get_sky_layer_color(timeofday)
if #skycolor.layer_names == 0 then
return nil
end
-- min timeofday value 0; max timeofday value 1. So sky color gradient range will be between 0 and 1 * skycolor.max_val
local rounded_time = math.floor(timeofday * skycolor.max_val)
return skycolor_utils.convert_to_rgb(
skycolor.min_val, skycolor.max_val,
rounded_time, skycolor.retrieve_layer()
)
end
function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
-- Clamp current_val to valid range
current_val = math.max(minval, current_val)
current_val = math.min(maxval, current_val)
-- Rescale current_val from a number between minval and maxval to a number between 1 and #colors
local scaled_value = (current_val - minval) / (maxval - minval) * (#colors - 1) + 1.0
-- Get the first color's values
local index1 = math.floor(scaled_value)
local color1 = colors[index1]
local frac1 = 1.0 - (scaled_value - index1)
-- Get the second color's values
local index2 = math.min(index1 + 1, #colors) -- clamp to maximum color index (will occur if index1 == #colors)
local frac2 = 1.0 - frac1
local color2 = colors[index2]
-- Interpolate between color1 and color2
local res = {
r = math.floor(frac1 * color1.r + frac2 * color2.r),
g = math.floor(frac1 * color1.g + frac2 * color2.g),
b = math.floor(frac1 * color1.b + frac2 * color2.b),
}
if DEBUG then
minetest.log(dump({
minval = minval,
maxval = maxval,
current_val = current_val,
colors = colors,
res = res,
scaled_value = scaled_value,
frac1 = frac1,
index1 = index1,
color1 = color1,
frac2 = frac2,
index2 = index2,
color2 = color2,
}))
end
return res
end
-- Simple getter. Either returns user given players list or get all connected players if none provided
function skycolor_utils.get_players(players)
if players == nil or #players == 0 then
if mods_loaded then
players = minetest.get_connected_players()
elseif players == nil then
players = {}
end
end
return players
end
-- Returns the sky color of the first player, which is done assuming that all players are in same color layout.
function skycolor_utils.get_current_bg_color()
local players = skycolor_utils.get_players(nil)
if players[1] then
return players[1]:get_sky(true).sky_color
end
return nil
end
local timer = 0
minetest.register_globalstep(function(dtime)
if skycolor.active ~= true or #minetest.get_connected_players() == 0 then
if mcl_weather.skycolor.active ~= true or #minetest.get_connected_players() == 0 then
return
end
if skycolor.force_update then
skycolor.update_sky_color()
skycolor.force_update = false
if mcl_weather.skycolor.force_update then
mcl_weather.skycolor.update_sky_color()
mcl_weather.skycolor.force_update = false
return
end
-- regular updates based on iterval
timer = timer + dtime;
if timer >= skycolor.update_interval then
skycolor.update_sky_color()
if timer >= mcl_weather.skycolor.update_interval then
mcl_weather.skycolor.update_sky_color()
timer = 0
end
end)
local function initsky(player)
if player.set_lighting then
player:set_lighting({
shadows = { intensity = 0.33 },
volumetric_light = { strength = 0.45 },
exposure = {
luminance_min = -3.5,
luminance_max = -2.5,
exposure_correction = 0.35,
speed_dark_bright = 1500,
speed_bright_dark = 700,
},
saturation = 1.1,
})
player:set_lighting({ shadows = { intensity = tonumber(minetest.settings:get("mcl_default_shadow_intensity") or 0.33) } })
end
if (skycolor.active) then
if (mcl_weather.skycolor.active) then
mcl_weather.skycolor.force_update = true
end
@ -304,7 +408,7 @@ minetest.register_on_joinplayer(initsky)
minetest.register_on_respawnplayer(initsky)
mcl_worlds.register_on_dimension_change(function(player)
skycolor.update_sky_color({player})
mcl_weather.skycolor.update_sky_color({player})
end)
minetest.register_on_mods_loaded(function()

View File

@ -1,185 +0,0 @@
local MINIMUM_LIGHT_LEVEL = 0.2
local VALID_SNOW_WEATHER_STATES = { snow = true, rain = true, thunder = true }
local VALID_RAIN_WEATHER_STATES = { rain = true, thunder = true }
local mg_name = minetest.get_mapgen_setting("mg_name")
local dimension_handlers = {}
mcl_weather.skycolor.dimension_handlers = dimension_handlers
-- Function to work out light modifier at different times
-- Noon is brightest, midnight is darkest, 0600 and 1800 is in the middle of this
local function get_light_modifier(time)
-- 0.1 = 0.2
-- 0.4 = 0.8
-- 0.5 = 1
-- 0.6 = 0.8
-- 0.9 = 0.2
local light_multiplier = time * 2
if time > 0.5 then
light_multiplier = 2 * (1 - time)
else
light_multiplier = time / 0.5
end
return light_multiplier
end
function dimension_handlers.overworld(player, sky_data)
local pos = player:get_pos()
local biomesky
local biomefog
if mg_name ~= "v6" and mg_name ~= "singlenode" then
local biome_index = minetest.get_biome_data(player:get_pos()).biome
local biome_name = minetest.get_biome_name(biome_index)
local biome = minetest.registered_biomes[biome_name]
if biome then
biomesky = biome._mcl_skycolor
biomefog = biome._mcl_fogcolor
end
end
-- Use overworld defaults
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5)
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.27)
local night_color = mcl_weather.skycolor.get_sky_layer_color(0.1)
sky_data.sky = {
type = "regular",
sky_color = {
day_sky = day_color or "#7BA4FF",
day_horizon = day_color or "#C0D8FF",
dawn_sky = dawn_color or "7BA4FF",
dawn_horizon = dawn_color or "#C0D8FF",
night_sky = night_color or "000000",
night_horizon = night_color or "4A6790",
fog_sun_tint = "#ff5f33",
fog_moon_tint = nil,
fog_tint_type = "custom",
},
clouds = true,
}
sky_data.sun = {visible = true, sunrise_visible = true}
sky_data.moon = {visible = true}
sky_data.stars = {visible = true}
if mcl_weather.state == "none" then
-- Clear weather
mcl_weather.set_sky_box_clear(player,biomesky,biomefog)
return
end
-- Check if we currently have weather that affects the sky color
local has_weather = mcl_worlds.has_weather(pos) and (
mcl_weather.has_snow(pos) and VALID_SNOW_WEATHER_STATES[mcl_weather.state] or
mcl_weather.has_rain(pos) and VALID_RAIN_WEATHER_STATES[mcl_weather.state]
)
if has_weather then
-- Weather skies
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5)
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.75)
local night_color = mcl_weather.skycolor.get_sky_layer_color(0)
table.update(sky_data.sky.sky_color,{
day_sky = day_color or "#7BA4FF",
day_horizon = day_color or "#C0D8FF",
dawn_sky = dawn_color or "7BA4FF",
dawn_horizon = dawn_color or "#C0D8FF",
night_sky = night_color or "000000",
night_horizon = night_color or "4A6790",
fog_tint_type = "default",
})
sky_data.sun = {visible = false, sunrise_visible = false}
sky_data.moon = {visible = false}
sky_data.stars = {visible = false}
local light_factor = mcl_weather.get_current_light_factor()
if mcl_weather.skycolor.current_layer_name() == "lightning" then
sky_data.day_night_ratio = 1
elseif light_factor then
local time = minetest.get_timeofday()
local light_multiplier = get_light_modifier(time)
local new_light = math.max(light_factor * light_multiplier, MINIMUM_LIGHT_LEVEL)
sky_data.day_night_ratio = new_light
end
end
end
-- This can't be function dimension_handlers.end() due to lua syntax
dimension_handlers["end"] = function(player, sky_data)
local biomesky = "#000000"
local biomefog = "#A080A0"
if mg_name ~= "v6" and mg_name ~= "singlenode" then
local biome_index = minetest.get_biome_data(player:get_pos()).biome
local biome_name = minetest.get_biome_name(biome_index)
local biome = minetest.registered_biomes[biome_name]
if biome then
biomesky = biome._mcl_skycolor
biomefog = biome._mcl_fogcolor -- The End biomes seemingly don't use the fog colour, despite having this value according to the wiki. The sky colour is seemingly used for both sky and fog?
end
end
local t = "mcl_playerplus_end_sky.png"
sky_data.sky = { type = "skybox",
base_color = biomesky,
textures = {t,t,t,t,t,t},
clouds = false,
}
sky_data.sun = {visible = false , sunrise_visible = false}
sky_data.moon = {visible = false}
sky_data.stars = {visible = false}
sky_data.day_night_ratio = 0.5
end
function dimension_handlers.nether(player, sky_data)
local biomesky = "#6EB1FF"
local biomefog = "#330808"
if mg_name ~= "v6" and mg_name ~= "singlenode" then
local biome_index = minetest.get_biome_data(player:get_pos()).biome
local biome_name = minetest.get_biome_name(biome_index)
local biome = minetest.registered_biomes[biome_name]
if biome then
-- The Nether biomes seemingly don't use the sky colour, despite having this value according to the wiki.
-- The fog colour is used for both sky and fog.
biomesky = biome._mcl_skycolor
biomefog = biome._mcl_fogcolor
end
end
sky_data.sky = {
type = "regular",
sky_color = {
day_sky = biomefog,
day_horizon = biomefog,
dawn_sky = biomefog,
dawn_horizon = biomefog,
night_sky = biomefog,
night_horizon = biomefog,
indoors = biomefog,
fog_sun_tint = biomefog,
fog_moon_tint = biomefog,
fog_tint_type = "custom"
},
clouds = false,
}
sky_data.sun = {visible = false , sunrise_visible = false}
sky_data.moon = {visible = false}
sky_data.stars = {visible = false}
end
function dimension_handlers.void(player, sky_data)
sky_data.sky = {
type = "plain",
base_color = "#000000",
clouds = false,
}
sky_data.sun = {visible = false, sunrise_visible = false}
sky_data.moon = {visible = false}
sky_data.stars = {visible = false}
end
local function dimension(player, sky_data)
local pos = player:get_pos()
local dim = mcl_worlds.pos_to_dimension(pos)
local handler = dimension_handlers[dim]
if handler then return handler(player, sky_data) end
end
table.insert(mcl_weather.skycolor.filters, dimension)

View File

@ -1,56 +0,0 @@
local DIM_ALLOW_NIGHT_VISION = {
overworld = true,
void = true,
}
local NIGHT_VISION_RATIO = mcl_weather.skycolor.NIGHT_VISION_RATIO
local effects_handlers = {}
local has_mcl_potions = mcl_util.to_bool(minetest.get_modpath("mcl_potions"))
function effects_handlers.darkness(player, meta, effect, sky_data)
-- No darkness effect if is a visited shepherd
if meta:get_int("mcl_shepherd:special") == 1 then return end
-- High stars
sky_data.stars = {visible = false}
-- Minor visibility if the player has the night vision effect
if mcl_potions.has_effect(player, "night_vision") then
sky_data.day_night_ratio = 0.1
else
sky_data.day_night_ratio = 0
end
end
function effects_handlers.night_vision(player, meta, effect, sky_data)
-- Apply night vision only for dark sky
if not (minetest.get_timeofday() > 0.8 or minetest.get_timeofday() < 0.2 or mcl_weather.state ~= "none") then return end
-- Only some dimensions allow night vision
local pos = player:get_pos()
local dim = mcl_worlds.pos_to_dimension(pos)
if not DIM_ALLOW_NIGHT_VISION[dim] then return end
-- Apply night vision
sky_data.day_night_ratio = math.max(sky_data.day_night_ratio or 0, NIGHT_VISION_RATIO)
end
local function effects(player, sky_data)
if not has_mcl_potions then return end
local meta = player:get_meta()
for name,effect in pairs(mcl_potions.registered_effects) do
local effect_data = mcl_potions.get_effect(player, name)
if effect_data then
local hook = effect.mcl_weather_skycolor or effects_handlers[name]
if hook then hook(player, meta, effect_data, sky_data) end
end
end
-- Handle night vision for shepherd
if meta:get_int("mcl_shepherd:special") == 1 then
return effects_handlers.night_vision(player, meta, {}, sky_data)
end
end
table.insert(mcl_weather.skycolor.filters, effects)

View File

@ -1,40 +0,0 @@
local DEFAULT_WATER_COLOR = "#3F76E4"
local mg_name = minetest.get_mapgen_setting("mg_name")
local function water_sky(player, sky_data)
local water_color = DEFAULT_WATER_COLOR
local checkname = mcl_playerinfo[player:get_player_name()].node_head
if minetest.get_item_group(checkname, "water") == 0 then return end
local pos = player:get_pos()
local biome = nil
if mg_name ~= "v6" and mg_name ~= "singlenode" then
local biome_index = minetest.get_biome_data(pos).biome
local biome_name = minetest.get_biome_name(biome_index)
biome = minetest.registered_biomes[biome_name]
end
if biome then water_color = biome._mcl_waterfogcolor end
if not biome then water_color = DEFAULT_WATER_COLOR end
if checkname == "mclx_core:river_water_source" or checkname == "mclx_core:river_water_flowing" then water_color = "#0084FF" end
sky_data.sky = { type = "regular",
sky_color = {
day_sky = water_color,
day_horizon = water_color,
dawn_sky = water_color,
dawn_horizon = water_color,
night_sky = water_color,
night_horizon = water_color,
indoors = water_color,
fog_sun_tint = water_color,
fog_moon_tint = water_color,
fog_tint_type = "custom"
},
clouds = true,
}
end
table.insert(mcl_weather.skycolor.filters, water_sky)

View File

@ -75,15 +75,13 @@ function mcl_weather.has_snow(pos)
end
function mcl_weather.snow.set_sky_box()
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-snow-sky" then
mcl_weather.skycolor.add_layer(
"weather-pack-snow-sky",
{{r=0, g=0, b=0},
{r=85, g=86, b=86},
{r=135, g=135, b=135},
{r=85, g=86, b=86},
{r=0, g=0, b=0}})
end
mcl_weather.skycolor.add_layer(
"weather-pack-snow-sky",
{{r=0, g=0, b=0},
{r=85, g=86, b=86},
{r=135, g=135, b=135},
{r=85, g=86, b=86},
{r=0, g=0, b=0}})
mcl_weather.skycolor.active = true
for _, player in pairs(get_connected_players()) do
player:set_clouds({color="#ADADADE8"})

View File

@ -10,10 +10,6 @@ mcl_weather.thunder = {
init_done = false,
}
lightning.register_on_strike(function(pos, pos2, objects)
if not mcl_weather.has_rain(pos) then return nil, true end
end)
minetest.register_globalstep(function(dtime)
if mcl_weather.get_weather() ~= "thunder" then
return false
@ -23,15 +19,13 @@ minetest.register_globalstep(function(dtime)
mcl_weather.rain.make_weather()
if mcl_weather.thunder.init_done == false then
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-thunder-sky" then
mcl_weather.skycolor.add_layer("weather-pack-thunder-sky", {
{r=0, g=0, b=0},
{r=40, g=40, b=40},
{r=85, g=86, b=86},
{r=40, g=40, b=40},
{r=0, g=0, b=0},
})
end
mcl_weather.skycolor.add_layer("weather-pack-thunder-sky", {
{r=0, g=0, b=0},
{r=40, g=40, b=40},
{r=85, g=86, b=86},
{r=40, g=40, b=40},
{r=0, g=0, b=0},
})
mcl_weather.skycolor.active = true
for _, player in pairs(get_connected_players()) do
player:set_clouds({color="#3D3D3FE8"})

View File

@ -678,7 +678,6 @@ local function make_formspec(name)
image_button[2.4,0.12;0.8,0.8;craftguide_search_icon.png;search;]
image_button[3.05,0.12;0.8,0.8;craftguide_clear_icon.png;clear;]
field_close_on_enter[filter;false]
field_enter_after_edit[filter;true]
]]
fs[#fs + 1] = fmt([[ tooltip[search;%s]

View File

@ -37,8 +37,6 @@ end
-- Digging capabilities of tool
tt.register_snippet(function(itemstring, toolcaps, itemstack)
local def = minetest.registered_items[itemstring]
if not def then return end
if not toolcaps then
return
end
@ -167,8 +165,6 @@ end)]]
-- Food
tt.register_snippet(function(itemstring)
local def = minetest.registered_items[itemstring]
if not def then return end
local desc
if def._tt_food then
desc = S("Food item")
@ -183,8 +179,6 @@ end)
-- Node info
tt.register_snippet(function(itemstring)
local def = minetest.registered_items[itemstring]
if not def then return end
local desc = ""
-- Health-related node facts

View File

@ -64,8 +64,6 @@ end)
tt.register_snippet(function(itemstring)
local def = minetest.registered_items[itemstring]
if not def then return end
local s = ""
if def.groups.eatable and def.groups.eatable > 0 then
s = s .. S("Hunger points: +@1", def.groups.eatable)
@ -91,8 +89,6 @@ end)
tt.register_snippet(function(itemstring)
local def = minetest.registered_items[itemstring]
if not def then return end
if def.groups.place_flowerlike == 1 then
return S("Grows on grass blocks or dirt")
elseif def.groups.place_flowerlike == 2 then
@ -102,8 +98,6 @@ end)
tt.register_snippet(function(itemstring)
local def = minetest.registered_items[itemstring]
if not def then return end
if def.groups.flammable then
return S("Flammable")
end
@ -133,8 +127,6 @@ end)
tt.register_snippet(function(itemstring, _, itemstack)
if not itemstack then return end
local def = itemstack:get_definition()
if not def then return end
if def.groups._mcl_potion ~= 1 then return end
local s = ""

View File

@ -20,12 +20,23 @@ dofile(minetest.get_modpath(minetest.get_current_modname()).."/snippets.lua")
-- Apply item description updates
local function apply_snippets(desc, itemstring, toolcaps, itemstack)
local first = true
-- Apply snippets
for s=1, #tt.registered_snippets do
local str, snippet_color = tt.registered_snippets[s](itemstring, toolcaps, itemstack)
if snippet_color == nil then
snippet_color = tt.COLOR_DEFAULT
end
if str then
if snippet_color == nil then snippet_color = tt.COLOR_DEFAULT end
desc = desc .. "\n" .. (snippet_color and minetest.colorize(snippet_color, str) or str)
if first then
first = false
end
desc = desc .. "\n"
if snippet_color then
desc = desc .. minetest.colorize(snippet_color, str)
else
desc = desc .. str
end
end
end
return desc
@ -56,7 +67,10 @@ function tt.reload_itemstack_description(itemstack)
if def and def._mcl_generate_description then
def._mcl_generate_description(itemstack)
elseif should_change(itemstring, def) then
local toolcaps = def.tool_capabilities and itemstack:get_tool_capabilities()
local toolcaps
if def.tool_capabilities then
toolcaps = itemstack:get_tool_capabilities()
end
local orig_desc = def._tt_original_description or def.description
if meta:get_string("name") ~= "" then
orig_desc = minetest.colorize(tt.NAME_COLOR, meta:get_string("name"))
@ -64,13 +78,17 @@ function tt.reload_itemstack_description(itemstack)
local potency = meta:get_int("mcl_potions:potion_potent")
local plus = meta:get_int("mcl_potions:potion_plus")
if potency > 0 then
orig_desc = orig_desc .. " " .. mcl_util.to_roman(potency+1)
local sym_potency = mcl_util.to_roman(potency+1)
orig_desc = orig_desc.. " ".. sym_potency
end
if plus > 0 then
orig_desc = orig_desc .. " "
for i = 1, plus do
orig_desc = orig_desc .. "+"
local sym_plus = " "
local i = plus
while i>0 do
i = i - 1
sym_plus = sym_plus.. "+"
end
orig_desc = orig_desc.. sym_plus
end
end
local desc = apply_snippets(orig_desc, itemstring, toolcaps or def.tool_capabilities, itemstack)

View File

@ -3,7 +3,9 @@
-- Custom text (_tt_help)
tt.register_snippet(function(itemstring)
local def = minetest.registered_items[itemstring]
return def and def._tt_help or nil
if def._tt_help then
return def._tt_help
end
end)

View File

@ -330,16 +330,9 @@ function hb.change_hudbar(player, identifier, new_value, new_max_value, new_icon
local name = player:get_player_name()
local hudtable = hb.get_hudtable(identifier)
-- hb.change_hudbar may be called with a non-existing hudbar like hunger.
if hudtable == nil then
return false
end
if not hudtable.hudstate[name] then
return false
end
local value_changed, max_changed = false, false
if new_value then

View File

@ -22,8 +22,10 @@ return {
"epCode",
"chmodsayshello",
"MrRar",
"FossFanatic ",
"SmokeyDope",
"Faerraven / Michieal",
"Codiac",
"rudzik8",
"teknomunk",
}},
@ -36,8 +38,6 @@ return {
"NO11",
"SumianVoice",
"PrairieWind",
"FossFanatic",
"Codiac",
}},
{S("Contributors"), 0x52FF00, {
"RandomLegoBrick",
@ -142,7 +142,6 @@ return {
"SOS-Games",
"Bram",
"qoheniac",
"WillConker",
}},
{S("Music"), 0xA60014, {
"Jordach for the jukebox music compilation from Big Freaking Dig",

View File

@ -1,4 +1,4 @@
name = mcl_death_messages
author = 4Evergreen4
description = Shows messages in chat when a player dies.
depends = mcl_colors, mcl_damage
depends = mcl_colors

View File

@ -5,4 +5,3 @@ 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
Bottle o' Enchanting=Erfahrungsfläschchen

View File

@ -1,12 +1,7 @@
-- Constants
local size_min = 20 / 100 -- minimum size, prescaled
local size_max = 59 / 100 -- maximum size, prescaled
local delta_size = (size_max - size_min) / 10 -- Size change for each XP size level
local max_orb_age = 300 -- seconds
local gravity = vector.new(0, -((tonumber(minetest.settings:get("movement_gravity"))) or 9.81), 0)
local size_min, size_max = 20, 59
local delta_size = size_max - size_min
local size_to_xp = {
-- min and max XP amount for a given size
{-32768, 2}, -- 1
{ 3, 6}, -- 2
{ 7, 16}, -- 3
@ -21,20 +16,24 @@ local size_to_xp = {
}
local function xp_to_size(xp)
xp = xp or 0
local i, l = 1, #size_to_xp
-- Find the size for the xp amount
for i=1,11 do
local bucket = size_to_xp[i]
if xp >= bucket[1] and xp <= bucket[2] then
return (i - 1) * delta_size + size_min
end
while xp > size_to_xp[i][1] and i < l do
i = i + 1
end
-- Fallback is the minimum size
return size_min
return ((i - 1) / (l - 1) * delta_size + size_min) / 100
end
local max_orb_age = 300 -- seconds
local gravity = vector.new(0, -((tonumber(minetest.settings:get("movement_gravity"))) or 9.81), 0)
local collector, pos, pos2
local direction, distance, player_velocity, goal
local currentvel, acceleration, multiplier, velocity
local node, vel, def
local is_moving, is_slippery, slippery, slip_factor
local size
local function xp_step(self, dtime)
--if item set to be collected then only execute go to player
if self.collected == true then
@ -42,32 +41,33 @@ local function xp_step(self, dtime)
self.collected = false
return
end
local 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()) < 7.25 then
self.object:set_acceleration(vector.new(0,0,0))
self.disable_physics(self)
--get the variables
local pos = self.object:get_pos()
local pos2 = collector:get_pos()
pos = self.object:get_pos()
pos2 = collector:get_pos()
local player_velocity = collector:get_velocity() or collector:get_player_velocity()
player_velocity = collector:get_velocity() or collector:get_player_velocity()
pos2.y = pos2.y + 0.8
local direction = vector.direction(pos,pos2)
local distance = vector.distance(pos2,pos)
local multiplier = distance
direction = vector.direction(pos,pos2)
distance = vector.distance(pos2,pos)
multiplier = distance
if multiplier < 1 then
multiplier = 1
end
local currentvel = self.object:get_velocity()
goal = vector.multiply(direction,multiplier)
currentvel = self.object:get_velocity()
if distance > 1 then
multiplier = 20 - distance
local velocity = vector.multiply(direction, multiplier)
local acceleration = vector.new(velocity.x - currentvel.x, velocity.y - currentvel.y, velocity.z - currentvel.z)
self.object:add_velocity(vector.add(acceleration, player_velocity))
velocity = vector.multiply(direction,multiplier)
goal = velocity
acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
self.object:add_velocity(vector.add(acceleration,player_velocity))
elseif distance < 0.8 then
mcl_experience.add_xp(collector, self._xp)
self.object:remove()
@ -75,26 +75,28 @@ local function xp_step(self, dtime)
return
else
self.collector = nil
self:enable_physics()
self.enable_physics(self)
end
end
-- Age orbs
self.age = self.age + dtime
if self.age > max_orb_age then
self.object:remove()
return
end
local pos = self.object:get_pos()
if not pos then return end
pos = self.object:get_pos()
-- Get the node directly below the XP orb
local node = minetest.get_node_or_nil({
x = pos.x,
y = pos.y - 0.25, -- Orb collision box is +/-0.2, so go a bit below that
z = pos.z
})
if pos then
node = minetest.get_node_or_nil({
x = pos.x,
y = pos.y -0.25,
z = pos.z
})
else
return
end
-- Remove nodes in 'ignore'
if node and node.name == "ignore" then
@ -107,18 +109,18 @@ local function xp_step(self, dtime)
end
-- Slide on slippery nodes
local vel = self.object:get_velocity()
local def = node and minetest.registered_nodes[node.name]
local is_moving = (def and not def.walkable) or
vel = self.object:get_velocity()
def = node and minetest.registered_nodes[node.name]
is_moving = (def and not def.walkable) or
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
local is_slippery = false
is_slippery = false
if def and def.walkable then
local slippery = minetest.get_item_group(node.name, "slippery")
slippery = minetest.get_item_group(node.name, "slippery")
is_slippery = slippery ~= 0
if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
-- Horizontal deceleration
local slip_factor = 4.0 / (slippery + 4)
slip_factor = 4.0 / (slippery + 4)
self.object:set_acceleration({
x = -vel.x * slip_factor,
y = 0,
@ -158,6 +160,7 @@ minetest.register_entity("mcl_experience:orb", {
initial_sprite_basepos = {x = 0, y = 0},
is_visible = true,
pointable = false,
static_save = false,
},
moving_state = true,
slippery_state = false,
@ -188,7 +191,7 @@ minetest.register_entity("mcl_experience:orb", {
-- This was a minetest bug for a while: https://github.com/minetest/minetest/issues/14420
local xp = tonumber(staticdata) or 0
self._xp = xp
local size = xp_to_size(xp)
size = xp_to_size(xp)
self.object:set_properties({
visual_size = {x = size, y = size},
@ -196,9 +199,6 @@ minetest.register_entity("mcl_experience:orb", {
})
self.object:set_sprite({x=1,y=math.random(1,14)}, 14, 0.05, false)
end,
get_staticdata = function(self)
return tostring(self._xp or 0)
end,
enable_physics = function(self)
if not self.physical_state then

View File

@ -1,10 +1,8 @@
## mcl_info
An API to make custom entries in the VL debug hud.
An api to make custom entries in the mcl2 debug hud.
### mcl_info.register_debug_field(name,defintion)
Debug field defintion example:
```
{
level = 3,
--show with debug level 3 and upwards
@ -15,7 +13,6 @@ Debug field defintion example:
-- It should output a string and determines
-- the content of the debug field.
}
```
### mcl_info.registered_debug_fields
Table the debug definitions are stored in. Do not modify this directly. If you need to overwrite a field just set it again with mcl_info.register_debug_field().

View File

@ -32,5 +32,4 @@ mcl_inventory.register_survival_inventory_tab({
-- Returns true by default
access = function(player)
end,
})
```

View File

@ -418,18 +418,6 @@ minetest.register_on_joinplayer(function(player)
end
end)
---@param player mt.PlayerObjectRef
local function is_touch_enabled(playername)
-- Minetest < 5.7.0 support
if not minetest.get_player_window_information then
return false
end
local window = minetest.get_player_window_information(playername)
-- Always return a boolean (not nil) to avoid false-negatives when
-- comparing to a boolean later.
return window and window.touch_controls or false
end
---@param player mt.PlayerObjectRef
function mcl_inventory.set_creative_formspec(player)
local playername = player:get_player_name()
@ -578,10 +566,8 @@ function mcl_inventory.set_creative_formspec(player)
bg_img = "crafting_creative_inactive" .. button_bg_postfix[this_tab] .. ".png"
end
return table.concat({
"style[" .. this_tab .. ";border=false;bgimg=;bgimg_pressed=]",
"style[" .. this_tab .. "_outer;border=false;bgimg=" .. bg_img ..
";bgimg_pressed=" .. bg_img .. "]",
"button[" .. offset[this_tab] .. ";1.5,1.44;" .. this_tab .. "_outer;]",
"style[" .. this_tab .. ";border=false;bgimg=;bgimg_pressed=;noclip=true]",
"image[" .. offset[this_tab] .. ";1.5,1.44;" .. bg_img .. "]",
"item_image_button[" .. boffset[this_tab] .. ";1,1;" .. tab_icon[this_tab] .. ";" .. this_tab .. ";]",
})
end
@ -591,21 +577,11 @@ function mcl_inventory.set_creative_formspec(player)
caption = "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, filtername[name])) .. "]"
end
local touch_enabled = is_touch_enabled(playername)
players[playername].last_touch_enabled = touch_enabled
local formspec = table.concat({
"formspec_version[6]",
-- Original formspec height was 8.75, increased to include tab buttons.
-- This avoids tab buttons going off-screen with high scaling values.
"size[13,11.43]",
-- Use as much space as possible on mobile - the tab buttons are a lot
-- of padding already.
touch_enabled and "padding[-0.015,-0.015]" or "",
"size[13,8.75]",
"no_prepend[]", mcl_vars.gui_nonbg, mcl_vars.gui_bg_color,
"background9[0,1.34;13,8.75;mcl_base_textures_background9.png;;7]",
"container[0,1.34]",
"style_type[image;noclip=true]",
-- Hotbar
mcl_formspec.get_itemslot_bg_v4(0.375, 7.375, 9, 1),
@ -662,7 +638,6 @@ function mcl_inventory.set_creative_formspec(player)
"set_focus[search;true]",
})
end
formspec = formspec .. "container_end[]"
if pagenum then formspec = formspec .. "p" .. tostring(pagenum) end
player:set_inventory_formspec(formspec)
end
@ -680,54 +655,54 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name()
if fields.blocks or fields.blocks_outer then
if fields.blocks then
if players[name].page == "blocks" then return end
set_inv_page("blocks", player)
page = "blocks"
elseif fields.deco or fields.deco_outer then
elseif fields.deco then
if players[name].page == "deco" then return end
set_inv_page("deco", player)
page = "deco"
elseif fields.redstone or fields.redstone_outer then
elseif fields.redstone then
if players[name].page == "redstone" then return end
set_inv_page("redstone", player)
page = "redstone"
elseif fields.rail or fields.rail_outer then
elseif fields.rail then
if players[name].page == "rail" then return end
set_inv_page("rail", player)
page = "rail"
elseif fields.misc or fields.misc_outer then
elseif fields.misc then
if players[name].page == "misc" then return end
set_inv_page("misc", player)
page = "misc"
elseif fields.nix or fields.nix_outer then
elseif fields.nix then
set_inv_page("all", player)
page = "nix"
elseif fields.food or fields.food_outer then
elseif fields.food then
if players[name].page == "food" then return end
set_inv_page("food", player)
page = "food"
elseif fields.tools or fields.tools_outer then
elseif fields.tools then
if players[name].page == "tools" then return end
set_inv_page("tools", player)
page = "tools"
elseif fields.combat or fields.combat_outer then
elseif fields.combat then
if players[name].page == "combat" then return end
set_inv_page("combat", player)
page = "combat"
elseif fields.mobs or fields.mobs_outer then
elseif fields.mobs then
if players[name].page == "mobs" then return end
set_inv_page("mobs", player)
page = "mobs"
elseif fields.brew or fields.brew_outer then
elseif fields.brew then
if players[name].page == "brew" then return end
set_inv_page("brew", player)
page = "brew"
elseif fields.matr or fields.matr_outer then
elseif fields.matr then
if players[name].page == "matr" then return end
set_inv_page("matr", player)
page = "matr"
elseif fields.inv or fields.inv_outer then
elseif fields.inv then
if players[name].page == "inv" then return end
page = "inv"
elseif fields.search == "" and not fields.creative_next and not fields.creative_prev then
@ -843,19 +818,3 @@ minetest.register_on_player_inventory_action(function(player, action, inventory,
player:get_inventory():set_stack("main", inventory_info.index, stack)
end
end)
-- This is necessary because get_player_window_information may return nil in
-- on_joinplayer.
-- (Also, Minetest plans to add support for toggling touchscreen mode in-game.)
minetest.register_globalstep(function(dtime)
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
if minetest.is_creative_enabled(name) then
local touch_enabled = is_touch_enabled(name)
if touch_enabled ~= players[name].last_touch_enabled then
mcl_inventory.set_creative_formspec(player)
end
end
end
end)

View File

@ -8,7 +8,7 @@ Show a hud message of `type` to player `player` with `data` as params.
The element will stay for the per-player param `stay` or `data.stay` (in gametick which is 1/20 second).
Here is a usage example:
Here is a usage exemple:
```lua
--show a title in the HUD with minecraft color "gold"
@ -35,7 +35,7 @@ Basicaly run `mcl_title.remove(player, type)` for every type.
## mcl_title.params_set(player, params)
Allow mods to set `stay` and upcoming `fadeIn`/`fadeOut` params.
Allow mods to set `stay` and upcomming `fadeIn`/`fadeOut` params.
```lua
mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field will stay during 30s (600/20)
@ -43,8 +43,8 @@ mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field
## mcl_title.params_get(player)
Get `stay` and upcoming `fadeIn` and `fadeOut` params of a player as a table.
Get `stay` and upcomming `fadeIn` and `fadeOut` params of a player as a table.
```lua
mcl_title.params_get(player)
```
```

View File

@ -1,4 +1,4 @@
name = mcl_title
description = Add an API to add in HUD title
depends = mcl_colors, mcl_util
description = Add an API to add in HUD title
depends = mcl_colors
author = AFCMS

View File

@ -113,8 +113,8 @@ mesecon.register_node("mcl_observers:observer", {
sounds = mcl_sounds.node_sound_stone_defaults(),
paramtype2 = "facedir",
on_rotate = false,
_mcl_blast_resistance = 3,
_mcl_hardness = 3,
_mcl_blast_resistance = 3.5,
_mcl_hardness = 3.5,
}, {
description = S("Observer"),
_tt_help = S("Emits redstone pulse when block in front changes"),
@ -172,8 +172,8 @@ mesecon.register_node("mcl_observers:observer_down", {
sounds = mcl_sounds.node_sound_stone_defaults(),
groups = {pickaxey=1, material_stone=1, not_opaque=1, not_in_creative_inventory=1 },
on_rotate = false,
_mcl_blast_resistance = 3,
_mcl_hardness = 3,
_mcl_blast_resistance = 3.5,
_mcl_hardness = 3.5,
drop = "mcl_observers:observer_off",
}, {
tiles = {
@ -224,8 +224,8 @@ mesecon.register_node("mcl_observers:observer_up", {
sounds = mcl_sounds.node_sound_stone_defaults(),
groups = {pickaxey=1, material_stone=1, not_opaque=1, not_in_creative_inventory=1 },
on_rotate = false,
_mcl_blast_resistance = 3,
_mcl_hardness = 3,
_mcl_blast_resistance = 3.5,
_mcl_hardness = 3.5,
drop = "mcl_observers:observer_off",
}, {
tiles = {

View File

@ -1,4 +0,0 @@
# textdomain: mcl_target
Target=Zielscheibe
A target is a block that provides a temporary redstone charge when hit by a projectile.=
Throw a projectile on the target to activate it.=

View File

@ -2,19 +2,13 @@
Use the button to push it.=Benutzen Sie den Knopf, um ihn zu drücken.
Stone Button=Steinknopf
A stone button is a redstone component made out of stone which can be pushed to provide redstone power. When pushed, it powers adjacent redstone components for 1 second.=Ein Steinknopf ist eine Redstonekomponente aus Stein. Er kann gedrückt werden, um ein Redstonesignal zu senden. Im gedrückten Zustand versorgt er benachbarte Redstonekomponenten für 1 Sekunde mit Redstoneenergie.
Polished Blackstone Button=Polierter Schwarzsteinknopf
A polished blackstone button is a redstone component made out of polished blackstone which can be pushed to provide redstone power. When pushed, it powers adjacent redstone components for 1 second.=
Oak Button=Eichenknopf
Acacia Button=Akazienknopf
Birch Button=Birkenknopf
Dark Oak Button=Schwarzeichenknopf
Spruce Button=Fichtenknopf
Jungle Button=Dschungelknopf
Mangrove Button=
Crimson Button=Karmesinholzknopf
Warped Button=Wirrholzknopf
A wooden button is a redstone component made out of wood which can be pushed to provide redstone power. When pushed, it powers adjacent redstone components for 1.5 seconds. Wooden buttons may also be pushed by arrows.=Ein Holzknopf ist eine Redstonekomponente aus Holz. Er kann gedrückt werden, um ein Redstonesignal zu senden. Im gedrückten Zustand versorgt er benachbarte Redstonekomponenten für 1,5 Sekunden mit Redstoneenergie. Holzknöpfe können auch von Pfeilen gedrückt werden.
Provides redstone power when pushed=Gibt Redstoneenergie, wenn gedrückt
Push duration: @1s=Druckdauer: @1s
Pushable by arrow=Drückbar von Pfeilen
A button is a redstone component which can be pushed to provide redstone power. When pushed, it powers adjacent redstone components for @1 seconds.=

View File

@ -1,3 +1,3 @@
name = mesecons_button
depends = mesecons, mesecons_mvps
depends = mesecons
optional_depends = doc

View File

@ -218,7 +218,7 @@ minetest.register_node("mesecons_pistons:piston_normal_off", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_up_normal_off"})
@ -255,7 +255,7 @@ minetest.register_node("mesecons_pistons:piston_normal_on", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = false,
})
@ -326,7 +326,7 @@ minetest.register_node("mesecons_pistons:piston_sticky_off", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_up_sticky_off"})
@ -363,7 +363,7 @@ minetest.register_node("mesecons_pistons:piston_sticky_on", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = false,
})
@ -449,7 +449,7 @@ minetest.register_node("mesecons_pistons:piston_up_normal_off", {
footstep = mcl_sounds.node_sound_wood_defaults().footstep
}),
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_down_normal_off"})
@ -487,7 +487,7 @@ minetest.register_node("mesecons_pistons:piston_up_normal_on", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = false,
})
@ -556,7 +556,7 @@ minetest.register_node("mesecons_pistons:piston_up_sticky_off", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_down_sticky_off"})
@ -594,7 +594,7 @@ minetest.register_node("mesecons_pistons:piston_up_sticky_on", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = false,
})
@ -680,7 +680,7 @@ minetest.register_node("mesecons_pistons:piston_down_normal_off", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_normal_off"})
@ -718,7 +718,7 @@ minetest.register_node("mesecons_pistons:piston_down_normal_on", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = false,
})
@ -782,7 +782,7 @@ minetest.register_node("mesecons_pistons:piston_down_sticky_off", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_sticky_off"})
@ -820,7 +820,7 @@ minetest.register_node("mesecons_pistons:piston_down_sticky_on", {
},
},
_mcl_blast_resistance = 0.5,
_mcl_hardness = 1.5,
_mcl_hardness = 0.5,
on_rotate = false,
})

View File

@ -6,18 +6,11 @@ Birch Pressure Plate=Birkendruckplatte
Dark Oak Pressure Plate=Schwarzeichendruckplatte
Spruce Pressure Plate=Fichtendruckplatte
Jungle Pressure Plate=Dschungeldruckplatte
Mangrove Pressure Plate=
Crimson Pressure Plate=Karmesinholzdruckplatte
Warped Pressure Plate=Wirrholzdruckplatte
A wooden pressure plate is a redstone component which supplies its surrounding blocks with redstone power while any movable object (including dropped items, players and mobs) rests on top of it.=Eine Holzdruckplatte ist eine Redstonekomponente, die ihre benachbarten Blöcke mit Redstoneenergie versorgt, solange sich ein beliebiges bewegliches Objekt (wie Gegenstände, Spieler und Mobs) auf ihm befindet.
Polished Blackstone Pressure Plate=Polierte Schwarzsteindruckplatte
A stone pressure plate is a redstone component which supplies its surrounding blocks with redstone power while a player or mob stands on top of it. It is not triggered by anything else.=Eine Steindruckplatte ist eine Redstonekomponente, die ihre benachbarten Blöcke mit Redstoneenergie versorgt, solange sich ein Spieler oder Mob auf ihm befindet. Sie wird von nichts anderem ausgelöst.
Stone Pressure Plate=Steindruckplatte
A stone pressure plate is a redstone component which supplies its surrounding blocks with redstone power while a player or mob stands on top of it. It is not triggered by anything else.=Eine Steindruckplatte ist eine Redstonekomponente, die ihre benachbarten Blöcke mit Redstoneenergie versorgt, solange sich ein Spieler oder Mob auf ihm befindet. Sie wird von nichts anderem ausgelöst.
Provides redstone power when pushed=Gibt Redstoneenergie aus, wenn gedrückt
Pushable by players, mobs and objects=Drückbar von Spielern, Mobs und Objekten
Pushable by players, mobs and objects=
Pushable by players and mobs=Drückbar von Spielern und Mobs
Pushable by players=Drückbar von Spielern
Pushable by mobs=Drückbar von Mobs
Pushable by players=
Pushable by mobs=

View File

@ -28,7 +28,6 @@ local function get_anvil_formspec(set_name)
"field[4.125,0.75;7.25,1;name;;" .. F(set_name) .. "]",
"field_close_on_enter[name;false]",
"field_enter_after_edit[name;true]",
"set_focus[name;true]",
mcl_formspec.get_itemslot_bg_v4(1.625, 2.6, 1, 1),

View File

@ -119,7 +119,7 @@ mcl_armor.register_set({
end,
},
--this is used to generate automaticaly armor crafts based on each element type following the regular minecraft pattern
--this is used to generate automaticaly armor crafts based on each element type folowing the regular minecraft pattern
--if set to nil no craft will be added
craft_material = "mcl_mobitems:leather",

View File

@ -58,25 +58,10 @@ mcl_armor = {
},
player_view_range_factors = {},
trims = {
core_textures = {},
blacklisted = {["mcl_armor:elytra"]=true, ["mcl_armor:elytra_enchanted"]=true},
core_textures = {},
blacklisted = {["mcl_armor:elytra"]=true, ["mcl_armor:elytra_enchanted"]=true},
overlays = {"sentry","dune","coast","wild","tide","ward","vex","rib","snout","eye","spire","silence","wayfinder"},
translations = {
sentry = S("sentry"),
dune = S("dune"),
coast = S("coast"),
wild = S("wild"),
tide = S("tide"),
ward = S("ward"),
vex = S("vex"),
rib = S("rib"),
snout = S("snout"),
eye = S("eye"),
spire = S("spire"),
silence = S("silence"),
wayfinder = S("wayfinder"),
},
colors = {["amethyst"]="#8246a5",["gold"]="#ce9627",["emerald"]="#1b9958",["copper"]="#c36447",["diamond"]="#5faed8",["iron"]="#938e88",["lapis"]="#1c306b",["netherite"]="#302a26",["quartz"]="#c9bcb9",["redstone"]="#af2c23"},
colors = {["amethyst"]="#8246a5",["gold"]="#ce9627",["emerald"]="#1b9958",["copper"]="#c36447",["diamond"]="#5faed8",["iron"]="#938e88",["lapis"]="#1c306b",["netherite"]="#302a26",["quartz"]="#c9bcb9",["redstone"]="#af2c23"},
},
}

View File

@ -1,21 +1,6 @@
# textdomain: mcl_armor
Blast Protection=Explosionsschutz
Projectile Protection=Projektilschutz
Thorns=Dornen
This is a piece of equippable armor which reduces the amount of damage you receive.=Dies ist ein Teil einer tragbaren Rüstung, die die Menge an Schaden, den Sie erleiden, reduziert.
To equip it, put it on the corresponding armor slot in your inventory menu.=Um es zu tragen, legen Sie es in den passenden Rüstungsplatz in Ihrem Inventarmenü.
sentry=
dune=
coast=Küste
wild=
ward=Bezirk
vex=
rib=
snout=Schnauze
eye=Auge
spire=
silence=
wayfinder=Pfadfinder
Leather Cap=Lederkappe
Iron Helmet=Eisenhelm
Golden Helmet=Goldhelm
@ -36,4 +21,6 @@ Iron Boots=Eisenstiefel
Golden Boots=Goldstiefel
Diamond Boots=Diamantstiefel
Chain Boots=Kettenstiefel
Smithing Template '@1'=Schmiedevorlage '@1'
Smithing Template '@1'=Schmiedevorlage '@1'

View File

@ -1,18 +1,6 @@
# textdomain: mcl_armor
This is a piece of equippable armor which reduces the amount of damage you receive.=
To equip it, put it on the corresponding armor slot in your inventory menu.=
sentry=
dune=
coast=
wild=
ward=
vex=
rib=
snout=
eye=
spire=
silence=
wayfinder=
Leather Cap=
Iron Helmet=
Golden Helmet=

View File

@ -2,63 +2,63 @@ local mod_registername = minetest.get_current_modname() .. ":"
local S = minetest.get_translator(minetest.get_current_modname())
for _, template_name in pairs(mcl_armor.trims.overlays) do
minetest.register_craftitem(mod_registername .. template_name, {
description = S("Smithing Template '@1'", mcl_armor.trims.translations[template_name]),
inventory_image = template_name .. "_armor_trim_smithing_template.png",
})
minetest.register_craft({
output = mod_registername .. template_name .. " 2",
recipe = {
{"mcl_core:diamond",mod_registername .. template_name,"mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:cobble","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
})
minetest.register_craftitem(mod_registername .. template_name, {
description = S("Smithing Template '@1'", template_name),
inventory_image = template_name .. "_armor_trim_smithing_template.png",
})
minetest.register_craft({
output = mod_registername .. template_name .. " 2",
recipe = {
{"mcl_core:diamond",mod_registername .. template_name,"mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:cobble","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
})
end
--temp craft recipies, missing structures
minetest.register_craft({
output = mod_registername .. "eye",
recipe = {
{"mcl_core:diamond","mcl_end:ender_eye","mcl_core:diamond"},
{"mcl_core:diamond","mcl_end:ender_eye","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
output = mod_registername .. "eye",
recipe = {
{"mcl_core:diamond","mcl_end:ender_eye","mcl_core:diamond"},
{"mcl_core:diamond","mcl_end:ender_eye","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
})
minetest.register_craft({
output = mod_registername .. "ward",
recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:apple_gold_enchanted","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
output = mod_registername .. "ward",
recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:apple_gold_enchanted","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
})
minetest.register_craft({
output = mod_registername .. "snout",
recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:goldblock","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
output = mod_registername .. "snout",
recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:goldblock","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
})
minetest.register_craft({
output = mod_registername .. "silence",
recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond", mod_registername.."ward","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
output = mod_registername .. "silence",
recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond", mod_registername.."ward","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
})
minetest.register_craft({
output = mod_registername .. "wayfinder",
recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond", "mcl_maps:empty_map","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
})
output = mod_registername .. "wayfinder",
recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond", "mcl_maps:empty_map","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
}
})

View File

@ -5,6 +5,9 @@
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
-- CONSTS
local DOUBLE_DROP_CHANCE = 8
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
-- "BAMBOO" goes here.
local BAMBOO = "mcl_bamboo:bamboo"
local BAMBOO_ENDCAP_NAME = "mcl_bamboo:bamboo_endcap"
local BAMBOO_PLANK = BAMBOO .. "_plank"
@ -13,7 +16,7 @@ local BAMBOO_PLANK = BAMBOO .. "_plank"
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local node_sound = mcl_sounds.node_sound_wood_defaults()
local pr = PseudoRandom((os.time() + 15766) * 12)
local pr = PseudoRandom((os.time() + 15766) * 12) -- switched from math.random() to PseudoRandom because the random wasn't very random.
local on_rotate
if minetest.get_modpath("screwdriver") then
@ -28,16 +31,38 @@ local bamboo_def = {
paramtype = "light",
groups = {handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3},
sounds = node_sound,
drop = BAMBOO,
drop = {
max_items = 1,
-- From the API:
-- max_items: Maximum number of item lists to drop.
-- The entries in 'items' are processed in order. For each:
-- Item filtering is applied, chance of drop is applied, if both are
-- successful the entire item list is dropped.
-- Entry processing continues until the number of dropped item lists
-- equals 'max_items'.
-- Therefore, entries should progress from low to high drop chance.
items = {
-- Examples:
{
-- 1 in DOUBLE_DROP_CHANCE chance of dropping.
-- Default rarity is '1'.
rarity = DOUBLE_DROP_CHANCE,
items = {BAMBOO .. " 2"},
},
{
-- 1 in 1 chance of dropping. (Note: this means that it will drop 100% of the time.)
-- Default rarity is '1'.
rarity = 1,
items = {BAMBOO},
},
},
},
inventory_image = "mcl_bamboo_bamboo_shoot.png",
wield_image = "mcl_bamboo_bamboo_shoot.png",
_mcl_blast_resistance = 1,
_mcl_hardness = 1,
_on_bone_meal = function(itemstack, placer, pointed_thing)
local pos = pointed_thing.under
return mcl_bamboo.grow_bamboo(pos, true)
end,
_mcl_hardness = 1.5,
node_box = {
type = "fixed",
fixed = {
@ -61,6 +86,7 @@ local bamboo_def = {
on_rotate = on_rotate,
on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing then
return itemstack
end
@ -215,6 +241,9 @@ local bamboo_def = {
if node_above and ((bamboo_node and bamboo_node > 0) or node_above.name == BAMBOO_ENDCAP_NAME) then
minetest.remove_node(new_pos)
minetest.sound_play(node_sound.dug, sound_params, true)
if pr:next(1, DOUBLE_DROP_CHANCE) == 1 then
minetest.add_item(new_pos, istack)
end
minetest.add_item(new_pos, istack)
end
end,
@ -224,12 +253,10 @@ minetest.register_node(BAMBOO, bamboo_def)
local bamboo_top = table.copy(bamboo_def)
bamboo_top.groups = {not_in_creative_inventory = 1, handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3}
bamboo_top.tiles = {"mcl_bamboo_endcap.png"}
-- bamboo_top.drawtype = "plantlike_rooted" --"plantlike"
bamboo_top.drawtype = "plantlike"
bamboo_top.paramtype2 = "meshoptions"
bamboo_top.param2 = 2
bamboo_top.waving = 2
bamboo_top.drawtype = "plantlike_rooted" --"plantlike"
--bamboo_top.paramtype2 = "meshoptions"
--bamboo_top.param2 = 2
-- bamboo_top.waving = 2
bamboo_top.special_tiles = {{name = "mcl_bamboo_endcap.png"}}
bamboo_top.nodebox = nil
bamboo_top.selection_box = nil
@ -250,7 +277,7 @@ local bamboo_block_def = {
sounds = node_sound,
paramtype2 = "facedir",
drops = "mcl_bamboo:bamboo_block",
_mcl_blast_resistance = 2,
_mcl_blast_resistance = 3,
_mcl_hardness = 2,
_mcl_stripped_variant = "mcl_bamboo:bamboo_block_stripped", -- this allows us to use the built in Axe's strip block.
on_place = mcl_util.rotate_axis,

View File

@ -9,6 +9,8 @@ local SIDE_SCAFFOLDING = false
local SIDE_SCAFFOLD_NAME = "mcl_bamboo:scaffolding_horizontal"
-- ---------------------------------------------------------------------------
local SCAFFOLDING_NAME = "mcl_bamboo:scaffolding"
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
-- "BAMBOO" goes here.
local BAMBOO = "mcl_bamboo:bamboo"
local BAMBOO_PLANK = BAMBOO .. "_plank"
@ -206,7 +208,7 @@ if minetest.get_modpath("mcl_fences") then
wood_groups,
minetest.registered_nodes["mcl_core:wood"]._mcl_hardness,
minetest.registered_nodes["mcl_core:wood"]._mcl_blast_resistance,
node_sound)
node_sound) -- note: about missing params.. will use defaults.
mcl_bamboo.mcl_log(dump(fence_id))
mcl_bamboo.mcl_log(dump(gate_id))

View File

@ -44,10 +44,6 @@ mcl_bamboo.bamboo_index = {
"mcl_bamboo:bamboo_2",
"mcl_bamboo:bamboo_3",
}
mcl_bamboo.bamboo_set = {}
for _,key in pairs(mcl_bamboo.bamboo_index) do
mcl_bamboo.bamboo_set[key] = true
end
function mcl_bamboo.is_bamboo(node_name)
local index = table.indexof(mcl_bamboo.bamboo_index, node_name)
@ -78,7 +74,7 @@ function mcl_bamboo.break_orphaned(pos)
local node_name = node_below.name
-- short circuit checks.
if node_name == "ignore" or mcl_bamboo.is_dirt(node_name) or mcl_bamboo.is_bamboo(node_name) or mcl_bamboo.is_bamboo(minetest.get_node(pos).name) == false then
if mcl_bamboo.is_dirt(node_name) or mcl_bamboo.is_bamboo(node_name) or mcl_bamboo.is_bamboo(minetest.get_node(pos).name) == false then
return
end
@ -98,84 +94,172 @@ end
--]]
function mcl_bamboo.grow_bamboo(pos, bonemeal_applied)
local log = mcl_bamboo.mcl_log
local node_above = minetest.get_node(vector.offset(pos, 0, 1, 0))
log("Grow bamboo called; bonemeal: " .. tostring(bonemeal_applied))
mcl_bamboo.mcl_log("Grow bamboo called; bonemeal: " .. tostring(bonemeal_applied))
if not bonemeal_applied then
-- Only allow natural growth at the top of the bamboo
if mcl_bamboo.is_bamboo(node_above.name) ~= false then return false end
-- Don't perform natual growth in low light
if minetest.get_node_light(pos) < 8 then return false end
if not bonemeal_applied and mcl_bamboo.is_bamboo(node_above.name) ~= false then
return false -- short circuit this function if we're trying to grow (std) the bamboo and it's not the top shoot.
end
-- Determine the location of soil
local soil_pos
soil_pos,a,b = mcl_util.trace_nodes(pos, -1, mcl_bamboo.bamboo_set, BAMBOO_MAX_HEIGHT - 1)
-- No soil found, return false so that bonemeal isn't used
if not soil_pos then return false end
log("Grow bamboo; soil found. ")
-- Find the first bamboo shoot and retrieve data about it
local first_shoot = vector.offset(soil_pos, 0, 1, 0)
local first_shoot_meta = minetest.get_meta(first_shoot)
-- Get or initialize bamboo height
local height = (first_shoot_meta and first_shoot_meta:get_int("height", -1)) or -1
if height == -1 then
height = rand(BAM_MAX_HEIGHT_STPCHK + 1, BAM_MAX_HEIGHT_TOP + 1)
first_shoot_meta:set_int("height", height)
end
log("Grow bamboo; height: " .. height)
-- Locate the bamboo tip
local bamboo_tip,actual_height,bamboo_tip_node = mcl_util.trace_nodes(first_shoot, 1, mcl_bamboo.bamboo_set, height - 1)
log("Current height: "..tostring(actual_height))
-- Short circuit growth if the bamboo is already finished growing
if not bamboo_tip or not actual_height or actual_height >= height then
log("Bamboo is already as large as it can grow")
if minetest.get_node_light(pos) < 8 then
return false
end
-- Now that we are actually going to add nodes, initialize some more information
local first_shoot_node_name = minetest.get_node(first_shoot).name
-- variables used in more than one spot.
local first_shoot
local chk_pos
local soil_pos
local node_name = ""
local dist = 0
local node_below
-- -------------------
-- If applying bonemeal, randomly grow two segments instead of one
local grow_amount = 1
mcl_bamboo.mcl_log("Grow bamboo; checking for soil: ")
-- the soil node below the bamboo.
for py = -1, BAMBOO_SOIL_DIST, -1 do
chk_pos = vector.offset(pos, 0, py, 0)
node_name = minetest.get_node(chk_pos).name
if mcl_bamboo.is_dirt(node_name) then
soil_pos = chk_pos
break
end
if mcl_bamboo.is_bamboo(node_name) == false then
break
end
end
-- requires knowing where the soil node is.
if soil_pos == nil then
return false -- returning false means don't use up the bonemeal.
end
mcl_bamboo.mcl_log("Grow bamboo; soil found. ")
local grow_amount = rand(1, GROW_DOUBLE_CHANCE)
grow_amount = rand(1, GROW_DOUBLE_CHANCE)
grow_amount = rand(1, GROW_DOUBLE_CHANCE) -- because yeah, not truly random, or even a good prng.
grow_amount = rand(1, GROW_DOUBLE_CHANCE)
local init_height = rand(BAM_MAX_HEIGHT_STPCHK + 1, BAM_MAX_HEIGHT_TOP + 1)
mcl_bamboo.mcl_log("Grow bamboo; random height: " .. init_height)
node_name = ""
-- update: add randomized max height to first node's meta data.
first_shoot = vector.offset(soil_pos, 0, 1, 0)
local meta = minetest.get_meta(first_shoot)
node_below = minetest.get_node(first_shoot).name
mcl_bamboo.mcl_log("Grow bamboo; checking height meta ")
-- check the meta data for the first node, to see how high to make the stalk.
if not meta then
-- if no metadata, set the metadata!!!
meta:set_int("height", init_height)
end
local height = meta:get_int("height", -1)
mcl_bamboo.mcl_log("Grow bamboo; meta-height: " .. height)
if height <= 10 then
height = init_height
meta:set_int("height", init_height)
end
mcl_bamboo.mcl_log("Grow bamboo; height: " .. height)
-- Bonemeal: Grows the bamboo by 1-2 stems. (per the minecraft wiki.)
if bonemeal_applied then
local rng = PcgRandom(minetest.hash_node_position(pos) + minetest.get_us_time())
if rng:next(1, GROW_DOUBLE_CHANGE) == 1 then
grow_amount = 2
-- handle applying bonemeal.
for py = 1, BAM_MAX_HEIGHT_TOP do
-- find the top node of bamboo.
chk_pos = vector.offset(pos, 0, py, 0)
node_name = minetest.get_node(chk_pos).name
dist = vector.distance(soil_pos, chk_pos)
if mcl_bamboo.is_bamboo(node_name) == false or node_name == BAMBOO_ENDCAP_NAME then
break
end
end
mcl_bamboo.mcl_log("Grow bamboo; dist: " .. dist)
if node_name == BAMBOO_ENDCAP_NAME then
-- prevent overgrowth
return false
end
-- check to see if we have a full stalk of bamboo.
if dist >= height - 1 then
if dist == height - 1 then
-- equals top of the stalk before the cap
if node_name == "air" then
mcl_bamboo.mcl_log("Grow bamboo; Placing endcap")
minetest.set_node(vector.offset(chk_pos, 0, 1, 0), { name = BAMBOO_ENDCAP_NAME })
return true -- returning true means use up the bonemeal.
else
return false
end
else
-- okay, we're higher than the end cap, fail out.
return false -- returning false means don't use up the bonemeal.
end
end
-- and now, the meat of the section... add bamboo to the stalk.
-- at this point, we should be lower than the generated maximum height. ~ about height -2 or lower.
if dist <= height - 2 then
if node_name == "air" then
-- here we can check to see if we can do up to 2 bamboo shoots onto the stalk
mcl_bamboo.mcl_log("Grow bamboo; Placing bamboo.")
minetest.set_node(chk_pos, { name = node_below })
-- handle growing a second node.
if grow_amount == 2 then
chk_pos = vector.offset(chk_pos, 0, 1, 0)
if minetest.get_node(chk_pos).name == "air" then
mcl_bamboo.mcl_log("Grow bamboo; OOOH! It's twofer day!")
minetest.set_node(chk_pos, { name = node_below })
end
end
return true -- exit out with a success. We've added 1-2 nodes, per the wiki.
end
end
end
log("Growing up to "..grow_amount.." segments")
-- Perform bamboo growth
for i = 1,grow_amount do
-- Check for air to grow into
local bamboo_tip_node = minetest.get_node(bamboo_tip)
if not bamboo_tip_node or bamboo_tip_node.name ~= "air" then
-- Something is blocking growth, stop and signal that use bonemeal has been used if at least on segment has grown
return i ~= 1
-- Non-Bonemeal growth.
for py = 1, BAM_MAX_HEIGHT_TOP do
-- Find the topmost node above the stalk, and check it for "air"
chk_pos = vector.offset(pos, 0, py, 0)
node_below = minetest.get_node(pos).name
node_name = minetest.get_node(chk_pos).name
dist = vector.distance(soil_pos, chk_pos)
if node_name ~= "air" and mcl_bamboo.is_bamboo(node_name) == false then
break
end
if actual_height + 1 == height then
-- This is the end cap
minetest.set_node(bamboo_tip, { name = BAMBOO_ENDCAP_NAME })
return true
else
-- This isn't the end cap, add a bamboo segment
minetest.set_node(bamboo_tip, { name = first_shoot_node_name })
actual_height = actual_height + 1
-- stop growing check. ie, handle endcap placement.
if dist >= height - 1 then
local above_node_name = minetest.get_node(vector.offset(chk_pos, 0, 1, 0)).name
if node_name == "air" and above_node_name == "air" then
if height - 1 == dist then
mcl_bamboo.mcl_log("Grow bamboo; Placing endcap")
minetest.set_node(chk_pos, { name = BAMBOO_ENDCAP_NAME })
end
end
break
end
bamboo_tip = vector.offset(bamboo_tip, 0, 1, 0)
-- handle regular node placement.
-- find the air node above the top shoot. place a node. And then, if short enough,
-- check for second node placement.
if node_name == "air" then
mcl_bamboo.mcl_log("Grow bamboo; dist: " .. dist)
mcl_bamboo.mcl_log("Grow bamboo; Placing bamboo.")
minetest.set_node(chk_pos, { name = node_below })
-- handle growing a second node. (1 in 32 chance.)
if grow_amount == 2 and dist <= height - 2 then
chk_pos = vector.offset(chk_pos, 0, 1, 0)
if minetest.get_node(chk_pos).name == "air" then
mcl_bamboo.mcl_log("Grow bamboo; OOOH! It's twofer day!")
minetest.set_node(chk_pos, { name = node_below })
end
end
break
end
end
return true
end
-- Add Groups function, courtesy of Warr1024.

View File

@ -7,6 +7,8 @@
-- LOCALS
local modname = minetest.get_current_modname()
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
-- "BAMBOO" goes here.
local BAMBOO = "mcl_bamboo:bamboo"
mcl_bamboo = {}

View File

@ -1,46 +0,0 @@
# textdomain: mcl_bamboo
### bamboo_base.lua ###
Bamboo=Bambus
Bamboo Mosaic Plank=Bambusmosaikplanken
Bamboo Plank=Bambusplanken
Stripped Bamboo Block=
Bamboo Block=Bambusblock
### bamboo_items.lua ###
A bamboo button is a redstone component made out of bamboo which can be pushed to provide redstone power. When pushed, it powers adjacent redstone components for 1 second.=
A wooden pressure plate is a redstone component which supplies its surrounding blocks with redstone power while any movable object (including dropped items, players and mobs) rests on top of it.=
Bamboo=Bambus
Bamboo Button=Bambusknopf
Bamboo Door=Bambustür
Bamboo Fence=Bambuszaun
Bamboo Fence Gate=Bambuszauntor
Bamboo Mosaic Slab=
Bamboo Mosaic Stair=Bambusmosaikstufe
Bamboo Plank Slab=Bambusplatte
Bamboo Plank Stair=Bambusplankenstufe
Bamboo Pressure Plate=Bambusdruckplatte
Bamboo Sign=Bambusschild
Bamboo Slab=Bambusplatte
Bamboo Stair=Bambusstufe
Bamboo Trapdoor=Bambusfalltür
Double Bamboo Mosaic Slab=
Double Bamboo Plank Slab=
Double Bamboo Slab=
Double Stripped Bamboo Slab=
Scaffolding=Bambusdruckplatte
Scaffolding (horizontal)=
Scaffolding block used to climb up or out across areas.=
Stripped Bamboo Slab=
Stripped Bamboo Stair=
To open or close the trapdoor, rightclick it or send a redstone signal to it.=
Wooden trapdoors are horizontal barriers which can be opened and closed by hand or a redstone signal. They occupy the upper or lower part of a block, depending on how they have been placed. When open, they can be climbed like a ladder.=
Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal.=
To open or close a wooden door, rightclick it or supply its lower half with a redstone signal.=

View File

@ -5,6 +5,8 @@
--- These are all of the fuel recipes and all of the crafting recipes, consolidated into one place.
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
-- "BAMBOO" goes here.
local BAMBOO = "mcl_bamboo:bamboo"
local BAMBOO_PLANK = BAMBOO .. "_plank"
-- Craftings

View File

@ -1,5 +1,5 @@
# textdomain: mcl_barrels
Barrel=Fass
Barrel=
Barrels are containers which provide 27 inventory slots.=
To access its inventory, rightclick it. When broken, the items will drop out.=
27 inventory slots=
27 inventory slots=

View File

@ -210,7 +210,7 @@ function mcl_beds.register_bed(name, def)
stack_max = 1,
groups = {handy=1, bed = 1, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, deco_block = 1, flammable=-1},
_mcl_hardness = 0.2,
_mcl_blast_resistance = 0.2,
_mcl_blast_resistance = 1,
sounds = def.sounds or default_sounds,
selection_box = common_box,
collision_box = common_box,
@ -286,9 +286,10 @@ function mcl_beds.register_bed(name, def)
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
groups = {handy = 1, flammable = -1, bed = 2, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, not_in_creative_inventory = 1},
-- FIXME: Should be bouncy=66, but this would be a higher bounciness than slime blocks!
groups = {handy = 1, flammable = -1, bed = 2, dig_by_piston=1, bouncy=33, fall_damage_add_percent=-50, not_in_creative_inventory = 1},
_mcl_hardness = 0.2,
_mcl_blast_resistance = 0.2,
_mcl_blast_resistance = 1,
sounds = def.sounds or default_sounds,
drop = "",
selection_box = common_box,

View File

@ -15,7 +15,7 @@ Black Bed=Schwarzes Bett
Yellow Bed=Gelbes Bett
Green Bed=Grünes Bett
Magenta Bed=Magenta Bett
Orange Bed=Oranges Bett
Orange Bed=Orange Bett
Purple Bed=Violettes Bett
Brown Bed=Braunes Bett
Pink Bed=Rosa Bett
@ -47,4 +47,4 @@ You are missing the 'shout' privilege! It's required in order to talk in chat...
You exceeded the maximum number of messages per 10 seconds!=Sie haben die maximale Anzahl an Chatnachrichten pro 10 Sekunden überschritten!
Hey! Would you guys mind sleeping?=Hey, würdet Ihr bitte zu Bett gehen?
Sorry, but you have to wait @1 seconds until you may use this button again!=Sie müssen leider noch @1 Sekunden warten, bevor sie diesen Knopf erneut benutzen können!
@1/@2 players currently in bed.=@1/@2 Spieler aktuell im Bett.
@1/@2 players currently in bed.=@1/@2 Spieler aktuell im Bett.

View File

@ -2,4 +2,4 @@ name = mcl_beds
author = BlockMen
description =
depends = playerphysics
optional_depends = mcl_sounds, mcl_worlds, mcl_wool, mcl_dye, mcl_explosions, mcl_weather, mcl_spawn, doc, mesecons, mesecons_mvps
optional_depends = mcl_sounds, mcl_worlds, mcl_wool, mcl_dye, mcl_explosions, mcl_weather, mcl_spawn, doc, mesecons

View File

@ -1,5 +0,0 @@
# textdomain: mcl_beehives
Beehive=Bienenstock
Artificial bee nest.=
Bee Nest=
A naturally generating block that houses bees and a tasty treat...if you can get it.=

View File

@ -1,2 +0,0 @@
# textdomain: mcl_bells
Bell=Dorfglocke

View File

@ -33,8 +33,8 @@ minetest.register_node("mcl_blackstone:blackstone_gilded", {
{items = {"mcl_blackstone:blackstone_gilded"}, rarity = 1},
}
},
_mcl_blast_resistance = 6,
_mcl_hardness = 1.5,
_mcl_blast_resistance = 2,
_mcl_hardness = 2,
_mcl_silk_touch_drop = true,
_mcl_fortune_drop = {
discrete_uniform_distribution = true,
@ -196,7 +196,7 @@ end
mcl_stairs.register_stair_and_slab("blackstone", "mcl_blackstone:blackstone",
{cracky=3, pickaxey=1, material_stone=1},
{"mcl_blackstone_top.png", "mcl_blackstone_top.png", "mcl_blackstone_side.png"},
S("Blackstone Stair"),
S("Blackstone Stairs"),
S("Blackstone Slab"),
mcl_sounds.node_sound_stone_defaults(), 6, 2,
S("Double Blackstone Slab"), nil)
@ -204,7 +204,7 @@ mcl_stairs.register_stair_and_slab("blackstone", "mcl_blackstone:blackstone",
mcl_stairs.register_stair_and_slab("blackstone_polished", "mcl_blackstone:blackstone_polished",
{cracky=3, pickaxey=1, material_stone=1},
{"mcl_blackstone_polished.png"},
S("Polished Blackstone Stair"),
S("Polished Blackstone Stairs"),
S("Polished Blackstone Slab"),
mcl_sounds.node_sound_stone_defaults(), 6, 2,
S("Double Polished Blackstone Slab"), nil)
@ -212,7 +212,7 @@ mcl_stairs.register_stair_and_slab("blackstone_polished", "mcl_blackstone:blacks
mcl_stairs.register_stair_and_slab("blackstone_chiseled_polished", "mcl_blackstone:blackstone_chiseled_polished",
{cracky=3, pickaxey=1, material_stone=1},
{"mcl_blackstone_chiseled_polished.png"},
S("Chiseled Polished Blackstone Stair"),
S("Chiseled Polished Blackstone Stairs"),
S("Chiseled Polished Blackstone Slab"),
mcl_sounds.node_sound_stone_defaults(), 6, 2,
S("Double Chiseled Polished Blackstone Slab"), nil)
@ -220,13 +220,24 @@ mcl_stairs.register_stair_and_slab("blackstone_chiseled_polished", "mcl_blacksto
mcl_stairs.register_stair_and_slab("blackstone_brick_polished", "mcl_blackstone:blackstone_brick_polished",
{cracky=3, pickaxey=1, material_stone=1},
{"mcl_blackstone_polished_bricks.png"},
S("Polished Blackstone Brick Stair"),
S("Polished Blackstone Brick Slab"),
S("Polished Blackstone Brick Stair Stairs"),
S("Polished Blackstone Brick Stair Slab"),
mcl_sounds.node_sound_stone_defaults(), 6, 2,
S("Double Polished Blackstone Brick Slab"), nil)
S("Double Polished Blackstone Brick Stair Slab"), nil)
--Wall
mcl_walls.register_wall("mcl_blackstone:wall", S("Blackstone Wall"), "mcl_blackstone:blackstone")
mcl_walls.register_wall(
"mcl_blackstone:wall",
S("Blackstone Wall"),
"mcl_blackstone:blackstone",
{
"mcl_blackstone_top.png",
"mcl_blackstone_top.png",
"mcl_blackstone_side.png"
},
"",
{ cracky=3, pickaxey=1, material_stone=1 }
)
--lavacooling

View File

@ -5,28 +5,22 @@ Chiseled Polished Blackstone=Gemeißelter polierter Schwarzstein
Polished Blackstone Bricks=Polierter Schwarzsteinziegel
Basalt=Basalt
Polished Basalt=Polierter Basalt
Blackstone Slab=Schwarzsteinstufe
Polished Blackstone Slab=Polierte Schwarzsteinstufe
Chiseled Polished Blackstone Slab=Gemeißelte polierte Schwarzsteinstufe
Polished Blackstone Brick Slab=Polierte Schwarzsteinziegelstufe
Blackstone Stair=Schwarzsteintreppe
Polished Blackstone Stair=Polierte Schwarzsteintreppe
Chiseled Polished Blackstone Stair=Gemeißelte polierte Schwarzsteintreppe
Polished Blackstone Brick Stair=Polierte Schwarzsteinziegeltreppe
Quartz Bricks=Quarzziegel
Blackstone Slab=Schwarzstein Stufe
Polished Blackstone Slab=Polierte Schwarzstein Stufe
Chiseled Polished Blackstone Slab=Gemeißelte Polierte Schwarzstein Stufe
Polished Blackstone Brick Slab=Polierte Schwarzsteinziegel Stufe
Blackstone Stairs=Schwarzstein Treppe
Polished Blackstone Stairs=Polierte Schwarzstein Treppe
Chiseled Polished Blackstone Stairs=Gemeißelte Polierte Schwarzstein Treppe
Polished Blackstone Brick Stairs=Polierte Schwarzsteinziegel Treppe
Quartz Bricks=Quartz Ziegel
Soul Torch=Seelenfakel
Torches are light sources which can be placed at the side or on the top of most blocks.=
Soul Lantern=Seelenlaterne
Soul Soil=Seelenerde
Eternal Soul Fire=Seelenfeuer
Gilded Blackstone=Vergoldeter Schwarzstein
Nether Gold Ore=Nethergolderz
Smooth Basalt=Glatter Basalt
Blackstone Wall=Schwarzsteinmauer
Double Blackstone Slab=
Polished Double Blackstone Slab=
Double Chiseled Polished Blackstone Slab=
Double Polished Blackstone Brick Slab=
@1 has been cooked crisp.=@1 wurde knusprig gebraten.
@1 felt the burn.=@1 ist völlig verbrannt.

View File

@ -1,11 +0,0 @@
# textdomain: mcl_blast_furnace
Inventory=
Blast Furnace=Schmelzofen
Smelts ores faster than furnace=
Use the recipe book to see what ores you can smelt, what you can use as fuel and how long it will burn.=
Use the blast furnace to open the furnace menu.=
Place a furnace fuel in the lower slot and the source material in the upper slot.=
The blast furnace will slowly use its fuel to smelt the item.=
The result will be placed into the output slot at the right side.=
Blast Furnaces smelt several items, mainly ores and armor, using a furnace fuel, but twice as fast as a normal furnace.=
Active Blast Furnace=

View File

@ -1,76 +0,0 @@
# Bone meal API
Bonemealing callbacks and particle functions.
## _on_bone_meal(itemstack, placer, pointed_thing)
The bone meal API provides a callback definition that nodes can use to
register a handler that is executed when a bone meal item is used on it.
Nodes that wish to use the bone meal API should in their node registration
define a callback handler named `_on_bone_meal`.
Note that by registering the callback handler, the node declares that bone
meal can be used on it and as a result, when the user is not in creative
mode, the used bone meal is spent and taken from the itemstack passed to
the `on_place()` handler of the bone meal item used regardless of whether
the bone meal had an effect on the node and regardless of the result of
the callback handler.
It is for all intents and purposes up to the callback defined in the node to
decide how to handle the specific effect that bone meal has on that node.
The `_on_bone_meal` callback handler is a
`function(itemstack, placer, pointed_thing)`
Its arguments are:
* `itemstack`: the stack of bonem eal being applied
* `placer`: ObjectRef of the player who aplied the bone meal, can be nil!
* `pointed_thing`: exact pointing location (see Minetest API), where the
bone meal is applied
The return value of the handler function indicates if the bonemealing had
its intended effect. If `true`, 'bone meal particles' are spawned at the
position of the bonemealed node.
The `on_place` code in the bone meal item will spawn bone meal particles and
decrease the bone meal itemstack if the handler returned `true` and the
`placer` is not in creative mode.
## mcl_bone_meal.add_bone_meal_particle(pos, def)
Spawns standard or custom bone meal particles.
* `pos`: position, is ignored if you define def.minpos and def.maxpos
* `def`: (optional) particle definition; see minetest.add_particlespawner()
for more details.
## mcl_bone_meal.use_bone_meal(itemstack, placer, pointed_thing)
For use in on_rightclick handlers that need support bone meal processing in addition
to other behaviors. Before calling, verify that the player is wielding bone meal.
* `itemstack`: The stack of bone meal being used
* `placer`: ObjectRef of the player who aplied the bone meal, can be nil!
* `pointed_thing`: exact pointing location (see Minetest API), where the
bone meal is applied
Returns itemstack with one bone meal consumed if not in creative mode.
# Legacy API
The bone meal API also provides a legacy compatibility function. This
function is not meant to be continued and callers should migrate to the
newer bonemealing API.
## mcl_bone_meal.register_on_bone_meal_apply(function(pointed_thing, placer))
Called when the bone meal is applied anywhere.
* `pointed_thing`: exact pointing location (see Minetest API), where the
bone meal is applied
* `placer`: ObjectRef of the player who aplied the bone meal, can be nil!
This function is deprecated and will be removed at some time in the future.
Bone meal is not consumed unless the provided function returns true.
## mcl_dye.add_bone_meal_particle(pos, def)
## mcl_dye.register_on_bone_meal_apply(function(pointed_thing, user))
These shims in mcl_dye that point to corresponding legacy compatibility
functions in mcl_bone_meal remain for legacy callers that have not yet been
updated to the new API. These shims will be removed at some time in the
future.

View File

@ -1,156 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
local longdesc = S(
"Bone meal is a white dye and also useful as a fertilizer to " ..
"speed up the growth of many plants."
)
local usagehelp = S(
"Rightclick a sheep to turn its wool white. Rightclick a plant " ..
"to speed up its growth. Note that not all plants can be " ..
"fertilized like this. When you rightclick a grass block, tall " ..
"grass and flowers will grow all over the place."
)
mcl_bone_meal = {}
-- Bone meal particle API:
--- Spawns bone meal particles.
-- pos: where the particles spawn
-- def: particle spawner parameters, see minetest.add_particlespawner() for
-- details on these parameters.
--
function mcl_bone_meal.add_bone_meal_particle(pos, def)
def = def or {}
minetest.add_particlespawner({
amount = def.amount or 10,
time = def.time or 0.1,
minpos = def.minpos or vector.subtract(pos, 0.5),
maxpos = def.maxpos or vector.add(pos, 0.5),
minvel = def.minvel or vector.new(-0.01, 0.01, -0.01),
maxvel = def.maxvel or vector.new(0.01, 0.01, 0.01),
minacc = def.minacc or vector.new(0, 0, 0),
maxacc = def.maxacc or vector.new(0, 0, 0),
minexptime = def.minexptime or 1,
maxexptime = def.maxexptime or 4,
minsize = def.minsize or 0.7,
maxsize = def.maxsize or 2.4,
texture = "mcl_particles_bonemeal.png^[colorize:#00EE00:125",
glow = def.glow or 1,
})
end
-- Begin legacy bone meal API.
--
-- Compatibility code for legacy users of the old bone meal API.
-- This code will be removed at some time in the future.
--
mcl_bone_meal.bone_meal_callbacks = {}
-- Shims for the old API are still available in mcl_dye and defer to
-- the real functions in mcl_bone_meal.
--
function mcl_bone_meal.register_on_bone_meal_apply(func)
minetest.log("warning", "register_on_bone_meal_apply(func) is deprecated. Read mcl_bone_meal/API.md!")
local lines = string.split(debug.traceback(),"\n")
for _,line in ipairs(lines) do
minetest.log("warning",line)
end
table.insert(mcl_bone_meal.bone_meal_callbacks, func)
end
-- Legacy registered users of the old API are handled through this function.
--
local function legacy_apply_bone_meal(pointed_thing, placer)
-- Legacy API support
local callbacks = mcl_bone_meal.bone_meal_callbacks
for i = 1,#callbacks do
if callbacks[i](pointed_thing, placer) then
return true
end
end
return false
end
-- End legacy bone meal API
function mcl_bone_meal.use_bone_meal(itemstack, placer, pointed_thing)
local positions = {pointed_thing.under, pointed_thing.above}
for i = 1,2 do
local pos = positions[i]
-- Check protection
if mcl_util.check_area_protection(pos, pointed_thing.above, placer) then return false end
local node = minetest.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
local success = false
local consume
-- If the pointed node can be bonemealed, let it handle the processing.
if ndef and ndef._on_bone_meal then
success = ndef._on_bone_meal(itemstack, placer, {under = pos, above = vector.offset(pos, 0, 1, 0)})
consume = true
else
-- Otherwise try the legacy API.
success = legacy_apply_bone_meal(pointed_thing, placer)
consume = success
end
-- Take the item
if consume then
-- Particle effects
mcl_bone_meal.add_bone_meal_particle(pos)
if not placer or not minetest.is_creative_enabled(placer:get_player_name()) then
itemstack:take_item()
end
return itemstack
end
if success then return itemstack end
end
return itemstack
end
minetest.register_craftitem("mcl_bone_meal:bone_meal", {
description = S("Bone Meal"),
_tt_help = S("Speeds up plant growth"),
_doc_items_longdesc = longdesc,
_doc_items_usagehelp = usagehelp,
inventory_image = "mcl_bone_meal.png",
groups = {craftitem=1},
on_place = function(itemstack, placer, pointed_thing)
local pos = pointed_thing.under
local node = minetest.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
-- Use pointed node's on_rightclick function first, if present.
if placer and not placer:get_player_control().sneak then
if ndef and ndef.on_rightclick then
local new_stack = mcl_util.call_on_rightclick(itemstack, placer, pointed_thing)
if new_stack and new_stack ~= itemstack then return new_stack end
end
end
return mcl_bone_meal.use_bone_meal(itemstack, placer, pointed_thing)
end,
_on_dispense = function(itemstack, pos, droppos, dropnode, dropdir)
local pointed_thing
if dropnode.name == "air" then
pointed_thing = {above = droppos, under = vector.offset(droppos, 0, -1 ,0)}
else
pointed_thing = {above = pos, under = droppos}
end
return mcl_bone_meal.use_bone_meal(itemstack, nil, pointed_thing)
end,
_dispense_into_walkable = true
})
minetest.register_craft({
output = "mcl_bone_meal:bone_meal 3",
recipe = {{"mcl_mobitems:bone"}},
})

View File

@ -1,5 +0,0 @@
# textdomain: mcl_bone_meal
Bone Meal=Knochenmehl
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=Knochenmehl ist ein weißer Farbstoff und auch nützlich als Dünger, um das Wachstum vieler Pflanzen zu beschleunigen.
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Rechtsklicken Sie auf ein Schaf, um die Wolle weiß einzufärben. Rechtsklicken Sie auf eine Pflanze, um ihr Wachstum zu beschleunigen. Beachten Sie, dass nicht alle Pflanzen darauf ansprechen. Benutzen Sie es auf einem Grasblock, wächst viel hohes Gras und vielleicht auch ein paar Blumen.
Speeds up plant growth=Beschleunigt Pflanzenwachstum

View File

@ -1,5 +0,0 @@
# textdomain: mcl_bone_meal
Bone Meal=Harina de hueso
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=La harina de hueso es un tinte blanco y también es útil como fertilizante para acelerar el crecimiento de muchas plantas.
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=RHaga clic derecho en una oveja para volver su lana blanca. Haga clic derecho en una planta para acelerar su crecimiento. Tenga en cuenta que no todas las plantas pueden ser fertilizadas de esta manera. Cuando haces clic derecho en un bloque de hierba, crecerán hierba alta y flores por todo el lugar.
Speeds up plant growth=Acelera el crecimiento de las plantas

View File

@ -1,5 +0,0 @@
# textdomain: mcl_bone_meal
Bone Meal=Farine d'Os
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=La farine d'os est une teinture blanche et est également utile comme engrais pour accélérer la croissance de nombreuses plantes.
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Cliquez avec le bouton droit sur un mouton pour blanchir sa laine. Cliquez avec le bouton droit sur une plante pour accélérer sa croissance. Cependant, toutes les plantes ne peuvent pas être fertilisées de cette manière. Lorsque vous cliquez avec le bouton droit sur un bloc d'herbe, les hautes herbes et les fleurs poussent autour.
Speeds up plant growth=Accélère la croissance des plantes

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