Update master #1

Merged
THE-NERD2 merged 296 commits from VoxeLibre/VoxeLibre:master into master 2024-11-21 19:51:04 +01:00
275 changed files with 4223 additions and 4185 deletions

View File

@ -46,6 +46,9 @@ Armor trim models were created by Aeonix_Aeon
Source: <https://www.curseforge.com/minecraft/texture-packs/ozocraft-remix> Source: <https://www.curseforge.com/minecraft/texture-packs/ozocraft-remix>
License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 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/) The main menu images are released under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
All other files, unless mentioned otherwise, fall under: All other files, unless mentioned otherwise, fall under:

View File

@ -117,10 +117,6 @@ end
-- Array of unique hardness values for each group which affects dig time. -- Array of unique hardness values for each group which affects dig time.
local hardness_values = get_hardness_values_for_groups() 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 function compute_creativetimes(group)
local creativetimes = {} local creativetimes = {}
@ -186,6 +182,7 @@ local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
local mult = capsdef.speed or 1 local mult = capsdef.speed or 1
local uses = capsdef.uses local uses = capsdef.uses
local def = mcl_autogroup.registered_diggroups[g] 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 local max_level = def.levels and #def.levels or 1
assert(capsdef.level, toolname .. ' is missing level for ' .. g) assert(capsdef.level, toolname .. ' is missing level for ' .. g)
@ -313,6 +310,13 @@ function mcl_autogroup.get_wear(toolname, diggroup)
end end
local function overwrite() 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 for nname, ndef in pairs(minetest.registered_nodes) do
local newgroups = table.copy(ndef.groups) local newgroups = table.copy(ndef.groups)
if (nname ~= "ignore" and ndef.diggable) then if (nname ~= "ignore" and ndef.diggable) then
@ -374,4 +378,5 @@ local function overwrite()
end end
end end
overwrite() -- Make sure all tools and groups are registered
minetest.register_on_mods_loaded(overwrite)

View File

@ -1,3 +1,4 @@
name = _mcl_autogroup name = _mcl_autogroup
depends = mcl_autogroup
author = ryvnf author = ryvnf
description = VoxeLibre core mod which automatically adds groups to all items. Very important for digging times. 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. Simple flow functions.
## flowlib.is_touching(realpos, nodepos, radius) ## 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 * realpos: position
* nodepos: position * nodepos: position
* radius: number * radius: number
## flowlib.is_water(pos) ## flowlib.is_water(pos)
Return true if node at <pos> is water, false overwise. Return true if node at `pos` is water, false otherwise.
* pos: position * pos: position
## flowlib.node_is_water(node) ## flowlib.node_is_water(node)
Return true if <node> is water, false overwise. Return true if `node` is water, false otherwise.
* node: node * node: node
## flowlib.is_lava(pos) ## flowlib.is_lava(pos)
Return true if node at <pos> is lava, false overwise. Return true if node at `pos` is lava, false otherwise.
* pos: position * pos: position
## flowlib.node_is_lava(node) ## flowlib.node_is_lava(node)
Return true if <node> is lava, false overwise. Return true if `node` is lava, false otherwise.
* node: node * node: node
## flowlib.is_liquid(pos) ## flowlib.is_liquid(pos)
Return true if node at <pos> is liquid, false overwise. Return true if node at `pos` is liquid, false otherwise.
* pos: position * pos: position
## flowlib.node_is_liquid(node) ## flowlib.node_is_liquid(node)
Return true if <node> is liquid, false overwise. Return true if `node` is liquid, false otherwise.
* node: node * node: node
## flowlib.quick_flow(pos, node) ## flowlib.quick_flow(pos, node)
@ -37,8 +37,8 @@ Return direction where the water is flowing (to be use to push mobs, items...).
* node: node * node: node
## flowlib.move_centre(pos, realpos, node, radius) ## 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>. 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. WARNING: This function is never used in VL, use at your own risk. The informations described here may be wrong.
* pos: position * pos: position
* realpos: position, position of the entity * realpos: position, position of the entity
* node: node * node: node

View File

@ -1,8 +1,8 @@
# mcl_autogroup # mcl_autogroup
This mod emulate digging times from mc. This mod emulates digging times from MC.
## mcl_autogroup.can_harvest(nodename, toolname, player) ## 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 * nodename: string, valid nodename
* toolname: (optional) string, valid toolname * toolname: (optional) string, valid toolname
* player: (optinal) ObjectRef, valid player * 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) * efficiency: (optional) integer, the efficiency level the tool is enchanted with (default 0)
## mcl_autogroup.get_wear(toolname, diggroup) ## 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. WARNING: This function can only be called after mod initialization.
* toolname: string, name of the tool used * toolname: string, name of the tool used
* diggroup: string, the name of the diggroup the tool is used on * diggroup: string, the name of the diggroup the tool is used on

View File

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

View File

@ -6,7 +6,7 @@ WARNING: Not using it inside your mods may cause strange bugs (using the native
## Callbacks ## Callbacks
To modify the amount of damage made by something: To modify the amount of damage dealt by something:
```lua ```lua
--obj: an ObjectRef --obj: an ObjectRef

View File

@ -1,9 +1,13 @@
## mcl_events # mcl_events
### Registering Events
`mlc_events.register_event("name",def)`
#### Event Definition ## Registering Events
{
`mcl_events.register_event("name", def)`
### Event Definition
```
{
stage = 0, stage = 0,
max_stage = 1, max_stage = 1,
percent = 100, percent = 100,
@ -22,6 +26,8 @@
cond_complete = function(event) end, cond_complete = function(event) end,
--return true if event finished successfully --return true if event finished successfully
} }
```
### Debugging ## Debugging
* /event_start <event> -- starts the given event at the current player coordinates
* /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) ## mcl_explosions.explode(pos, strength, info, puncher)
* pos: position, initial position of the explosion * pos: position, initial position of the explosion
* strenght: number, radius of the explosion * strength: number, radius of the explosion
* info: table, explosion informations: * info: table, explosion informations:
* drop_chance: number, if specified becomes the drop chance of all nodes in the explosion (default: 1.0 / strength) * 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 * 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) * sound: bool, if true, the explosion will play a sound (default: true)
* particles: bool, if true, the explosion will create particles (default: true) * particles: bool, if true, the explosion will create particles (default: true)
* fire: bool, if true, 1/3 nodes become fire (default: false) * fire: bool, if true, 1/3 of nodes become fire (default: false)
* griefing: bool, if true, the explosion will destroy nodes (default: true) * 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) * 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,6 +48,20 @@ function table.pairs_by_keys(t, f)
return iter return iter
end 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 LOGGING_ON = minetest.settings:get_bool("mcl_logging_default", false)
local LOG_MODULE = "[MCL2]" local LOG_MODULE = "[MCL2]"
function mcl_util.mcl_log(message, module, bypass_default_logger) function mcl_util.mcl_log(message, module, bypass_default_logger)
@ -1103,3 +1117,39 @@ function mcl_util.is_it_christmas()
return false return false
end end
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,20 +5,21 @@ This mod provides utility functions about positions and dimensions.
This function returns: This function returns:
* true, true: if pos is in deep void (deadly) * true, true: if pos is in deep void (deadly)
* true, false: if the pos is in void (non deadly) * true, false: if the pos is in void (non-deadly)
* false, false: owerwise * false, false: otherwise
Params: Params:
* pos: position * pos: position
## mcl_worlds.y_to_layer(y) ## 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. Mainly used for ore generation.
Takes an Y coordinate as input and returns: 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
* 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" If the Y coordinate is not located in any dimension, it will return: nil, "void"
Params: Params:
@ -26,7 +27,7 @@ Params:
* y: int * y: int
## mcl_worlds.pos_to_dimension(pos) ## 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 * pos: position
@ -38,31 +39,32 @@ mc_dimension can be "overworld", "nether", "end" (default: "overworld").
* mc_dimension: string * mc_dimension: string
## mcl_worlds.has_weather(pos) ## mcl_worlds.has_weather(pos)
Returns true if <pos> can have weather, false owerwise. Returns true if pos can have weather, false otherwise.
Weather can be only in the overworld. Weather can be only in the overworld.
* pos: position * pos: position
## mcl_worlds.has_dust(pos) ## mcl_worlds.has_dust(pos)
Returns true if <pos> can have nether dust, false owerwise. Returns true if pos can have nether dust, false otherwise.
Nether dust can be only in the nether. Nether dust can be only in the nether.
* pos: position * pos: position
## mcl_worlds.compass_works(pos) ## mcl_worlds.compass_works(pos)
Returns true if compasses are working at <pos>, false owerwise. Returns true if compasses are working at pos, false otherwise.
In mc, you cant use compass in the nether and the end. In MC, you cant use compass in the nether and the end.
* pos: position * pos: position
## mcl_worlds.compass_works(pos) ## mcl_worlds.compass_works(pos)
Returns true if clock are working at <pos>, false owerwise. Returns true if clock are working at pos, false otherwise.
In mc, you cant use clock in the nether and the end. In MC, you cant use clock in the nether and the end.
* pos: position * pos: position
## mcl_worlds.register_on_dimension_change(function(player, dimension, last_dimension)) ## mcl_worlds.register_on_dimension_change(function(player, dimension, last_dimension))
Register a callback function func(player, dimension). Register a callback function func(player, dimension).
It will be called whenever a player changes between dimensions. It will be called whenever a player changes between dimensions.
The void counts as dimension. The void counts as dimension.
@ -75,7 +77,7 @@ The void counts as dimension.
Table containing all function registered with mcl_worlds.register_on_dimension_change() Table containing all function registered with mcl_worlds.register_on_dimension_change()
## mcl_worlds.dimension_change(player, dimension) ## 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 * player: player, player who changed the dimension
* dimension: string, new dimension ("overworld", "nether", "end", "void") * dimension: string, new dimension ("overworld", "nether", "end", "void")

View File

@ -10,3 +10,14 @@ Rightclick on a water source to place the boat. Rightclick the boat to enter it.
Spruce Boat=Fichtenboot Spruce Boat=Fichtenboot
Water vehicle=Wasserfahrzeug Water vehicle=Wasserfahrzeug
Sneak to dismount=Zum Aussteigen schleichen 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.expirationtime = t
pt.texture = "[combine:2x2:" .. pt.texture = "[combine:2x2:" ..
-math.random(1, 16) .. "," .. -math.random(1, 16) .. "=" .. texture math.random(-14, 0) .. "," .. math.random(-14, 0) .. "=" .. texture
minetest.add_particle(pt) minetest.add_particle(pt)

View File

@ -321,6 +321,7 @@ function minetest.handle_node_drops(pos, drops, digger)
if tool and nodedef._mcl_fortune_drop and enchantments.fortune then if tool and nodedef._mcl_fortune_drop and enchantments.fortune then
local fortune_level = enchantments.fortune local fortune_level = enchantments.fortune
local fortune_drop = nodedef._mcl_fortune_drop local fortune_drop = nodedef._mcl_fortune_drop
local simple_drop = nodedef._mcl_fortune_drop.drop_without_fortune
if fortune_drop.discrete_uniform_distribution then if fortune_drop.discrete_uniform_distribution then
local min_count = fortune_drop.min_count local min_count = fortune_drop.min_count
local max_count = fortune_drop.max_count + fortune_level * (fortune_drop.factor or 1) local max_count = fortune_drop.max_count + fortune_level * (fortune_drop.factor or 1)
@ -336,6 +337,12 @@ function minetest.handle_node_drops(pos, drops, digger)
local drop = get_fortune_drops(fortune_drop, fortune_level) local drop = get_fortune_drops(fortune_drop, fortune_level)
drops = get_drops(drop, tool:get_name(), dug_node.param2, nodedef.paramtype2) drops = get_drops(drop, tool:get_name(), dug_node.param2, nodedef.paramtype2)
end end
if simple_drop then
for _, item in pairs(simple_drop) do
table.insert(drops, item)
end
end
end end
if digger and mcl_experience.throw_xp and not silk_touch_drop then if digger and mcl_experience.throw_xp and not silk_touch_drop then

View File

@ -388,7 +388,7 @@ end
local function on_step_work (self, dtime) local function on_step_work(self, dtime, moveresult)
local pos = self.object:get_pos() local pos = self.object:get_pos()
if not pos then return end if not pos then return end
@ -402,7 +402,7 @@ local function on_step_work (self, dtime)
-- Do we abandon out of here now? -- Do we abandon out of here now?
end end
if self:falling(pos) then return end if self:falling(pos, moveresult) then return end
if self:step_damage (dtime, pos) then return end if self:step_damage (dtime, pos) then return end
if self.state == "die" then return end if self.state == "die" then return end
@ -502,11 +502,11 @@ end
-- main mob function -- main mob function
function mob_class:on_step(dtime) function mob_class:on_step(dtime, moveresult)
if not DEVELOPMENT then if not DEVELOPMENT then
-- Removed as bundled Lua (5.1 doesn't support xpcall) -- 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 = xpcall(on_step_work, on_step_error_handler, self, dtime)
local status, retVal = pcall(on_step_work, self, dtime) local status, retVal = pcall(on_step_work, self, dtime, moveresult)
if status then if status then
return retVal return retVal
else else
@ -521,7 +521,7 @@ function mob_class:on_step(dtime)
log_error (dump(retVal), dump(pos), dump(self)) log_error (dump(retVal), dump(pos), dump(self))
end end
else else
return on_step_work (self, dtime) return on_step_work (self, dtime, moveresult)
end end
end end

View File

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

View File

@ -5,6 +5,7 @@ local validate_vector = mcl_util.validate_vector
local active_particlespawners = {} local active_particlespawners = {}
local disable_blood = minetest.settings:get_bool("mobs_disable_blood") local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
local DEFAULT_FALL_SPEED = -9.81*1.5 local DEFAULT_FALL_SPEED = -9.81*1.5
local PI_THIRD = math.pi / 3 -- 60 degrees
local PATHFINDING = "gowp" local PATHFINDING = "gowp"
@ -294,86 +295,66 @@ function mcl_mobs:set_animation(self, anim)
self:set_animation(anim) self:set_animation(anim)
end 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 function who_are_you_looking_at (self, dtime)
local pos = self.object:get_pos() if self.order == "sleep" then
self._locked_object = nil
return
end
local stop_look_at_player_chance = math.random(833/self.curiosity)
-- was 10000 - div by 12 for avg entities as outside loop -- 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 if self.attack then
if not self.target_time_lost then self._locked_object = not self.target_time_lost and self.attack or nil
self._locked_object = self.attack
else
self._locked_object = nil
end
elseif self.following then elseif self.following then
self._locked_object = self.following self._locked_object = self.following
elseif self._locked_object then elseif self._locked_object then
if stop_look_at_player then if stop_look_at_player then self._locked_object = nil end
--minetest.log("Stop look: ".. self.name)
self._locked_object = nil
end
elseif not self._locked_object then elseif not self._locked_object then
if mcl_util.check_dtime_timer(self, dtime, "step_look_for_someone", 0.2) then if mcl_util.check_dtime_timer(self, dtime, "step_look_for_someone", 0.2) then
--minetest.log("Change look check: ".. self.name) local pos = self.object:get_pos()
-- 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 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 self._locked_object = obj
break break
elseif obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity()) then elseif obj:is_player() or (obj:get_luaentity() and self ~= obj:get_luaentity() and obj:get_luaentity().name == self.name) then
if look_at_player then -- For the wither this was 20/60=0.33, so probably need to rebalance and divide rates.
--minetest.log("Change look to mob: ".. self.name) -- 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
self._locked_object = obj self._locked_object = obj
break break
end end
end end
end end
end end
end end
end end
function mob_class:check_head_swivel(dtime) function mob_class:check_head_swivel(dtime)
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
who_are_you_looking_at(self, dtime)
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
local final_rotation = vector.zero() local locked_object = self._locked_object
local oldp,oldr = self.object:get_bone_position(self.head_swivel) if locked_object and (locked_object:is_player() or locked_object:get_luaentity()) and locked_object:get_hp() > 0 then
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 local _locked_object_eye_height = 1.5
if self._locked_object:get_luaentity() then if locked_object:is_player() then
_locked_object_eye_height = self._locked_object:get_luaentity().head_eye_height _locked_object_eye_height = locked_object:get_properties().eye_height
end elseif locked_object:get_luaentity() then
if self._locked_object:is_player() then _locked_object_eye_height = locked_object:get_luaentity().head_eye_height
_locked_object_eye_height = self._locked_object:get_properties().eye_height
end end
if _locked_object_eye_height then if _locked_object_eye_height then
local self_rot = self.object:get_rotation() local self_rot = self.object:get_rotation()
-- If a mob is attached, should we really be messing with what they are looking at? -- If a mob is attached, should we really be messing with what they are looking at?
-- Should this be excluded? -- Should this be excluded?
@ -381,40 +362,48 @@ function mob_class:check_head_swivel(dtime)
self_rot = self.object:get_attach():get_rotation() self_rot = self.object:get_attach():get_rotation()
end end
local player_pos = self._locked_object:get_pos() local ps = self.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))) ps.y = ps.y + self.head_eye_height * .7
local mob_yaw = math.deg(-(-(self_rot.y)-(-minetest.dir_to_yaw(direction_player))))+self.head_yaw_offset local pt = locked_object:get_pos()
local mob_pitch = math.deg(-dir_to_pitch(direction_player))*self.head_pitch_multiplier 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
if (mob_yaw < -60 or mob_yaw > 60) and not (self.attack and self.state == "attack" and not self.runaway) then if (mob_yaw < -PI_THIRD or mob_yaw > PI_THIRD) and not (self.attack and self.state == "attack" and not self.runaway) then
final_rotation = vector.multiply(oldr, 0.9) newr = vector.multiply(oldr, 0.9)
elseif self.attack and self.state == "attack" and not self.runaway then elseif self.attack and self.state == "attack" and not self.runaway then
if self.head_yaw == "y" then if self.head_yaw == "y" then
final_rotation = vector.new(mob_pitch, mob_yaw, 0) newr = vector.new(mob_pitch, mob_yaw, 0)
elseif self.head_yaw == "z" then elseif self.head_yaw == "z" then
final_rotation = vector.new(mob_pitch, 0, -mob_yaw) newr = vector.new(mob_pitch, 0, -mob_yaw)
end end
else else
if self.head_yaw == "y" then if self.head_yaw == "y" then
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, ((mob_yaw-oldr.y)*.3)+oldr.y, 0) newr = vector.new((mob_pitch-oldr.x)*.3+oldr.x, (mob_yaw-oldr.y)*.3+oldr.y, 0)
elseif self.head_yaw == "z" then elseif self.head_yaw == "z" then
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, 0, -(((mob_yaw-oldr.y)*.3)+oldr.y)*3) newr = vector.new((mob_pitch-oldr.x)*.3+oldr.x, 0, ((mob_yaw-oldr.y)*.3+oldr.y)*-3)
end end
end end
end end
elseif not self._locked_object and math.abs(oldr.y) > 3 and math.abs(oldr.x) < 3 then elseif not locked_object and math.abs(oldr.y) > 0.05 and math.abs(oldr.x) < 0.05 then
final_rotation = vector.multiply(oldr, 0.9) newr = vector.multiply(oldr, 0.9)
else
--final_rotation = vector.new(0,0,0)
end end
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horizontal_head_height), final_rotation) -- 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))
end
end end
function mob_class:set_animation_speed() function mob_class:set_animation_speed()
local v = self.object:get_velocity() local v = self.object:get_velocity()
if v then if v then

View File

@ -141,7 +141,7 @@ function mcl_mobs.register_mob(name, def)
local final_def = { local final_def = {
use_texture_alpha = def.use_texture_alpha, use_texture_alpha = def.use_texture_alpha,
head_swivel = def.head_swivel or nil, -- bool to activate this function head_swivel = def.head_swivel or nil, -- bool to activate this function
head_yaw_offset = def.head_yaw_offset or 0, -- for wonkey model bones head_yaw_offset = math.rad(def.head_yaw_offset or 0), -- for wonkey model bones
head_pitch_multiplier = def.head_pitch_multiplier or 1, --for inverted pitch 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 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 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
@ -528,7 +528,7 @@ end
-- Note: This also introduces the “spawn_egg” group: -- Note: This also introduces the “spawn_egg” group:
-- * spawn_egg=1: Spawn egg (generic mob, no metadata) -- * spawn_egg=1: Spawn egg (generic mob, no metadata)
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata) -- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addegg, no_creative) function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, addegg, no_creative)
local grp = {spawn_egg = 1} local grp = {spawn_egg = 1}
@ -539,7 +539,7 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")" local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")"
if old_spawn_icons then if old_spawn_icons then
local mobname = mob:gsub("mobs_mc:","") local mobname = mob_id:gsub("mobs_mc:","")
local fn = "mobs_mc_spawn_icon_"..mobname..".png" local fn = "mobs_mc_spawn_icon_"..mobname..".png"
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then
invimg = fn invimg = fn
@ -551,7 +551,7 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
end end
-- register old stackable mob egg -- register old stackable mob egg
minetest.register_craftitem(mob, { minetest.register_craftitem(mob_id, {
description = desc, description = desc,
inventory_image = invimg, inventory_image = invimg,
@ -561,7 +561,6 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
_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."), _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) on_place = function(itemstack, placer, pointed_thing)
local pos = pointed_thing.above local pos = pointed_thing.above
-- am I clicking on something with existing on_rightclick function? -- am I clicking on something with existing on_rightclick function?
@ -571,11 +570,12 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
return def.on_rightclick(pointed_thing.under, under, placer, itemstack) return def.on_rightclick(pointed_thing.under, under, placer, itemstack)
end 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 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 name = placer:get_player_name()
local privs = minetest.get_player_privs(name) local privs = minetest.get_player_privs(name)
if under.name == "mcl_mobspawners:spawner" then if under.name == "mcl_mobspawners:spawner" then
if minetest.is_protected(pointed_thing.under, name) then if minetest.is_protected(pointed_thing.under, name) then
minetest.record_protection_violation(pointed_thing.under, name) minetest.record_protection_violation(pointed_thing.under, name)
@ -593,7 +593,6 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
--minetest.log("max light: " .. mob_light_lvl[2]) --minetest.log("max light: " .. mob_light_lvl[2])
-- Handle egg conversion -- Handle egg conversion
local mob_name = itemstack:get_name()
local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to
if convert_to then mob_name = convert_to end if convert_to then mob_name = convert_to end
@ -604,19 +603,24 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
return itemstack return itemstack
end end
if not minetest.registered_entities[mob] then if not minetest.registered_entities[mob_name] then
return itemstack return itemstack
end end
if minetest.settings:get_bool("only_peaceful_mobs", false) if minetest.settings:get_bool("only_peaceful_mobs", false)
and minetest.registered_entities[mob].type == "monster" then and minetest.registered_entities[mob_name].type == "monster" then
minetest.chat_send_player(name, S("Only peaceful mobs allowed!")) minetest.chat_send_player(name, S("Only peaceful mobs allowed!"))
return itemstack return itemstack
end end
pos.y = pos.y - 0.5 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
local mob = minetest.add_entity(pos, mob)
local entityname = itemstack:get_name() local entityname = itemstack:get_name()
minetest.log("action", "Player " ..name.." spawned "..entityname.." at "..minetest.pos_to_string(pos)) minetest.log("action", "Player " ..name.." spawned "..entityname.." at "..minetest.pos_to_string(pos))
local ent = mob:get_luaentity() local ent = mob:get_luaentity()
@ -647,5 +651,4 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
return itemstack return itemstack
end, end,
}) })
end end

View File

@ -258,6 +258,18 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
end end
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 not moving then set animation and return
if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
@ -273,18 +285,6 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
mcl_mobs:set_animation(entity, moving_anim) mcl_mobs:set_animation(entity, moving_anim)
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
-- enforce speed limit forward and reverse -- enforce speed limit forward and reverse
local max_spd = entity.max_speed_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.state = "stand"
self:set_animation( "stand") self:set_animation( "stand")
end end
yaw = yaw + math.random(-0.5, 0.5) yaw = yaw + math.random() - 0.5
yaw = self:set_yaw( yaw, 8) yaw = self:set_yaw( yaw, 8)
return return
end end
@ -788,9 +788,9 @@ function mob_class:flop()
if self.object:get_velocity().y < 0.1 then if self.object:get_velocity().y < 0.1 then
self:mob_sound("flop") self:mob_sound("flop")
self.object:set_velocity({ self.object:set_velocity({
x = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED), x = (math.random()-0.5) * 2 * FLOP_HOR_SPEED,
y = FLOP_HEIGHT, y = FLOP_HEIGHT,
z = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED), z = (math.random()-0.5) * 2 * FLOP_HOR_SPEED,
}) })
end end
end end
@ -920,7 +920,7 @@ function mob_class:do_states_walk()
-- Randomly turn -- Randomly turn
if math.random(1, 100) <= 30 then if math.random(1, 100) <= 30 then
yaw = yaw + math.random(-0.5, 0.5) yaw = yaw + math.random() - 0.5
yaw = self:set_yaw( yaw, 8) yaw = self:set_yaw( yaw, 8)
end end
end end
@ -929,7 +929,7 @@ function mob_class:do_states_walk()
-- otherwise randomly turn -- otherwise randomly turn
elseif math.random(1, 100) <= 30 then elseif math.random(1, 100) <= 30 then
yaw = yaw + math.random(-0.5, 0.5) yaw = yaw + math.random() - 0.5
yaw = self:set_yaw( yaw, 8) yaw = self:set_yaw( yaw, 8)
end 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 if lp.x > s.x then yaw = yaw +math.pi end
else else
yaw = yaw + math.random(-0.5, 0.5) yaw = yaw + math.random() - 0.5
end end
yaw = self:set_yaw( yaw, 8) yaw = self:set_yaw( yaw, 8)

View File

@ -692,14 +692,10 @@ function mob_class:do_env_damage()
local nodef3 = minetest.registered_nodes[self.standing_under] local nodef3 = minetest.registered_nodes[self.standing_under]
-- rain -- rain
if self.rain_damage > 0 then if self.rain_damage > 0 and mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
if mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then self.health = self.health - self.rain_damage
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:check_for_death("rain", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end end
end end
@ -707,14 +703,10 @@ function mob_class:do_env_damage()
-- water damage -- water damage
if self.water_damage > 0 and nodef.groups.water then if self.water_damage > 0 and nodef.groups.water then
if self.water_damage ~= 0 then self.health = self.health - self.water_damage
self.health = self.health - self.water_damage mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png", nil, nil, 1, nil)
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:check_for_death("water", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end end
elseif self.lava_damage > 0 and (nodef.groups.lava) then elseif self.lava_damage > 0 and (nodef.groups.lava) then
-- lava damage -- lava damage
@ -730,91 +722,69 @@ function mob_class:do_env_damage()
end end
elseif self.fire_damage > 0 and (nodef2.groups.fire) then elseif self.fire_damage > 0 and (nodef2.groups.fire) then
-- magma damage -- magma damage
if self.fire_damage ~= 0 then self.health = self.health - self.fire_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:check_for_death("fire", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end end
elseif self.fire_damage > 0 and (nodef.groups.fire) then elseif self.fire_damage > 0 and (nodef.groups.fire) then
-- fire damage -- fire damage
if self.fire_damage ~= 0 then self.health = self.health - self.fire_damage
self.health = self.health - self.fire_damage mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil) mcl_burning.set_on_fire(self.object, 5)
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:check_for_death("fire", {type = "environment",
pos = pos, node = self.standing_in}) then
return true
end
end end
elseif nodef.damage_per_second ~= 0 and not nodef.groups.lava and not nodef.groups.fire then elseif nodef.damage_per_second ~= 0 and not nodef.groups.lava and not nodef.groups.fire then
-- damage_per_second node check -- damage_per_second node check
self.health = self.health - nodef.damage_per_second self.health = self.health - nodef.damage_per_second
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png") 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 return true
end end
end end
-- Cactus damage -- Cactus damage
local near = minetest.find_node_near(pos, 1, "mcl_core:cactus", true) if self.standing_on == "mcl_core:cactus" or self.standing_in == "mcl_core:cactus" or self.standing_under == "mcl_core:cactus" then
if not near and near ~= nil then self:damage_mob("cactus", 2)
near = find_node_near({x=pos.x, y=pos.y-1, z=pos.z}, 1, "mcl_core:cactus", true) if self:check_for_death("cactus", {type = "environment", pos = pos, node = self.standing_in}) then
end return true
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 end
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 else
if self.health ~= 0 then 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
self:damage_mob("cactus", 2) 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 return true
end end
end end
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 -- Drowning damage
if self.breath_max ~= -1 then if self.breath_max ~= -1 then
@ -957,8 +927,7 @@ end
-- falling and fall damage -- falling and fall damage
-- returns true if mob died -- returns true if mob died
function mob_class:falling(pos) function mob_class:falling(pos, moveresult)
if self.fly and self.state ~= "die" then if self.fly and self.state ~= "die" then
return return
end end
@ -981,7 +950,13 @@ function mob_class:falling(pos)
new_acceleration = vector.new(0, DEFAULT_FALL_SPEED, 0) new_acceleration = vector.new(0, DEFAULT_FALL_SPEED, 0)
elseif v.y <= 0 and v.y > self.fall_speed then elseif v.y <= 0 and v.y > self.fall_speed then
-- fall downwards at set speed -- fall downwards at set speed
new_acceleration = vector.new(0, self.fall_speed, 0) 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
else else
-- stop accelerating once max fall speed hit -- stop accelerating once max fall speed hit
new_acceleration =vector.zero() new_acceleration =vector.zero()

View File

@ -10,12 +10,12 @@ 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 overworld_passive_threshold = tonumber(minetest.settings:get("mcl_mobs_overworld_passive_threshold")) or 7
local get_node = minetest.get_node local get_node = minetest.get_node
local get_item_group = minetest.get_item_group
local get_node_light = minetest.get_node_light local get_node_light = minetest.get_node_light
local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
local mt_get_biome_name = minetest.get_biome_name local mt_get_biome_name = minetest.get_biome_name
local get_objects_inside_radius = minetest.get_objects_inside_radius local get_objects_inside_radius = minetest.get_objects_inside_radius
local get_connected_players = minetest.get_connected_players local get_connected_players = minetest.get_connected_players
local registered_nodes = minetest.registered_nodes
local math_min = math.min local math_min = math.min
local math_max = math.max local math_max = math.max
@ -25,6 +25,7 @@ local math_ceil = math.ceil
local math_cos = math.cos local math_cos = math.cos
local math_sin = math.sin local math_sin = math.sin
local math_sqrt = math.sqrt local math_sqrt = math.sqrt
local math_abs = math.abs
local vector_distance = vector.distance local vector_distance = vector.distance
local vector_new = vector.new local vector_new = vector.new
@ -35,18 +36,14 @@ local table_remove = table.remove
local pairs = pairs local pairs = pairs
local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false) local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
local function mcl_log (message, property) local function mcl_log(message, property)
if logging then if property then message = message .. ": " .. dump(property) end
if property then mcl_util.mcl_log(message, "[Mobs spawn]", true)
message = message .. ": " .. dump(property)
end
mcl_util.mcl_log (message, "[Mobs spawn]", true)
end
end end
if not logging then mcl_log = function() end end
local dbg_spawn_attempts = 0 local dbg_spawn_attempts = 0
local dbg_spawn_succ = 0 local dbg_spawn_succ = 0
local dbg_spawn_counts = {}
local remove_far = true local remove_far = true
@ -99,169 +96,6 @@ mcl_log("Percentage of hostile spawns are group: " .. hostile_group_percentage_s
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false
-- 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 -- count how many mobs are in an area
local function count_mobs(pos,r,mob_type) local function count_mobs(pos,r,mob_type)
local num = 0 local num = 0
@ -289,11 +123,7 @@ local function count_mobs_total(mob_type)
end end
local function count_mobs_add_entry (mobs_list, mob_cat) local function count_mobs_add_entry (mobs_list, mob_cat)
if mobs_list[mob_cat] then mobs_list[mob_cat] = (mobs_list[mob_cat] or 0) + 1
mobs_list[mob_cat] = mobs_list[mob_cat] + 1
else
mobs_list[mob_cat] = 1
end
end end
--categorise_by can be name or type or spawn_class --categorise_by can be name or type or spawn_class
@ -452,7 +282,7 @@ function mcl_mobs:spawn_setup(def)
local dimension = def.dimension or "overworld" local dimension = def.dimension or "overworld"
local type_of_spawning = def.type_of_spawning or "ground" local type_of_spawning = def.type_of_spawning or "ground"
local biomes = def.biomes or list_of_all_biomes local biomes = def.biomes or nil
local min_light = def.min_light or 0 local min_light = def.min_light or 0
local max_light = def.max_light or (minetest.LIGHT_MAX + 1) local max_light = def.max_light or (minetest.LIGHT_MAX + 1)
local chance = def.chance or 1000 local chance = def.chance or 1000
@ -619,14 +449,14 @@ local function get_next_mob_spawn_pos(pos)
xoff, yoff, zoff = xoff * dd, yoff * dd, zoff * dd xoff, yoff, zoff = xoff * dd, yoff * dd, zoff * dd
local goal_pos = vector.offset(pos, xoff, yoff, zoff) 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(goal_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)) mcl_log("Pos outside mapgen limits: " .. minetest.pos_to_string(goal_pos))
return nil return nil
end end
-- Calculate upper/lower y limits -- Calculate upper/lower y limits
local d2 = xoff*xoff + zoff*zoff -- squared distance in x,z plane only 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 y1 = math_sqrt(MOB_SPAWN_ZONE_OUTER_SQ - d2) -- absolue value of distance to outer sphere
local y_min, y_max local y_min, y_max
if d2 >= MOB_SPAWN_ZONE_INNER_SQ then if d2 >= MOB_SPAWN_ZONE_INNER_SQ then
@ -635,7 +465,7 @@ local function get_next_mob_spawn_pos(pos)
y_max = pos.y + y1 y_max = pos.y + y1
else else
-- Inner region, y range spans between inner and outer spheres -- Inner region, y range spans between inner and outer spheres
local y2 = math_sqrt( MOB_SPAWN_ZONE_INNER_SQ - d2 ) local y2 = math_sqrt(MOB_SPAWN_ZONE_INNER_SQ - d2)
if goal_pos.y > pos.y then if goal_pos.y > pos.y then
-- Upper hemisphere -- Upper hemisphere
y_min = pos.y + y2 y_min = pos.y + y2
@ -680,12 +510,12 @@ end
--a simple helper function for mob_spawn --a simple helper function for mob_spawn
local function biome_check(biome_list, biome_goal) local function biome_check(biome_list, biome_goal)
if not biome_goal then return false end
for _, data in pairs(biome_list) do for _, data in pairs(biome_list) do
if data == biome_goal then if data == biome_goal then
return true return true
end end
end end
return false return false
end end
@ -700,8 +530,8 @@ local function get_water_spawn(p)
end end
end end
local function has_room(self,pos) local function has_room(self, pos)
local cb = self.collisionbox local cb = self.spawnbox or self.collisionbox
local nodes = {} local nodes = {}
if self.fly_in then if self.fly_in then
local t = type(self.fly_in) local t = type(self.fly_in)
@ -712,18 +542,74 @@ local function has_room(self,pos)
end end
end end
table.insert(nodes,"air") table.insert(nodes,"air")
local x = cb[4] - cb[1]
local y = cb[5] - cb[2] -- Calculate area to check for room
local z = cb[6] - cb[3] local cb_height = cb[5] - cb[2]
local r = math.ceil(x * y * z) local p1 = vector.new(
local p1 = vector.offset(pos,cb[1],cb[2],cb[3]) math.round(pos.x + cb[1]),
local p2 = vector.offset(pos,cb[4],cb[5],cb[6]) math.floor(pos.y),
local n = #minetest.find_nodes_in_area(p1,p2,nodes) or 0 math.round(pos.z + cb[3]))
if r > n then local p2 = vector.new(
minetest.log("warning","[mcl_mobs] No room for mob "..self.name.." at "..minetest.pos_to_string(vector.round(pos))) math.round(pos.x + cb[4]),
return false 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
end end
return true
-- 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
end end
mcl_mobs.custom_biomecheck = nil mcl_mobs.custom_biomecheck = nil
@ -732,119 +618,92 @@ function mcl_mobs.register_custom_biomecheck(custom_biomecheck)
mcl_mobs.custom_biomecheck = custom_biomecheck mcl_mobs.custom_biomecheck = custom_biomecheck
end end
local function get_biome_name(pos) local function get_biome_name(pos)
if mcl_mobs.custom_biomecheck then if mcl_mobs.custom_biomecheck then return mcl_mobs.custom_biomecheck(pos) end
return mcl_mobs.custom_biomecheck (pos) local gotten_biome = minetest.get_biome_data(pos)
else return gotten_biome and mt_get_biome_name(gotten_biome.biome)
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 end
local function spawn_check(pos, spawn_def) local function spawn_check(pos, spawn_def)
if not spawn_def or not pos then return end if not spawn_def or not pos then return end
dbg_spawn_attempts = dbg_spawn_attempts + 1 dbg_spawn_attempts = dbg_spawn_attempts + 1
local dimension = mcl_worlds.pos_to_dimension(pos) local dimension = mcl_worlds.pos_to_dimension(pos)
local mob_def = minetest.registered_entities[spawn_def.name] if spawn_def.dimension ~= dimension then return end -- wrong dimension
local mob_type = mob_def.type -- find ground node below spawn position
local gotten_node = get_node(pos).name local node_name = get_node(pos).name
if not gotten_node then return end local node_def = registered_nodes[node_name]
if node_def and not node_def.groups.solid then -- try node one below instead
local biome_name = get_biome_name(pos)
if not biome_name then return end
local is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
if not is_ground then
pos.y = pos.y - 1 pos.y = pos.y - 1
gotten_node = get_node(pos).name node_name = get_node(pos).name
is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0 node_def = registered_nodes[node_name]
end 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 pos.y = pos.y + 1
local is_water = get_item_group(gotten_node, "water") ~= 0 -- check spawn height
local is_lava = get_item_group(gotten_node, "lava") ~= 0 if pos.y < spawn_def.min_height or pos.y > spawn_def.max_height then return end
local is_leaf = get_item_group(gotten_node, "leaves") ~= 0 mcl_log("spawn_check#1 position checks passed")
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 -- do not spawn ground mobs on leaves
and pos.y <= spawn_def.max_height if spawn_def.type_of_spawning == "ground" and (not node_def.groups.solid or node_def.groups.leaves) then return end
and spawn_def.dimension == dimension -- water mobs only on water
and biome_check(spawn_def.biomes, biome_name) then 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
mcl_log("Spawn level 1 check - Passed") ---- More expensive calls:
if (is_ground or spawn_def.type_of_spawning ~= "ground") -- check the biome
and (spawn_def.type_of_spawning ~= "ground" or not is_leaf) if spawn_def.biomes and not biome_check(spawn_def.biomes, get_biome_name(pos)) then return end
and (not is_farm_animal(spawn_def.name) or is_grass) -- check if there is enough room
and (spawn_def.type_of_spawning ~= "water" or is_water) local mob_def = minetest.registered_entities[spawn_def.name]
and not is_bedrock if not has_room(mob_def,pos) then return end
and has_room(mob_def,pos) -- additional checks (slime etc.)
and (spawn_def.check_position and spawn_def.check_position(pos) or spawn_def.check_position == nil) if spawn_def.check_position and not spawn_def.check_position(pos) then return end
and ( not spawn_protected or not minetest.is_protected(pos, "") ) then if spawn_protected and minetest.is_protected(pos, "") then return end
mcl_log("spawn_check#2 advanced checks passed")
mcl_log("Spawn level 2 check - Passed") -- check light thresholds
local gotten_light = get_node_light(pos) 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
if modern_lighting then local sky_light = minetest.get_natural_light(pos)
local my_node = get_node(pos) local art_light = minetest.get_artificial_light(get_node(pos).param1)
local sky_light = minetest.get_natural_light(pos) if mob_def.spawn_check then
local art_light = minetest.get_artificial_light(my_node.param1) return mob_def.spawn_check(pos, gotten_light, art_light, sky_light)
end
if mob_def.spawn_check then if mob_def.type == "monster" then
return mob_def.spawn_check(pos, gotten_light, art_light, sky_light) if dimension == "nether" then
elseif mob_type == "monster" then if art_light <= nether_threshold then
if dimension == "nether" then return true
if art_light <= nether_threshold then end
return true elseif dimension == "end" then
end if art_light <= end_threshold then
elseif dimension == "end" then return true
if art_light <= end_threshold then end
return true elseif dimension == "overworld" then
end if art_light <= overworld_threshold and sky_light <= overworld_sky_threshold then
elseif dimension == "overworld" then return true
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
end end
return false
end end
return false -- passive threshold is apparently the same in all dimensions ...
return gotten_light > overworld_passive_threshold
end end
function mcl_mobs.spawn(pos,id) 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] local def = minetest.registered_entities[id] or minetest.registered_entities["mobs_mc:"..id] or minetest.registered_entities["extra_mobs:"..id]
if not def or (def.can_spawn and not def.can_spawn(pos)) or not def.is_mob then if not def or not def.is_mob or (def.can_spawn and not def.can_spawn(pos)) then return false end
return false if not has_room(def, pos) then return false end
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) return minetest.add_entity(pos, def.name)
end end
local function spawn_group(p,mob,spawn_on,amount_to_spawn) 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 nn= minetest.find_nodes_in_area_under_air(vector.offset(p,-5,-3,-5),vector.offset(p,5,3,5),spawn_on)
local o local o
@ -1238,7 +1097,6 @@ end
minetest.register_chatcommand("mobstats",{ minetest.register_chatcommand("mobstats",{
privs = { debug = true }, privs = { debug = true },
func = function(n,param) func = function(n,param)
--minetest.chat_send_player(n,dump(dbg_spawn_counts))
local pos = minetest.get_player_by_name(n):get_pos() 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,"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) minetest.chat_send_player(n,"spawning attempts since server start:" .. dbg_spawn_succ .. "/" .. dbg_spawn_attempts)

View File

@ -72,18 +72,24 @@ local axolotl = {
fly = true, fly = true,
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" }, fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
breathes_in_water = true, breathes_in_water = true,
jump = true, jump = false, -- would get them out of the water too often
damage = 2, damage = 2,
reach = 2, reach = 2,
attack_type = "dogfight", attack_type = "dogfight",
attack_animals = true, attack_animals = true,
specific_attack = { specific_attack = {
"extra_mobs_cod", "mobs_mc:cod",
"extra_mobs_glow_squid", "mobs_mc:glow_squid",
"extra_mobs_salmon", "mobs_mc:salmon",
"extra_mobs_tropical_fish", "mobs_mc:tropical_fish",
"mobs_mc_squid" "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",
},
runaway = true, runaway = true,
} }

View File

@ -95,8 +95,8 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
end end
local pos = self.object:get_pos() local pos = self.object:get_pos()
minetest.add_particle({ minetest.add_particle({
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}, 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=math.random(1,1), z=0}, velocity = {x=0, y=1, z=0},
expirationtime = math.random(), expirationtime = math.random(),
size = math.random(1, 4), size = math.random(1, 4),
collisiondetection = true, collisiondetection = true,
@ -110,8 +110,8 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
}, },
}) })
minetest.add_particle({ minetest.add_particle({
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}, 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=math.random(1,1), z=0}, velocity = {x=0, y=1, z=0},
expirationtime = math.random(), expirationtime = math.random(),
size = math.random(1, 4), size = math.random(1, 4),
collisiondetection = true, collisiondetection = true,
@ -125,8 +125,8 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
}, },
}) })
minetest.add_particle({ minetest.add_particle({
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}, 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=math.random(1,1), z=0}, velocity = {x=0, y=1, z=0},
expirationtime = math.random(), expirationtime = math.random(),
size = math.random(1, 4), size = math.random(1, 4),
collisiondetection = true, 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)) 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 minetest.get_item_group(self.standing_in, "water") ~= 0 then
if self.object:get_velocity().y < 5 then if self.object:get_velocity().y < 5 then
self.object:add_velocity({ x = 0 , y = math.random(-.007, .007), z = 0 }) self.object:add_velocity({ x = 0 , y = math.random()*.014-.007, z = 0 })
end end
end end
--]] --]]

View File

@ -81,16 +81,16 @@ local dolphin = {
reach = 2, reach = 2,
damage = 2.5, damage = 2.5,
attack_type = "dogfight", 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) 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)) 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 minetest.get_item_group(self.standing_in, "water") ~= 0 then
if self.object:get_velocity().y < 5 then if self.object:get_velocity().y < 5 then
self.object:add_velocity({ x = 0 , y = math.random(-.007, .007), z = 0 }) self.object:add_velocity({ x = 0 , y = math.random()*.014-.007, z = 0 })
end end
end end
--]]
end, end,
--]]
} }
mcl_mobs.register_mob("mobs_mc:dolphin", dolphin) mcl_mobs.register_mob("mobs_mc:dolphin", dolphin)

View File

@ -1,5 +1,5 @@
name = mobs_mc name = mobs_mc
author = maikerumine author = maikerumine
description = Adds Minecraft-like monsters and animals. description = Adds Minecraft-like monsters and animals.
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core, mcl_util depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core, mcl_util, mcl_entity_invs
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, doc_items, mcl_worlds 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.xp_max = 20
piglin_brute.hp_min = 50 piglin_brute.hp_min = 50
piglin_brute.hp_max = 50 piglin_brute.hp_max = 50
piglin_brute.fire_resistant = 1 piglin_brute.fire_resistant = false
piglin_brute.do_custom = function() piglin_brute.do_custom = function()
return return
end end
@ -371,8 +371,8 @@ piglin_brute.on_rightclick = function()
return return
end end
piglin_brute.attacks_monsters = true piglin_brute.attacks_monsters = true
piglin_brute.lava_damage = 0 piglin_brute.lava_damage = 4
piglin_brute.fire_damage = 0 piglin_brute.fire_damage = 2
piglin_brute.attack_animals = true piglin_brute.attack_animals = true
piglin_brute.mesh = "extra_mobs_sword_piglin.b3d" piglin_brute.mesh = "extra_mobs_sword_piglin.b3d"
piglin_brute.textures = {"extra_mobs_piglin_brute.png", "default_tool_goldaxe.png", "extra_mobs_trans.png"} 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, makes_footstep_sound = false,
swim = true, swim = true,
fly = true, fly = true,
fly_in = "mcl_core:water_source", fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
breathes_in_water = true, breathes_in_water = true,
jump = false, jump = false,
view_range = 16, view_range = 16,

View File

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

View File

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

View File

@ -1,112 +1,31 @@
--License for code WTFPL and otherwise stated in readmes --License for code WTFPL and otherwise stated in readmes
local S = minetest.get_translator("mobs_mc") local S = minetest.get_translator("mobs_mc")
local MAPBLOCK_SIZE = 16 local MAPBLOCK_SIZE = 16 -- size for slime chunk logic
local SEED_OFFSET = 362 -- module specific seed
local seed = minetest.get_mapgen_setting("seed") local world_seed = (minetest.get_mapgen_setting("seed") + SEED_OFFSET) % 4294967296
-- slime density, where default N=10 is every 10th chunk
local slime_chunk_match 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 slime_chunk_spawn_max = mcl_worlds.layer_to_y(40)
local x_modifier
local z_modifier
local function split_by_char (inputstr, sep, limit) local floor = math.floor
if sep == nil then local max = math.max
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) local function is_slime_chunk(pos)
if not pos then return end if not pos then return end -- no position given
if slime_ratio == 0 then return end -- no slime chunks
local chunk_val = calculate_chunk_value (pos, x_modifier, z_modifier) if slime_ratio <= 1 then return true end -- slime everywhere
local slime_chunk = chunk_val == slime_chunk_match 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
--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 end
local check_position = function (pos)
return is_slime_chunk(pos)
end
-- Returns a function that spawns children in a circle around pos. -- Returns a function that spawns children in a circle around pos.
-- To be used as on_die callback. -- To be used as on_die callback.
-- self: mob reference -- self: mob reference
@ -116,19 +35,15 @@ end
-- eject_speed: Initial speed of child mob away from "mother" mob -- eject_speed: Initial speed of child mob away from "mother" mob
local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed) local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed)
return function(self, pos) return function(self, pos)
local posadd, newpos, dir eject_speed = eject_speed or 1
if not eject_speed then
eject_speed = 1
end
local mndef = minetest.registered_nodes[minetest.get_node(pos).name] local mndef = minetest.registered_nodes[minetest.get_node(pos).name]
local mother_stuck = mndef and mndef.walkable local mother_stuck = mndef and mndef.walkable
local angle = math.random(0, math.pi*2) local angle = math.random() * math.pi * 2
local children = {} local children = {}
local spawn_count = math.random(2, 4) local spawn_count = math.random(2, 4)
for i = 1, spawn_count do for i = 1, spawn_count do
dir = vector.new(math.cos(angle), 0, math.sin(angle)) local dir = vector.new(math.cos(angle), 0, math.sin(angle))
posadd = vector.normalize(dir) * spawn_distance local newpos = pos + dir * spawn_distance
newpos = pos + posadd
-- If child would end up in a wall, use position of the "mother", unless -- If child would end up in a wall, use position of the "mother", unless
-- the "mother" was stuck as well -- the "mother" was stuck as well
if not mother_stuck then if not mother_stuck then
@ -138,12 +53,14 @@ local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed)
eject_speed = eject_speed * 0.5 eject_speed = eject_speed * 0.5
end end
end end
local mob = minetest.add_entity(newpos, child_mob) local mob = mcl_mobs.spawn(newpos, child_mob)
if not mother_stuck then if mob then
mob:set_velocity(dir * eject_speed) if not mother_stuck then
mob:set_velocity(dir * eject_speed)
end
mob:set_yaw(angle - math.pi/2)
table.insert(children, mob)
end end
mob:set_yaw(angle - math.pi/2)
table.insert(children, mob)
angle = angle + (math.pi*2) / spawn_count angle = angle + (math.pi*2) / spawn_count
end end
-- If mother was murdered, children attack the killer after 1 second -- If mother was murdered, children attack the killer after 1 second
@ -162,16 +79,12 @@ local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed)
end end
end end
local swamp_light_max = 7 -- two different rules, underground slime chunks and regular swamp spawning
local function slime_spawn_check(pos, environmental_light, artificial_light, sky_light) 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 if pos.y <= slime_chunk_spawn_max and is_slime_chunk(pos) then
maxlight = minetest.LIGHT_MAX + 1 return max(artificial_light, sky_light) <= slime_max_light
end end
return max(artificial_light, sky_light) <= swamp_light_max
return math.max(artificial_light, sky_light) <= maxlight
end end
-- Slime -- Slime
@ -322,13 +235,13 @@ mcl_mobs:spawn_specific(
"ground", "ground",
cave_biomes, cave_biomes,
0, 0,
minetest.LIGHT_MAX+1, slime_max_light,
30, 30,
1000, 1000,
4, 4,
cave_min, cave_min,
cave_max, cave_max,
nil, nil, check_position) nil, nil, is_slime_chunk)
mcl_mobs:spawn_specific( mcl_mobs:spawn_specific(
"mobs_mc:slime_tiny", "mobs_mc:slime_tiny",
@ -349,13 +262,13 @@ mcl_mobs:spawn_specific(
"ground", "ground",
cave_biomes, cave_biomes,
0, 0,
minetest.LIGHT_MAX+1, slime_max_light,
30, 30,
1000, 1000,
4, 4,
cave_min, cave_min,
cave_max, cave_max,
nil, nil, check_position) nil, nil, is_slime_chunk)
mcl_mobs:spawn_specific( mcl_mobs:spawn_specific(
"mobs_mc:slime_small", "mobs_mc:slime_small",
@ -376,13 +289,13 @@ mcl_mobs:spawn_specific(
"ground", "ground",
cave_biomes, cave_biomes,
0, 0,
minetest.LIGHT_MAX+1, slime_max_light,
30, 30,
1000, 1000,
4, 4,
cave_min, cave_min,
cave_max, cave_max,
nil, nil, check_position) nil, nil, is_slime_chunk)
mcl_mobs:spawn_specific( mcl_mobs:spawn_specific(
"mobs_mc:slime_big", "mobs_mc:slime_big",
@ -559,3 +472,4 @@ 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") mcl_mobs.register_egg("mobs_mc:slime_big", S("Slime"), "#52a03e", "#7ebf6d")
-- FIXME: add spawn eggs for small and tiny slimes and magma cubes -- FIXME: add spawn eggs for small and tiny slimes and magma cubes

View File

@ -67,6 +67,7 @@ local spider = {
curiosity = 10, curiosity = 10,
head_yaw="z", head_yaw="z",
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.89, 0.7}, 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", visual = "mesh",
mesh = "mobs_mc_spider.b3d", mesh = "mobs_mc_spider.b3d",
textures = { textures = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,20 @@
local mods_loaded = false -- Constants
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local NIGHT_VISION_RATIO = 0.45 local NIGHT_VISION_RATIO = 0.45
local DEBUG = false
local MINIMUM_LIGHT_LEVEL = 0.2 -- Settings
local minimum_update_interval = { 250e3 }
local water_color = "#3F76E4" -- Module state
local mods_loaded = false
local mg_name = minetest.get_mapgen_setting("mg_name")
function mcl_weather.set_sky_box_clear(player, sky, fog) function mcl_weather.set_sky_box_clear(player, sky, fog)
local pos = player:get_pos() -- Make sure the player's head isn't in water before changing the skybox
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 node_head = mcl_playerinfo[player:get_player_name()].node_head
if minetest.get_item_group(node_head, "water") ~= 0 then return end
local sc = { local sc = {
day_sky = "#7BA4FF", day_sky = "#7BA4FF",
day_horizon = "#C0D8FF", day_horizon = "#C0D8FF",
@ -38,8 +43,10 @@ function mcl_weather.set_sky_box_clear(player, sky, fog)
end end
function mcl_weather.set_sky_color(player, def) function mcl_weather.set_sky_color(player, def)
local pos = player:get_pos() -- Make sure the player's head isn't in water before changing the skybox
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1.5, 0)).name, "water") ~= 0 then return end local node_head = mcl_playerinfo[player:get_player_name()].node_head
if minetest.get_item_group(node_head, "water") ~= 0 then return end
player:set_sky({ player:set_sky({
type = def.type, type = def.type,
sky_color = def.sky_color, sky_color = def.sky_color,
@ -47,25 +54,7 @@ function mcl_weather.set_sky_color(player, def)
}) })
end end
-- Function to work out light modifier at different times local skycolor = {
-- 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. -- Should be activated before do any effect.
active = true, active = true,
@ -85,319 +74,226 @@ mcl_weather.skycolor = {
-- number of colors while constructing gradient of user given colors -- number of colors while constructing gradient of user given colors
max_val = 1000, max_val = 1000,
NIGHT_VISION_RATIO = NIGHT_VISION_RATIO,
-- Table for tracking layer order -- Table for tracking layer order
layer_names = {}, layer_names = {},
-- To layer to colors table utils = {},
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 local timer = 0
minetest.register_globalstep(function(dtime) minetest.register_globalstep(function(dtime)
if mcl_weather.skycolor.active ~= true or #minetest.get_connected_players() == 0 then if skycolor.active ~= true or #minetest.get_connected_players() == 0 then
return return
end end
if mcl_weather.skycolor.force_update then if skycolor.force_update then
mcl_weather.skycolor.update_sky_color() skycolor.update_sky_color()
mcl_weather.skycolor.force_update = false skycolor.force_update = false
return return
end end
-- regular updates based on iterval -- regular updates based on iterval
timer = timer + dtime; timer = timer + dtime;
if timer >= mcl_weather.skycolor.update_interval then if timer >= skycolor.update_interval then
mcl_weather.skycolor.update_sky_color() skycolor.update_sky_color()
timer = 0 timer = 0
end end
end) end)
local function initsky(player) local function initsky(player)
if player.set_lighting then if player.set_lighting then
player:set_lighting({ shadows = { intensity = tonumber(minetest.settings:get("mcl_default_shadow_intensity") or 0.33) } }) 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,
})
end end
if (mcl_weather.skycolor.active) then if (skycolor.active) then
mcl_weather.skycolor.force_update = true mcl_weather.skycolor.force_update = true
end end
@ -408,7 +304,7 @@ minetest.register_on_joinplayer(initsky)
minetest.register_on_respawnplayer(initsky) minetest.register_on_respawnplayer(initsky)
mcl_worlds.register_on_dimension_change(function(player) mcl_worlds.register_on_dimension_change(function(player)
mcl_weather.skycolor.update_sky_color({player}) skycolor.update_sky_color({player})
end) end)
minetest.register_on_mods_loaded(function() minetest.register_on_mods_loaded(function()

View File

@ -0,0 +1,185 @@
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

@ -0,0 +1,56 @@
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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -330,9 +330,16 @@ function hb.change_hudbar(player, identifier, new_value, new_max_value, new_icon
local name = player:get_player_name() local name = player:get_player_name()
local hudtable = hb.get_hudtable(identifier) 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 if not hudtable.hudstate[name] then
return false return false
end end
local value_changed, max_changed = false, false local value_changed, max_changed = false, false
if new_value then if new_value then

View File

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

View File

@ -5,3 +5,4 @@ Error: Too many parameters!=Fehler: Zu viele Parameter!
Error: Incorrect value of XP=Fehler: Ungültiger EP-Wert Error: Incorrect value of XP=Fehler: Ungültiger EP-Wert
Error: Player not found=Fehler: Spieler nicht gefunden 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 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,8 +1,10 @@
## mcl_info ## mcl_info
An api to make custom entries in the mcl2 debug hud. An API to make custom entries in the VL debug hud.
### mcl_info.register_debug_field(name,defintion) ### mcl_info.register_debug_field(name,defintion)
Debug field defintion example: Debug field defintion example:
```
{ {
level = 3, level = 3,
--show with debug level 3 and upwards --show with debug level 3 and upwards
@ -13,6 +15,7 @@ Debug field defintion example:
-- It should output a string and determines -- It should output a string and determines
-- the content of the debug field. -- the content of the debug field.
} }
```
### mcl_info.registered_debug_fields ### 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(). 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,4 +32,5 @@ mcl_inventory.register_survival_inventory_tab({
-- Returns true by default -- Returns true by default
access = function(player) access = function(player)
end, end,
})
``` ```

View File

@ -418,6 +418,18 @@ minetest.register_on_joinplayer(function(player)
end end
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 ---@param player mt.PlayerObjectRef
function mcl_inventory.set_creative_formspec(player) function mcl_inventory.set_creative_formspec(player)
local playername = player:get_player_name() local playername = player:get_player_name()
@ -566,8 +578,10 @@ function mcl_inventory.set_creative_formspec(player)
bg_img = "crafting_creative_inactive" .. button_bg_postfix[this_tab] .. ".png" bg_img = "crafting_creative_inactive" .. button_bg_postfix[this_tab] .. ".png"
end end
return table.concat({ return table.concat({
"style[" .. this_tab .. ";border=false;bgimg=;bgimg_pressed=;noclip=true]", "style[" .. this_tab .. ";border=false;bgimg=;bgimg_pressed=]",
"image[" .. offset[this_tab] .. ";1.5,1.44;" .. bg_img .. "]", "style[" .. this_tab .. "_outer;border=false;bgimg=" .. bg_img ..
";bgimg_pressed=" .. bg_img .. "]",
"button[" .. offset[this_tab] .. ";1.5,1.44;" .. this_tab .. "_outer;]",
"item_image_button[" .. boffset[this_tab] .. ";1,1;" .. tab_icon[this_tab] .. ";" .. this_tab .. ";]", "item_image_button[" .. boffset[this_tab] .. ";1,1;" .. tab_icon[this_tab] .. ";" .. this_tab .. ";]",
}) })
end end
@ -577,11 +591,21 @@ function mcl_inventory.set_creative_formspec(player)
caption = "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, filtername[name])) .. "]" caption = "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, filtername[name])) .. "]"
end end
local touch_enabled = is_touch_enabled(playername)
players[playername].last_touch_enabled = touch_enabled
local formspec = table.concat({ local formspec = table.concat({
"formspec_version[6]", "formspec_version[6]",
"size[13,8.75]", -- 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 "",
"style_type[image;noclip=true]", "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]",
-- Hotbar -- Hotbar
mcl_formspec.get_itemslot_bg_v4(0.375, 7.375, 9, 1), mcl_formspec.get_itemslot_bg_v4(0.375, 7.375, 9, 1),
@ -638,6 +662,7 @@ function mcl_inventory.set_creative_formspec(player)
"set_focus[search;true]", "set_focus[search;true]",
}) })
end end
formspec = formspec .. "container_end[]"
if pagenum then formspec = formspec .. "p" .. tostring(pagenum) end if pagenum then formspec = formspec .. "p" .. tostring(pagenum) end
player:set_inventory_formspec(formspec) player:set_inventory_formspec(formspec)
end end
@ -655,54 +680,54 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name() local name = player:get_player_name()
if fields.blocks then if fields.blocks or fields.blocks_outer then
if players[name].page == "blocks" then return end if players[name].page == "blocks" then return end
set_inv_page("blocks", player) set_inv_page("blocks", player)
page = "blocks" page = "blocks"
elseif fields.deco then elseif fields.deco or fields.deco_outer then
if players[name].page == "deco" then return end if players[name].page == "deco" then return end
set_inv_page("deco", player) set_inv_page("deco", player)
page = "deco" page = "deco"
elseif fields.redstone then elseif fields.redstone or fields.redstone_outer then
if players[name].page == "redstone" then return end if players[name].page == "redstone" then return end
set_inv_page("redstone", player) set_inv_page("redstone", player)
page = "redstone" page = "redstone"
elseif fields.rail then elseif fields.rail or fields.rail_outer then
if players[name].page == "rail" then return end if players[name].page == "rail" then return end
set_inv_page("rail", player) set_inv_page("rail", player)
page = "rail" page = "rail"
elseif fields.misc then elseif fields.misc or fields.misc_outer then
if players[name].page == "misc" then return end if players[name].page == "misc" then return end
set_inv_page("misc", player) set_inv_page("misc", player)
page = "misc" page = "misc"
elseif fields.nix then elseif fields.nix or fields.nix_outer then
set_inv_page("all", player) set_inv_page("all", player)
page = "nix" page = "nix"
elseif fields.food then elseif fields.food or fields.food_outer then
if players[name].page == "food" then return end if players[name].page == "food" then return end
set_inv_page("food", player) set_inv_page("food", player)
page = "food" page = "food"
elseif fields.tools then elseif fields.tools or fields.tools_outer then
if players[name].page == "tools" then return end if players[name].page == "tools" then return end
set_inv_page("tools", player) set_inv_page("tools", player)
page = "tools" page = "tools"
elseif fields.combat then elseif fields.combat or fields.combat_outer then
if players[name].page == "combat" then return end if players[name].page == "combat" then return end
set_inv_page("combat", player) set_inv_page("combat", player)
page = "combat" page = "combat"
elseif fields.mobs then elseif fields.mobs or fields.mobs_outer then
if players[name].page == "mobs" then return end if players[name].page == "mobs" then return end
set_inv_page("mobs", player) set_inv_page("mobs", player)
page = "mobs" page = "mobs"
elseif fields.brew then elseif fields.brew or fields.brew_outer then
if players[name].page == "brew" then return end if players[name].page == "brew" then return end
set_inv_page("brew", player) set_inv_page("brew", player)
page = "brew" page = "brew"
elseif fields.matr then elseif fields.matr or fields.matr_outer then
if players[name].page == "matr" then return end if players[name].page == "matr" then return end
set_inv_page("matr", player) set_inv_page("matr", player)
page = "matr" page = "matr"
elseif fields.inv then elseif fields.inv or fields.inv_outer then
if players[name].page == "inv" then return end if players[name].page == "inv" then return end
page = "inv" page = "inv"
elseif fields.search == "" and not fields.creative_next and not fields.creative_prev then elseif fields.search == "" and not fields.creative_next and not fields.creative_prev then
@ -818,3 +843,19 @@ minetest.register_on_player_inventory_action(function(player, action, inventory,
player:get_inventory():set_stack("main", inventory_info.index, stack) player:get_inventory():set_stack("main", inventory_info.index, stack)
end end
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). The element will stay for the per-player param `stay` or `data.stay` (in gametick which is 1/20 second).
Here is a usage exemple: Here is a usage example:
```lua ```lua
--show a title in the HUD with minecraft color "gold" --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) ## mcl_title.params_set(player, params)
Allow mods to set `stay` and upcomming `fadeIn`/`fadeOut` params. Allow mods to set `stay` and upcoming `fadeIn`/`fadeOut` params.
```lua ```lua
mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field will stay during 30s (600/20) mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field will stay during 30s (600/20)
@ -43,7 +43,7 @@ mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field
## mcl_title.params_get(player) ## mcl_title.params_get(player)
Get `stay` and upcomming `fadeIn` and `fadeOut` params of a player as a table. Get `stay` and upcoming `fadeIn` and `fadeOut` params of a player as a table.
```lua ```lua
mcl_title.params_get(player) mcl_title.params_get(player)

View File

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

View File

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

View File

@ -0,0 +1,4 @@
# 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,13 +2,19 @@
Use the button to push it.=Benutzen Sie den Knopf, um ihn zu drücken. Use the button to push it.=Benutzen Sie den Knopf, um ihn zu drücken.
Stone Button=Steinknopf 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. 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 Oak Button=Eichenknopf
Acacia Button=Akazienknopf Acacia Button=Akazienknopf
Birch Button=Birkenknopf Birch Button=Birkenknopf
Dark Oak Button=Schwarzeichenknopf Dark Oak Button=Schwarzeichenknopf
Spruce Button=Fichtenknopf Spruce Button=Fichtenknopf
Jungle Button=Dschungelknopf 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. 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 Provides redstone power when pushed=Gibt Redstoneenergie, wenn gedrückt
Push duration: @1s=Druckdauer: @1s Push duration: @1s=Druckdauer: @1s
Pushable by arrow=Drückbar von Pfeilen 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 name = mesecons_button
depends = mesecons depends = mesecons, mesecons_mvps
optional_depends = doc optional_depends = doc

View File

@ -218,7 +218,7 @@ minetest.register_node("mesecons_pistons:piston_normal_off", {
}, },
}, },
_mcl_blast_resistance = 0.5, _mcl_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_up_normal_off"}) 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_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = false, on_rotate = false,
}) })
@ -326,7 +326,7 @@ minetest.register_node("mesecons_pistons:piston_sticky_off", {
}, },
}, },
_mcl_blast_resistance = 0.5, _mcl_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_up_sticky_off"}) 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_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = false, on_rotate = false,
}) })
@ -449,7 +449,7 @@ minetest.register_node("mesecons_pistons:piston_up_normal_off", {
footstep = mcl_sounds.node_sound_wood_defaults().footstep footstep = mcl_sounds.node_sound_wood_defaults().footstep
}), }),
_mcl_blast_resistance = 0.5, _mcl_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_down_normal_off"}) 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_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = false, on_rotate = false,
}) })
@ -556,7 +556,7 @@ minetest.register_node("mesecons_pistons:piston_up_sticky_off", {
}, },
}, },
_mcl_blast_resistance = 0.5, _mcl_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_down_sticky_off"}) 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_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = false, on_rotate = false,
}) })
@ -680,7 +680,7 @@ minetest.register_node("mesecons_pistons:piston_down_normal_off", {
}, },
}, },
_mcl_blast_resistance = 0.5, _mcl_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_normal_off"}) 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_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = false, on_rotate = false,
}) })
@ -782,7 +782,7 @@ minetest.register_node("mesecons_pistons:piston_down_sticky_off", {
}, },
}, },
_mcl_blast_resistance = 0.5, _mcl_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = function(pos, node, user, mode) on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_AXIS then if mode == screwdriver.ROTATE_AXIS then
minetest.set_node(pos, {name="mesecons_pistons:piston_sticky_off"}) 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_blast_resistance = 0.5,
_mcl_hardness = 0.5, _mcl_hardness = 1.5,
on_rotate = false, on_rotate = false,
}) })

View File

@ -6,11 +6,18 @@ Birch Pressure Plate=Birkendruckplatte
Dark Oak Pressure Plate=Schwarzeichendruckplatte Dark Oak Pressure Plate=Schwarzeichendruckplatte
Spruce Pressure Plate=Fichtendruckplatte Spruce Pressure Plate=Fichtendruckplatte
Jungle Pressure Plate=Dschungeldruckplatte 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. 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.
Stone Pressure Plate=Steindruckplatte 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. 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
Provides redstone power when pushed=Gibt Redstoneenergie aus, wenn gedrückt 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=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 and mobs=Drückbar von Spielern und Mobs
Pushable by players=Drückbar von Spielern Pushable by players=Drückbar von Spielern
Pushable by mobs=Drückbar von Mobs Pushable by mobs=Drückbar von Mobs
Pushable by players=
Pushable by mobs=

View File

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

View File

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

View File

@ -1,6 +1,21 @@
# textdomain: mcl_armor # 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. 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ü. 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 Leather Cap=Lederkappe
Iron Helmet=Eisenhelm Iron Helmet=Eisenhelm
Golden Helmet=Goldhelm Golden Helmet=Goldhelm
@ -21,6 +36,4 @@ Iron Boots=Eisenstiefel
Golden Boots=Goldstiefel Golden Boots=Goldstiefel
Diamond Boots=Diamantstiefel Diamond Boots=Diamantstiefel
Chain Boots=Kettenstiefel Chain Boots=Kettenstiefel
Smithing Template '@1'=Schmiedevorlage '@1' Smithing Template '@1'=Schmiedevorlage '@1'

View File

@ -1,6 +1,18 @@
# textdomain: mcl_armor # textdomain: mcl_armor
This is a piece of equippable armor which reduces the amount of damage you receive.= 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.= 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= Leather Cap=
Iron Helmet= Iron Helmet=
Golden 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()) local S = minetest.get_translator(minetest.get_current_modname())
for _, template_name in pairs(mcl_armor.trims.overlays) do for _, template_name in pairs(mcl_armor.trims.overlays) do
minetest.register_craftitem(mod_registername .. template_name, { minetest.register_craftitem(mod_registername .. template_name, {
description = S("Smithing Template '@1'", template_name), description = S("Smithing Template '@1'", mcl_armor.trims.translations[template_name]),
inventory_image = template_name .. "_armor_trim_smithing_template.png", inventory_image = template_name .. "_armor_trim_smithing_template.png",
}) })
minetest.register_craft({ minetest.register_craft({
output = mod_registername .. template_name .. " 2", output = mod_registername .. template_name .. " 2",
recipe = { recipe = {
{"mcl_core:diamond",mod_registername .. template_name,"mcl_core:diamond"}, {"mcl_core:diamond",mod_registername .. template_name,"mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:cobble","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:cobble","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
} }
}) })
end end
--temp craft recipies, missing structures --temp craft recipies, missing structures
minetest.register_craft({ minetest.register_craft({
output = mod_registername .. "eye", output = mod_registername .. "eye",
recipe = { 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_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"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
} }
}) })
minetest.register_craft({ minetest.register_craft({
output = mod_registername .. "ward", output = mod_registername .. "ward",
recipe = { recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"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:apple_gold_enchanted","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
} }
}) })
minetest.register_craft({ minetest.register_craft({
output = mod_registername .. "snout", output = mod_registername .. "snout",
recipe = { recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:goldblock","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:goldblock","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
} }
}) })
minetest.register_craft({ minetest.register_craft({
output = mod_registername .. "silence", output = mod_registername .. "silence",
recipe = { recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond", mod_registername.."ward","mcl_core:diamond"}, {"mcl_core:diamond", mod_registername.."ward","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
} }
}) })
minetest.register_craft({ minetest.register_craft({
output = mod_registername .. "wayfinder", output = mod_registername .. "wayfinder",
recipe = { recipe = {
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
{"mcl_core:diamond", "mcl_maps:empty_map","mcl_core:diamond"}, {"mcl_core:diamond", "mcl_maps:empty_map","mcl_core:diamond"},
{"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"}, {"mcl_core:diamond","mcl_core:diamond","mcl_core:diamond"},
} }
}) })

View File

@ -5,9 +5,6 @@
--- Copyright (C) 2022 - 2023, Michieal. See License.txt --- Copyright (C) 2022 - 2023, Michieal. See License.txt
-- CONSTS -- 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 = "mcl_bamboo:bamboo"
local BAMBOO_ENDCAP_NAME = "mcl_bamboo:bamboo_endcap" local BAMBOO_ENDCAP_NAME = "mcl_bamboo:bamboo_endcap"
local BAMBOO_PLANK = BAMBOO .. "_plank" local BAMBOO_PLANK = BAMBOO .. "_plank"
@ -16,7 +13,7 @@ local BAMBOO_PLANK = BAMBOO .. "_plank"
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname) local S = minetest.get_translator(modname)
local node_sound = mcl_sounds.node_sound_wood_defaults() local node_sound = mcl_sounds.node_sound_wood_defaults()
local pr = PseudoRandom((os.time() + 15766) * 12) -- switched from math.random() to PseudoRandom because the random wasn't very random. local pr = PseudoRandom((os.time() + 15766) * 12)
local on_rotate local on_rotate
if minetest.get_modpath("screwdriver") then if minetest.get_modpath("screwdriver") then
@ -31,38 +28,16 @@ local bamboo_def = {
paramtype = "light", paramtype = "light",
groups = {handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3}, groups = {handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3},
sounds = node_sound, 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", inventory_image = "mcl_bamboo_bamboo_shoot.png",
wield_image = "mcl_bamboo_bamboo_shoot.png", wield_image = "mcl_bamboo_bamboo_shoot.png",
_mcl_blast_resistance = 1, _mcl_blast_resistance = 1,
_mcl_hardness = 1.5, _mcl_hardness = 1,
_on_bone_meal = function(itemstack, placer, pointed_thing)
local pos = pointed_thing.under
return mcl_bamboo.grow_bamboo(pos, true)
end,
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
@ -86,7 +61,6 @@ local bamboo_def = {
on_rotate = on_rotate, on_rotate = on_rotate,
on_place = function(itemstack, placer, pointed_thing) on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing then if not pointed_thing then
return itemstack return itemstack
end end
@ -241,9 +215,6 @@ local bamboo_def = {
if node_above and ((bamboo_node and bamboo_node > 0) or node_above.name == BAMBOO_ENDCAP_NAME) then if node_above and ((bamboo_node and bamboo_node > 0) or node_above.name == BAMBOO_ENDCAP_NAME) then
minetest.remove_node(new_pos) minetest.remove_node(new_pos)
minetest.sound_play(node_sound.dug, sound_params, true) 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) minetest.add_item(new_pos, istack)
end end
end, end,
@ -253,10 +224,12 @@ minetest.register_node(BAMBOO, bamboo_def)
local bamboo_top = table.copy(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.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.tiles = {"mcl_bamboo_endcap.png"}
bamboo_top.drawtype = "plantlike_rooted" --"plantlike"
--bamboo_top.paramtype2 = "meshoptions" -- bamboo_top.drawtype = "plantlike_rooted" --"plantlike"
--bamboo_top.param2 = 2 bamboo_top.drawtype = "plantlike"
-- bamboo_top.waving = 2 bamboo_top.paramtype2 = "meshoptions"
bamboo_top.param2 = 2
bamboo_top.waving = 2
bamboo_top.special_tiles = {{name = "mcl_bamboo_endcap.png"}} bamboo_top.special_tiles = {{name = "mcl_bamboo_endcap.png"}}
bamboo_top.nodebox = nil bamboo_top.nodebox = nil
bamboo_top.selection_box = nil bamboo_top.selection_box = nil
@ -277,7 +250,7 @@ local bamboo_block_def = {
sounds = node_sound, sounds = node_sound,
paramtype2 = "facedir", paramtype2 = "facedir",
drops = "mcl_bamboo:bamboo_block", drops = "mcl_bamboo:bamboo_block",
_mcl_blast_resistance = 3, _mcl_blast_resistance = 2,
_mcl_hardness = 2, _mcl_hardness = 2,
_mcl_stripped_variant = "mcl_bamboo:bamboo_block_stripped", -- this allows us to use the built in Axe's strip block. _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, on_place = mcl_util.rotate_axis,

View File

@ -9,8 +9,6 @@ local SIDE_SCAFFOLDING = false
local SIDE_SCAFFOLD_NAME = "mcl_bamboo:scaffolding_horizontal" local SIDE_SCAFFOLD_NAME = "mcl_bamboo:scaffolding_horizontal"
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
local SCAFFOLDING_NAME = "mcl_bamboo:scaffolding" 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 = "mcl_bamboo:bamboo"
local BAMBOO_PLANK = BAMBOO .. "_plank" local BAMBOO_PLANK = BAMBOO .. "_plank"
@ -208,7 +206,7 @@ if minetest.get_modpath("mcl_fences") then
wood_groups, wood_groups,
minetest.registered_nodes["mcl_core:wood"]._mcl_hardness, minetest.registered_nodes["mcl_core:wood"]._mcl_hardness,
minetest.registered_nodes["mcl_core:wood"]._mcl_blast_resistance, minetest.registered_nodes["mcl_core:wood"]._mcl_blast_resistance,
node_sound) -- note: about missing params.. will use defaults. node_sound)
mcl_bamboo.mcl_log(dump(fence_id)) mcl_bamboo.mcl_log(dump(fence_id))
mcl_bamboo.mcl_log(dump(gate_id)) mcl_bamboo.mcl_log(dump(gate_id))

View File

@ -44,6 +44,10 @@ mcl_bamboo.bamboo_index = {
"mcl_bamboo:bamboo_2", "mcl_bamboo:bamboo_2",
"mcl_bamboo:bamboo_3", "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) function mcl_bamboo.is_bamboo(node_name)
local index = table.indexof(mcl_bamboo.bamboo_index, node_name) local index = table.indexof(mcl_bamboo.bamboo_index, node_name)
@ -74,7 +78,7 @@ function mcl_bamboo.break_orphaned(pos)
local node_name = node_below.name local node_name = node_below.name
-- short circuit checks. -- short circuit checks.
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 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
return return
end end
@ -94,172 +98,84 @@ end
--]] --]]
function mcl_bamboo.grow_bamboo(pos, bonemeal_applied) 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)) local node_above = minetest.get_node(vector.offset(pos, 0, 1, 0))
mcl_bamboo.mcl_log("Grow bamboo called; bonemeal: " .. tostring(bonemeal_applied)) log("Grow bamboo called; bonemeal: " .. tostring(bonemeal_applied))
if not bonemeal_applied and mcl_bamboo.is_bamboo(node_above.name) ~= false then if not bonemeal_applied then
return false -- short circuit this function if we're trying to grow (std) the bamboo and it's not the top shoot. -- 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
end end
if minetest.get_node_light(pos) < 8 then
-- 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")
return false return false
end end
-- variables used in more than one spot. -- Now that we are actually going to add nodes, initialize some more information
local first_shoot local first_shoot_node_name = minetest.get_node(first_shoot).name
local chk_pos
local soil_pos
local node_name = ""
local dist = 0
local node_below
-- -------------------
mcl_bamboo.mcl_log("Grow bamboo; checking for soil: ") -- If applying bonemeal, randomly grow two segments instead of one
-- the soil node below the bamboo. local grow_amount = 1
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 if bonemeal_applied then
-- handle applying bonemeal. local rng = PcgRandom(minetest.hash_node_position(pos) + minetest.get_us_time())
for py = 1, BAM_MAX_HEIGHT_TOP do if rng:next(1, GROW_DOUBLE_CHANGE) == 1 then
-- find the top node of bamboo. grow_amount = 2
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
end end
log("Growing up to "..grow_amount.." segments")
-- Non-Bonemeal growth. -- Perform bamboo growth
for py = 1, BAM_MAX_HEIGHT_TOP do for i = 1,grow_amount do
-- Find the topmost node above the stalk, and check it for "air" -- Check for air to grow into
chk_pos = vector.offset(pos, 0, py, 0) local bamboo_tip_node = minetest.get_node(bamboo_tip)
node_below = minetest.get_node(pos).name if not bamboo_tip_node or bamboo_tip_node.name ~= "air" then
node_name = minetest.get_node(chk_pos).name -- Something is blocking growth, stop and signal that use bonemeal has been used if at least on segment has grown
dist = vector.distance(soil_pos, chk_pos) return i ~= 1
if node_name ~= "air" and mcl_bamboo.is_bamboo(node_name) == false then
break
end end
-- stop growing check. ie, handle endcap placement. if actual_height + 1 == height then
if dist >= height - 1 then -- This is the end cap
local above_node_name = minetest.get_node(vector.offset(chk_pos, 0, 1, 0)).name minetest.set_node(bamboo_tip, { name = BAMBOO_ENDCAP_NAME })
if node_name == "air" and above_node_name == "air" then return true
if height - 1 == dist then else
mcl_bamboo.mcl_log("Grow bamboo; Placing endcap") -- This isn't the end cap, add a bamboo segment
minetest.set_node(chk_pos, { name = BAMBOO_ENDCAP_NAME }) minetest.set_node(bamboo_tip, { name = first_shoot_node_name })
end actual_height = actual_height + 1
end
break
end end
-- handle regular node placement. bamboo_tip = vector.offset(bamboo_tip, 0, 1, 0)
-- 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 end
return true
end end
-- Add Groups function, courtesy of Warr1024. -- Add Groups function, courtesy of Warr1024.

View File

@ -7,8 +7,6 @@
-- LOCALS -- LOCALS
local modname = minetest.get_current_modname() 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" local BAMBOO = "mcl_bamboo:bamboo"
mcl_bamboo = {} mcl_bamboo = {}

View File

@ -0,0 +1,46 @@
# 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,8 +5,6 @@
--- These are all of the fuel recipes and all of the crafting recipes, consolidated into one place. --- 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 --- 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 = "mcl_bamboo:bamboo"
local BAMBOO_PLANK = BAMBOO .. "_plank" local BAMBOO_PLANK = BAMBOO .. "_plank"
-- Craftings -- Craftings

View File

@ -1,5 +1,5 @@
# textdomain: mcl_barrels # textdomain: mcl_barrels
Barrel= Barrel=Fass
Barrels are containers which provide 27 inventory slots.= Barrels are containers which provide 27 inventory slots.=
To access its inventory, rightclick it. When broken, the items will drop out.= 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, stack_max = 1,
groups = {handy=1, bed = 1, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, deco_block = 1, flammable=-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_hardness = 0.2,
_mcl_blast_resistance = 1, _mcl_blast_resistance = 0.2,
sounds = def.sounds or default_sounds, sounds = def.sounds or default_sounds,
selection_box = common_box, selection_box = common_box,
collision_box = common_box, collision_box = common_box,
@ -288,7 +288,7 @@ function mcl_beds.register_bed(name, def)
is_ground_content = false, 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}, groups = {handy = 1, flammable = -1, bed = 2, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, not_in_creative_inventory = 1},
_mcl_hardness = 0.2, _mcl_hardness = 0.2,
_mcl_blast_resistance = 1, _mcl_blast_resistance = 0.2,
sounds = def.sounds or default_sounds, sounds = def.sounds or default_sounds,
drop = "", drop = "",
selection_box = common_box, selection_box = common_box,

View File

@ -15,7 +15,7 @@ Black Bed=Schwarzes Bett
Yellow Bed=Gelbes Bett Yellow Bed=Gelbes Bett
Green Bed=Grünes Bett Green Bed=Grünes Bett
Magenta Bed=Magenta Bett Magenta Bed=Magenta Bett
Orange Bed=Orange Bett Orange Bed=Oranges Bett
Purple Bed=Violettes Bett Purple Bed=Violettes Bett
Brown Bed=Braunes Bett Brown Bed=Braunes Bett
Pink Bed=Rosa Bett Pink Bed=Rosa Bett

View File

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

View File

@ -0,0 +1,5 @@
# 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

@ -0,0 +1,2 @@
# 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}, {items = {"mcl_blackstone:blackstone_gilded"}, rarity = 1},
} }
}, },
_mcl_blast_resistance = 2, _mcl_blast_resistance = 6,
_mcl_hardness = 2, _mcl_hardness = 1.5,
_mcl_silk_touch_drop = true, _mcl_silk_touch_drop = true,
_mcl_fortune_drop = { _mcl_fortune_drop = {
discrete_uniform_distribution = true, discrete_uniform_distribution = true,
@ -226,18 +226,7 @@ mcl_stairs.register_stair_and_slab("blackstone_brick_polished", "mcl_blackstone:
S("Double Polished Blackstone Brick Slab"), nil) S("Double Polished Blackstone Brick Slab"), nil)
--Wall --Wall
mcl_walls.register_wall( mcl_walls.register_wall("mcl_blackstone:wall", S("Blackstone Wall"), "mcl_blackstone:blackstone")
"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 --lavacooling

View File

@ -5,22 +5,28 @@ Chiseled Polished Blackstone=Gemeißelter polierter Schwarzstein
Polished Blackstone Bricks=Polierter Schwarzsteinziegel Polished Blackstone Bricks=Polierter Schwarzsteinziegel
Basalt=Basalt Basalt=Basalt
Polished Basalt=Polierter Basalt Polished Basalt=Polierter Basalt
Blackstone Slab=Schwarzstein Stufe Blackstone Slab=Schwarzsteinstufe
Polished Blackstone Slab=Polierte Schwarzstein Stufe Polished Blackstone Slab=Polierte Schwarzsteinstufe
Chiseled Polished Blackstone Slab=Gemeißelte Polierte Schwarzstein Stufe Chiseled Polished Blackstone Slab=Gemeißelte polierte Schwarzsteinstufe
Polished Blackstone Brick Slab=Polierte Schwarzsteinziegel Stufe Polished Blackstone Brick Slab=Polierte Schwarzsteinziegelstufe
Blackstone Stair=Schwarzstein Treppe Blackstone Stair=Schwarzsteintreppe
Polished Blackstone Stair=Polierte Schwarzstein Treppe Polished Blackstone Stair=Polierte Schwarzsteintreppe
Chiseled Polished Blackstone Stair=Gemeißelte Polierte Schwarzstein Treppe Chiseled Polished Blackstone Stair=Gemeißelte polierte Schwarzsteintreppe
Polished Blackstone Brick Stair=Polierte Schwarzsteinziegel Treppe Polished Blackstone Brick Stair=Polierte Schwarzsteinziegeltreppe
Quartz Bricks=Quartz Ziegel Quartz Bricks=Quarzziegel
Soul Torch=Seelenfakel 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 Lantern=Seelenlaterne
Soul Soil=Seelenerde Soul Soil=Seelenerde
Eternal Soul Fire=Seelenfeuer Eternal Soul Fire=Seelenfeuer
Gilded Blackstone=Vergoldeter Schwarzstein Gilded Blackstone=Vergoldeter Schwarzstein
Nether Gold Ore=Nethergolderz Nether Gold Ore=Nethergolderz
Smooth Basalt=Glatter Basalt 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 has been cooked crisp.=@1 wurde knusprig gebraten.
@1 felt the burn.=@1 ist völlig verbrannt. @1 felt the burn.=@1 ist völlig verbrannt.

View File

@ -0,0 +1,11 @@
# 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

@ -0,0 +1,76 @@
# 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

@ -0,0 +1,156 @@
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

@ -0,0 +1,5 @@
# 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

@ -0,0 +1,5 @@
# 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

@ -0,0 +1,5 @@
# 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

View File

@ -0,0 +1,5 @@
# textdomain: mcl_bone_meal
Bone Meal=Mączka kostna
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=Mączka kostna to biała farba i przydatny nawóz, który przyspiesza rośnięcie wielu roślin.
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.=Kliknij prawym na owcę, aby wybielić jej wełnę. Kliknij prawym na roślinę aby przyspieszyć jej wzrost. Zważ, że nie na wszystkie rośliny to tak działa. Gdy klikniesz prawym na blok trawy, wysoka trawa wyrośnie wokół.
Speeds up plant growth=Przyspiesza wzrost roślin

View File

@ -0,0 +1,5 @@
# textdomain: mcl_bone_meal
Bone Meal=Костная мука
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=Костная мука является белым красителем. Она также полезна в качестве удобрения, чтобы увеличить скорость роста многих растений.
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.=Кликните правой по овце, чтобы сделать её шерсть белой. Кликните правой по растению, чтобы ускорить его рост. Имейте в виду, что не все растения можно удобрять таким способом. Если вы кликнете по травяному блоку, то на этом месте вырастет высокая трава и цветы.
Speeds up plant growth=Ускоряет рост растений

View File

@ -0,0 +1,5 @@
# textdomain: mcl_bone_meal
Bone Meal=骨粉
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=骨粉是一種白色染料,也可作為肥料,加速許多植物的生長。
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.=右鍵點擊一隻羊,使其羊毛變白。右鍵點擊一株植物以加快其生長速度。注意,不是所有的植物都能像這樣施肥。當你右鍵點擊一個草方時,高高的草和花會到處生長。
Speeds up plant growth=加速植物生長

View File

@ -0,0 +1,5 @@
# textdomain: mcl_bone_meal
Bone Meal=
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=
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.=
Speeds up plant growth=

View File

@ -0,0 +1,3 @@
name = mcl_bone_meal
description = Bone meal can be used as a fertilizer and as a dye.
author = kabou, teknomunk

View File

Before

Width:  |  Height:  |  Size: 165 B

After

Width:  |  Height:  |  Size: 165 B

View File

@ -257,10 +257,10 @@ function ARROW_ENTITY.on_step(self, dtime)
mcl_burning.set_on_fire(obj, 5) mcl_burning.set_on_fire(obj, 5)
end end
if not self._in_player and not self._blocked then if not self._in_player and not self._blocked then
obj:punch(self.object, 1.0, { mcl_util.deal_damage(obj, self._damage, {type = "arrow", source = self._shooter, direct = self.object})
full_punch_interval=1.0, if self._extra_hit_func then
damage_groups={fleshy=self._damage}, self._extra_hit_func(obj)
}, self.object:get_velocity()) end
if obj:is_player() then if obj:is_player() then
if not mcl_shields.is_blocking(obj) then if not mcl_shields.is_blocking(obj) then
local placement local placement

View File

@ -168,7 +168,8 @@ S("The speed and damage of the arrow increases the longer you charge. The regula
itemstack:get_meta():set_string("active", "true") itemstack:get_meta():set_string("active", "true")
return itemstack return itemstack
end, end,
groups = {weapon=1,weapon_ranged=1,bow=1,enchantability=1}, groups = {weapon=1,weapon_ranged=1,bow=1,cannot_block=1,enchantability=1},
touch_interaction = "short_dig_long_place",
_mcl_uses = 385, _mcl_uses = 385,
}) })
@ -216,7 +217,7 @@ for level=0, 2 do
wield_scale = mcl_vars.tool_wield_scale, wield_scale = mcl_vars.tool_wield_scale,
stack_max = 1, stack_max = 1,
range = 0, -- Pointing range to 0 to prevent punching with bow :D range = 0, -- Pointing range to 0 to prevent punching with bow :D
groups = {not_in_creative_inventory=1, not_in_craft_guide=1, bow=1, enchantability=1}, groups = {not_in_creative_inventory=1, not_in_craft_guide=1, bow=1, cannot_block=1, enchantability=1},
-- Trick to disable digging as well -- Trick to disable digging as well
on_use = function() return end, on_use = function() return end,
on_drop = function(itemstack, dropper, pos) on_drop = function(itemstack, dropper, pos)
@ -235,6 +236,7 @@ for level=0, 2 do
on_place = function(itemstack) on_place = function(itemstack)
return itemstack return itemstack
end, end,
touch_interaction = "short_dig_long_place",
_mcl_uses = 385, _mcl_uses = 385,
}) })
end end

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