Compare commits
51 Commits
c1dc9d401c
...
2e5b0bb3c7
Author | SHA1 | Date |
---|---|---|
kno10 | 2e5b0bb3c7 | |
Mikita Wiśniewski | 41b188caea | |
kno10 | ae7995d195 | |
kno10 | e293cbe631 | |
the-real-herowl | fd6cac5f0c | |
teknomunk | e864cc19ed | |
teknomunk | 66c3c014a1 | |
teknomunk | 7807093b50 | |
teknomunk | f6c3f4bd16 | |
teknomunk | 96a03b1923 | |
teknomunk | 2145470f63 | |
teknomunk | 2ca0ccd8fe | |
teknomunk | 614518c6cd | |
kno10 | 253a06fa08 | |
kno10 | dcfd31d17a | |
teknomunk | c34aecfcab | |
Mikita Wiśniewski | 9cb4f51468 | |
kno10 | d264ba70d8 | |
Mikita Wiśniewski | 513413afc7 | |
kno10 | 011be754ca | |
teknomunk | eea96867c4 | |
the-real-herowl | cd2ee49591 | |
seventeenthShulker | de3b34f5ea | |
seventeenthShulker | e2bcd129c1 | |
seventeenthShulker | 79e8452f62 | |
the-real-herowl | b239549774 | |
teknomunk | 0b62c827aa | |
teknomunk | 626bdd13d8 | |
teknomunk | 31a3788ce1 | |
teknomunk | e65370b845 | |
teknomunk | 6c50e0a82b | |
teknomunk | 8ef08128b1 | |
teknomunk | 15efd00a29 | |
teknomunk | fa3df0d8c6 | |
teknomunk | c41ce8ba59 | |
teknomunk | 4d58f63485 | |
teknomunk | fa09b65010 | |
teknomunk | d8d39ffd52 | |
teknomunk | b6aafedf25 | |
Mikita Wiśniewski | 178cb9340d | |
kno10 | f219e5f4ae | |
kno10 | 66b7a52d47 | |
WillConker | ce5eb8d88d | |
the-real-herowl | 6eb0d3c9d9 | |
teknomunk | 1707eef672 | |
SmallJoker | fb3e9dae84 | |
SmallJoker | 7f5b19cda8 | |
the-real-herowl | 7f372d066e | |
kno10 | f9290c6493 | |
kno10 | 52124bd201 | |
Mikita Wiśniewski | 19d662dee4 |
|
@ -117,10 +117,6 @@ end
|
|||
-- Array of unique hardness values for each group which affects dig time.
|
||||
local hardness_values = get_hardness_values_for_groups()
|
||||
|
||||
-- Map indexed by hardness values which return the index of that value in
|
||||
-- hardness_value. Used for quick lookup.
|
||||
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
||||
|
||||
--[[local function compute_creativetimes(group)
|
||||
local creativetimes = {}
|
||||
|
||||
|
@ -186,6 +182,7 @@ local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
|
|||
local mult = capsdef.speed or 1
|
||||
local uses = capsdef.uses
|
||||
local def = mcl_autogroup.registered_diggroups[g]
|
||||
assert(def, toolname .. " has unknown diggroup '" .. dump(g) .. "'")
|
||||
local max_level = def.levels and #def.levels or 1
|
||||
|
||||
assert(capsdef.level, toolname .. ' is missing level for ' .. g)
|
||||
|
@ -313,6 +310,13 @@ function mcl_autogroup.get_wear(toolname, diggroup)
|
|||
end
|
||||
|
||||
local function overwrite()
|
||||
-- Refresh, now that all groups are known.
|
||||
hardness_values = get_hardness_values_for_groups()
|
||||
|
||||
-- Map indexed by hardness values which return the index of that value in
|
||||
-- hardness_value. Used for quick lookup.
|
||||
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
||||
|
||||
for nname, ndef in pairs(minetest.registered_nodes) do
|
||||
local newgroups = table.copy(ndef.groups)
|
||||
if (nname ~= "ignore" and ndef.diggable) then
|
||||
|
@ -374,4 +378,5 @@ local function overwrite()
|
|||
end
|
||||
end
|
||||
|
||||
overwrite()
|
||||
-- Make sure all tools and groups are registered
|
||||
minetest.register_on_mods_loaded(overwrite)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
name = _mcl_autogroup
|
||||
depends = mcl_autogroup
|
||||
author = ryvnf
|
||||
description = VoxeLibre core mod which automatically adds groups to all items. Very important for digging times.
|
||||
|
|
|
@ -2,33 +2,33 @@
|
|||
Simple flow functions.
|
||||
|
||||
## flowlib.is_touching(realpos, nodepos, radius)
|
||||
Return true if a sphere of <radius> at <realpos> collide with node at <nodepos>.
|
||||
Return true if a sphere of `radius` at `realpos` collide with node at `nodepos`.
|
||||
* realpos: position
|
||||
* nodepos: position
|
||||
* radius: number
|
||||
|
||||
## flowlib.is_water(pos)
|
||||
Return true if node at <pos> is water, false overwise.
|
||||
Return true if node at `pos` is water, false otherwise.
|
||||
* pos: position
|
||||
|
||||
## flowlib.node_is_water(node)
|
||||
Return true if <node> is water, false overwise.
|
||||
Return true if `node` is water, false otherwise.
|
||||
* node: node
|
||||
|
||||
## 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
|
||||
|
||||
## flowlib.node_is_lava(node)
|
||||
Return true if <node> is lava, false overwise.
|
||||
Return true if `node` is lava, false otherwise.
|
||||
* node: node
|
||||
|
||||
## 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
|
||||
|
||||
## flowlib.node_is_liquid(node)
|
||||
Return true if <node> is liquid, false overwise.
|
||||
Return true if `node` is liquid, false otherwise.
|
||||
* node: node
|
||||
|
||||
## flowlib.quick_flow(pos, node)
|
||||
|
@ -37,9 +37,9 @@ Return direction where the water is flowing (to be use to push mobs, items...).
|
|||
* node: node
|
||||
|
||||
## flowlib.move_centre(pos, realpos, node, radius)
|
||||
Return the pos of the nearest not water block near from <pos> in a sphere of <radius> at <realpos>.
|
||||
WARNING: This function is never used in mcl2, use at your own risk. The informations described here may be wrong.
|
||||
Return the pos of the nearest not water block near from `pos` in a sphere of `radius` at `realpos`.
|
||||
WARNING: This function is never used in VL, use at your own risk. The informations described here may be wrong.
|
||||
* pos: position
|
||||
* realpos: position, position of the entity
|
||||
* node: node
|
||||
* radius: number
|
||||
* radius: number
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# mcl_autogroup
|
||||
This mod emulate digging times from mc.
|
||||
This mod emulates digging times from MC.
|
||||
|
||||
## mcl_autogroup.can_harvest(nodename, toolname, player)
|
||||
Return true if <nodename> can be dig with <toolname> by <player>.
|
||||
Return true if `nodename` can be dig with `toolname` by <player>.
|
||||
* nodename: string, valid nodename
|
||||
* toolname: (optional) string, valid toolname
|
||||
* player: (optinal) ObjectRef, valid player
|
||||
|
@ -14,7 +14,7 @@ WARNING: This function can only be called after mod initialization.
|
|||
* efficiency: (optional) integer, the efficiency level the tool is enchanted with (default 0)
|
||||
|
||||
## mcl_autogroup.get_wear(toolname, diggroup)
|
||||
Return the max wear of <toolname> with <diggroup>
|
||||
Return the max wear of `toolname` with `diggroup`
|
||||
WARNING: This function can only be called after mod initialization.
|
||||
* toolname: string, name of the tool used
|
||||
* diggroup: string, the name of the diggroup the tool is used on
|
||||
|
@ -25,4 +25,4 @@ WARNING: This function can only be called after mod initialization.
|
|||
* level: (optional) string, if specified it is an array containing the names of the different digging levels the digging group supports
|
||||
|
||||
## mcl_autogroup.registered_diggroups
|
||||
List of registered diggroups, indexed by name.
|
||||
List of registered diggroups, indexed by name.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# 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.*
|
||||
Colors by upper name, in hex value.
|
||||
|
|
|
@ -6,10 +6,10 @@ WARNING: Not using it inside your mods may cause strange bugs (using the native
|
|||
|
||||
## Callbacks
|
||||
|
||||
To modify the amount of damage made by something:
|
||||
To modify the amount of damage dealt by something:
|
||||
|
||||
```lua
|
||||
--obj: an ObjectRef
|
||||
mcl_damage.register_modifier(function(obj, damage, reason)
|
||||
end, 0)
|
||||
```
|
||||
```
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
## mcl_events
|
||||
### Registering Events
|
||||
`mlc_events.register_event("name",def)`
|
||||
# mcl_events
|
||||
|
||||
#### Event Definition
|
||||
{
|
||||
## Registering Events
|
||||
|
||||
`mcl_events.register_event("name", def)`
|
||||
|
||||
### Event Definition
|
||||
|
||||
```
|
||||
{
|
||||
stage = 0,
|
||||
max_stage = 1,
|
||||
percent = 100,
|
||||
|
@ -22,6 +26,8 @@
|
|||
cond_complete = function(event) end,
|
||||
--return true if event finished successfully
|
||||
}
|
||||
```
|
||||
|
||||
### Debugging
|
||||
* /event_start <event> -- starts the given event at the current player coordinates
|
||||
## Debugging
|
||||
|
||||
* /event_start `event` -- starts the given event at the current player coordinates
|
||||
|
|
|
@ -3,13 +3,13 @@ This mod provide helper functions to create explosions.
|
|||
|
||||
## mcl_explosions.explode(pos, strength, info, puncher)
|
||||
* pos: position, initial position of the explosion
|
||||
* strenght: number, radius of the explosion
|
||||
* strength: number, radius of the explosion
|
||||
* info: table, explosion informations:
|
||||
* drop_chance: number, if specified becomes the drop chance of all nodes in the explosion (default: 1.0 / strength)
|
||||
* max_blast_resistance: int, if specified the explosion will treat all non-indestructible nodes as having a blast resistance of no more than this value
|
||||
* sound: bool, if true, the explosion will play a sound (default: true)
|
||||
* particles: bool, if true, the explosion will create particles (default: true)
|
||||
* fire: bool, if true, 1/3 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)
|
||||
* 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
|
||||
|
|
|
@ -48,6 +48,20 @@ function table.pairs_by_keys(t, f)
|
|||
return iter
|
||||
end
|
||||
|
||||
-- Removes one element randomly selected from the array section of the table and
|
||||
-- returns it, or nil if there are no elements in the array section of the table
|
||||
function table.remove_random_element(table)
|
||||
local count = #table
|
||||
if count == 0 then return nil end
|
||||
|
||||
local idx = math.random(count)
|
||||
local res = table[idx]
|
||||
table[idx] = table[count]
|
||||
table[count] = nil
|
||||
count = count - 1
|
||||
return res
|
||||
end
|
||||
|
||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default", false)
|
||||
local LOG_MODULE = "[MCL2]"
|
||||
function mcl_util.mcl_log(message, module, bypass_default_logger)
|
||||
|
|
|
@ -5,20 +5,21 @@ This mod provides utility functions about positions and dimensions.
|
|||
This function returns:
|
||||
|
||||
* true, true: if pos is in deep void (deadly)
|
||||
* true, false: if the pos is in void (non deadly)
|
||||
* false, false: owerwise
|
||||
* true, false: if the pos is in void (non-deadly)
|
||||
* false, false: otherwise
|
||||
|
||||
Params:
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.y_to_layer(y)
|
||||
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
|
||||
This function is used to calculate the Minetest y layer and dimension of the given y Minecraft layer.
|
||||
Mainly used for ore generation.
|
||||
Takes 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"
|
||||
|
||||
Params:
|
||||
|
@ -26,7 +27,7 @@ Params:
|
|||
* y: int
|
||||
|
||||
## mcl_worlds.pos_to_dimension(pos)
|
||||
This function return the Minecraft dimension of <pos> ("overworld", "nether" or "end") or "void" if <y> is in the void.
|
||||
This function return the Minecraft dimension of pos ("overworld", "nether" or "end") or "void" if y is in the void.
|
||||
|
||||
* pos: position
|
||||
|
||||
|
@ -38,31 +39,32 @@ mc_dimension can be "overworld", "nether", "end" (default: "overworld").
|
|||
* mc_dimension: string
|
||||
|
||||
## mcl_worlds.has_weather(pos)
|
||||
Returns true if <pos> can have weather, false owerwise.
|
||||
Returns true if pos can have weather, false otherwise.
|
||||
Weather can be only in the overworld.
|
||||
|
||||
* pos: position
|
||||
|
||||
## 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.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.compass_works(pos)
|
||||
Returns true if compasses are working at <pos>, false owerwise.
|
||||
In mc, you cant use compass in the nether and the end.
|
||||
Returns true if compasses are working at pos, false otherwise.
|
||||
In MC, you cant use compass in the nether and the end.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.compass_works(pos)
|
||||
Returns true if clock are working at <pos>, false owerwise.
|
||||
In mc, you cant use clock in the nether and the end.
|
||||
Returns true if clock are working at pos, false otherwise.
|
||||
In MC, you cant use clock in the nether and the end.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.register_on_dimension_change(function(player, dimension, last_dimension))
|
||||
Register a callback function func(player, dimension).
|
||||
|
||||
It will be called whenever a player changes between dimensions.
|
||||
The void counts as dimension.
|
||||
|
||||
|
@ -75,7 +77,7 @@ The void counts as dimension.
|
|||
Table containing all function registered with mcl_worlds.register_on_dimension_change()
|
||||
|
||||
## mcl_worlds.dimension_change(player, dimension)
|
||||
Notify this mod of a dimension change of <player> to <dimension>
|
||||
Notify this mod of a dimension change of player to dimension
|
||||
|
||||
* player: player, player who changed the dimension
|
||||
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
||||
|
|
|
@ -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()
|
||||
if not pos then return end
|
||||
|
||||
|
@ -402,7 +402,7 @@ local function on_step_work (self, dtime)
|
|||
-- Do we abandon out of here now?
|
||||
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.state == "die" then return end
|
||||
|
@ -502,11 +502,11 @@ end
|
|||
|
||||
|
||||
-- main mob function
|
||||
function mob_class:on_step(dtime)
|
||||
function mob_class:on_step(dtime, moveresult)
|
||||
if not DEVELOPMENT then
|
||||
-- Removed as bundled Lua (5.1 doesn't support xpcall)
|
||||
--local status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime)
|
||||
local status, retVal = pcall(on_step_work, self, dtime)
|
||||
local status, retVal = pcall(on_step_work, self, dtime, moveresult)
|
||||
if status then
|
||||
return retVal
|
||||
else
|
||||
|
@ -521,7 +521,7 @@ function mob_class:on_step(dtime)
|
|||
log_error (dump(retVal), dump(pos), dump(self))
|
||||
end
|
||||
else
|
||||
return on_step_work (self, dtime)
|
||||
return on_step_work (self, dtime, moveresult)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -285,6 +285,7 @@ function mob_class:check_breeding()
|
|||
end
|
||||
|
||||
local child = mcl_mobs.spawn_child(pos, parent1.name)
|
||||
if not child then return end
|
||||
|
||||
local ent_c = child:get_luaentity()
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ local validate_vector = mcl_util.validate_vector
|
|||
local active_particlespawners = {}
|
||||
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||
local PI_THIRD = math.pi / 3 -- 60 degrees
|
||||
|
||||
local PATHFINDING = "gowp"
|
||||
|
||||
|
@ -294,86 +295,66 @@ function mcl_mobs:set_animation(self, anim)
|
|||
self:set_animation(anim)
|
||||
end
|
||||
|
||||
local function dir_to_pitch(dir)
|
||||
--local dir2 = vector.normalize(dir)
|
||||
local xz = math.abs(dir.x) + math.abs(dir.z)
|
||||
return -math.atan2(-dir.y, xz)
|
||||
end
|
||||
|
||||
local function who_are_you_looking_at (self, dtime)
|
||||
local pos = self.object:get_pos()
|
||||
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
|
||||
|
||||
local stop_look_at_player = stop_look_at_player_chance == 1
|
||||
local stop_look_at_player = math.random() * 833 <= self.curiosity
|
||||
|
||||
if self.attack then
|
||||
if not self.target_time_lost then
|
||||
self._locked_object = self.attack
|
||||
else
|
||||
self._locked_object = nil
|
||||
end
|
||||
self._locked_object = not self.target_time_lost and self.attack or nil
|
||||
elseif self.following then
|
||||
self._locked_object = self.following
|
||||
elseif self._locked_object then
|
||||
if stop_look_at_player then
|
||||
--minetest.log("Stop look: ".. self.name)
|
||||
self._locked_object = nil
|
||||
end
|
||||
if stop_look_at_player then self._locked_object = nil end
|
||||
elseif not self._locked_object then
|
||||
if mcl_util.check_dtime_timer(self, dtime, "step_look_for_someone", 0.2) then
|
||||
--minetest.log("Change look check: ".. self.name)
|
||||
|
||||
-- For the wither this was 20/60=0.33, so probably need to rebalance and divide rates.
|
||||
-- but frequency of check isn't good as it is costly. Making others too infrequent requires testing
|
||||
local chance = 150/self.curiosity
|
||||
|
||||
if chance < 1 then chance = 1 end
|
||||
local look_at_player_chance = math.random(chance)
|
||||
|
||||
-- was 5000 but called in loop based on entities. so div by 12 as estimate avg of entities found,
|
||||
-- then div by 20 as less freq lookup
|
||||
|
||||
local look_at_player = look_at_player_chance == 1
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
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
|
||||
--minetest.log("Change look to player: ".. self.name)
|
||||
if obj:is_player() and vector.distance(pos, obj:get_pos()) < 4 then
|
||||
self._locked_object = obj
|
||||
break
|
||||
elseif obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity()) then
|
||||
if look_at_player then
|
||||
--minetest.log("Change look to mob: ".. self.name)
|
||||
elseif obj:is_player() or (obj:get_luaentity() and self ~= obj:get_luaentity() and obj:get_luaentity().name == self.name) then
|
||||
-- For the wither this was 20/60=0.33, so probably need to rebalance and divide rates.
|
||||
-- but frequency of check isn't good as it is costly. Making others too infrequent requires testing
|
||||
-- was 5000 but called in loop based on entities. so div by 12 as estimate avg of entities found,
|
||||
-- then div by 20 as less freq lookup
|
||||
if math.random() * 150 <= self.curiosity then
|
||||
self._locked_object = obj
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function mob_class:check_head_swivel(dtime)
|
||||
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
|
||||
|
||||
who_are_you_looking_at(self, dtime)
|
||||
|
||||
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 oldp,oldr = self.object:get_bone_position(self.head_swivel)
|
||||
|
||||
if self._locked_object and (self._locked_object:is_player() or self._locked_object:get_luaentity()) and self._locked_object:get_hp() > 0 then
|
||||
local locked_object = self._locked_object
|
||||
if locked_object and (locked_object:is_player() or locked_object:get_luaentity()) and locked_object:get_hp() > 0 then
|
||||
local _locked_object_eye_height = 1.5
|
||||
if self._locked_object:get_luaentity() then
|
||||
_locked_object_eye_height = self._locked_object:get_luaentity().head_eye_height
|
||||
end
|
||||
if self._locked_object:is_player() then
|
||||
_locked_object_eye_height = self._locked_object:get_properties().eye_height
|
||||
if locked_object:is_player() then
|
||||
_locked_object_eye_height = locked_object:get_properties().eye_height
|
||||
elseif locked_object:get_luaentity() then
|
||||
_locked_object_eye_height = locked_object:get_luaentity().head_eye_height
|
||||
end
|
||||
if _locked_object_eye_height then
|
||||
|
||||
local self_rot = self.object:get_rotation()
|
||||
-- If a mob is attached, should we really be messing with what they are looking at?
|
||||
-- Should this be excluded?
|
||||
|
@ -381,40 +362,48 @@ function mob_class:check_head_swivel(dtime)
|
|||
self_rot = self.object:get_attach():get_rotation()
|
||||
end
|
||||
|
||||
local player_pos = self._locked_object:get_pos()
|
||||
local direction_player = vector.direction(vector.add(self.object:get_pos(), vector.new(0, self.head_eye_height*.7, 0)), vector.add(player_pos, vector.new(0, _locked_object_eye_height, 0)))
|
||||
local mob_yaw = math.deg(-(-(self_rot.y)-(-minetest.dir_to_yaw(direction_player))))+self.head_yaw_offset
|
||||
local mob_pitch = math.deg(-dir_to_pitch(direction_player))*self.head_pitch_multiplier
|
||||
local ps = self.object:get_pos()
|
||||
ps.y = ps.y + self.head_eye_height * .7
|
||||
local pt = locked_object:get_pos()
|
||||
pt.y = pt.y + _locked_object_eye_height
|
||||
local dir = vector.direction(ps, pt)
|
||||
local mob_yaw = self_rot.y + math.atan2(dir.x, dir.z) + self.head_yaw_offset
|
||||
local mob_pitch = math.asin(-dir.y) * self.head_pitch_multiplier
|
||||
|
||||
if (mob_yaw < -60 or mob_yaw > 60) and not (self.attack and self.state == "attack" and not self.runaway) then
|
||||
final_rotation = vector.multiply(oldr, 0.9)
|
||||
if (mob_yaw < -PI_THIRD or mob_yaw > PI_THIRD) and not (self.attack and self.state == "attack" and not self.runaway) then
|
||||
newr = vector.multiply(oldr, 0.9)
|
||||
elseif self.attack and self.state == "attack" and not self.runaway 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
|
||||
final_rotation = vector.new(mob_pitch, 0, -mob_yaw)
|
||||
newr = vector.new(mob_pitch, 0, -mob_yaw)
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
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
|
||||
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
|
||||
elseif not self._locked_object and math.abs(oldr.y) > 3 and math.abs(oldr.x) < 3 then
|
||||
final_rotation = vector.multiply(oldr, 0.9)
|
||||
else
|
||||
--final_rotation = vector.new(0,0,0)
|
||||
elseif not locked_object and math.abs(oldr.y) > 0.05 and math.abs(oldr.x) < 0.05 then
|
||||
newr = vector.multiply(oldr, 0.9)
|
||||
end
|
||||
|
||||
-- 0.02 is about 1.14 degrees tolerance, to update less often
|
||||
local newp = vector.new(0, self.bone_eye_height, self.horizontal_head_height)
|
||||
if math.abs(oldr.x-newr.x) + math.abs(oldr.y-newr.y) + math.abs(oldr.z-newr.z) < 0.02 and vector.equals(oldp, newp) then return end
|
||||
if self.object.get_bone_override then -- minetest >= 5.9
|
||||
self.object:set_bone_override(self.head_swivel, {
|
||||
position = { vec = newp, absolute = true },
|
||||
rotation = { vec = newr, absolute = true } })
|
||||
else -- minetest < 5.9
|
||||
-- old API uses degrees not radians
|
||||
self.object:set_bone_position(self.head_swivel, newp, vector.apply(newr, math.deg))
|
||||
end
|
||||
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horizontal_head_height), final_rotation)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function mob_class:set_animation_speed()
|
||||
local v = self.object:get_velocity()
|
||||
if v then
|
||||
|
|
|
@ -141,7 +141,7 @@ function mcl_mobs.register_mob(name, def)
|
|||
local final_def = {
|
||||
use_texture_alpha = def.use_texture_alpha,
|
||||
head_swivel = def.head_swivel or nil, -- bool to activate this function
|
||||
head_yaw_offset = 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
|
||||
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
|
||||
|
@ -528,7 +528,7 @@ end
|
|||
-- Note: This also introduces the “spawn_egg” group:
|
||||
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
|
||||
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
|
||||
function mcl_mobs.register_egg(mob, 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}
|
||||
|
||||
|
@ -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 .. ")"
|
||||
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"
|
||||
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then
|
||||
invimg = fn
|
||||
|
@ -551,7 +551,7 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
|
|||
end
|
||||
|
||||
-- register old stackable mob egg
|
||||
minetest.register_craftitem(mob, {
|
||||
minetest.register_craftitem(mob_id, {
|
||||
|
||||
description = desc,
|
||||
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."),
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
|
||||
local pos = pointed_thing.above
|
||||
|
||||
-- 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)
|
||||
end
|
||||
|
||||
local mob_name = itemstack:get_name()
|
||||
|
||||
if pos and within_limits(pos, 0) and not minetest.is_protected(pos, placer:get_player_name()) then
|
||||
local name = placer:get_player_name()
|
||||
local privs = minetest.get_player_privs(name)
|
||||
|
||||
|
||||
if under.name == "mcl_mobspawners:spawner" then
|
||||
if minetest.is_protected(pointed_thing.under, name) then
|
||||
minetest.record_protection_violation(pointed_thing.under, name)
|
||||
|
@ -593,7 +593,6 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
|
|||
--minetest.log("max light: " .. mob_light_lvl[2])
|
||||
|
||||
-- Handle egg conversion
|
||||
local mob_name = itemstack:get_name()
|
||||
local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to
|
||||
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
|
||||
end
|
||||
|
||||
if not minetest.registered_entities[mob] then
|
||||
if not minetest.registered_entities[mob_name] then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
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!"))
|
||||
return itemstack
|
||||
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()
|
||||
minetest.log("action", "Player " ..name.." spawned "..entityname.." at "..minetest.pos_to_string(pos))
|
||||
local ent = mob:get_luaentity()
|
||||
|
@ -647,5 +651,4 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg
|
|||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
end
|
||||
|
|
|
@ -927,8 +927,7 @@ end
|
|||
|
||||
-- falling and fall damage
|
||||
-- returns true if mob died
|
||||
function mob_class:falling(pos)
|
||||
|
||||
function mob_class:falling(pos, moveresult)
|
||||
if self.fly and self.state ~= "die" then
|
||||
return
|
||||
end
|
||||
|
@ -951,7 +950,13 @@ function mob_class:falling(pos)
|
|||
new_acceleration = vector.new(0, DEFAULT_FALL_SPEED, 0)
|
||||
elseif v.y <= 0 and v.y > self.fall_speed then
|
||||
-- fall downwards at set speed
|
||||
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
|
||||
-- stop accelerating once max fall speed hit
|
||||
new_acceleration =vector.zero()
|
||||
|
|
|
@ -25,6 +25,7 @@ local math_ceil = math.ceil
|
|||
local math_cos = math.cos
|
||||
local math_sin = math.sin
|
||||
local math_sqrt = math.sqrt
|
||||
local math_abs = math.abs
|
||||
|
||||
local vector_distance = vector.distance
|
||||
local vector_new = vector.new
|
||||
|
@ -43,7 +44,6 @@ if not logging then mcl_log = function() end end
|
|||
|
||||
local dbg_spawn_attempts = 0
|
||||
local dbg_spawn_succ = 0
|
||||
local dbg_spawn_counts = {}
|
||||
|
||||
local remove_far = true
|
||||
|
||||
|
@ -96,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 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
|
||||
local function count_mobs(pos,r,mob_type)
|
||||
local num = 0
|
||||
|
@ -286,11 +123,7 @@ local function count_mobs_total(mob_type)
|
|||
end
|
||||
|
||||
local function count_mobs_add_entry (mobs_list, mob_cat)
|
||||
if mobs_list[mob_cat] then
|
||||
mobs_list[mob_cat] = mobs_list[mob_cat] + 1
|
||||
else
|
||||
mobs_list[mob_cat] = 1
|
||||
end
|
||||
mobs_list[mob_cat] = (mobs_list[mob_cat] or 0) + 1
|
||||
end
|
||||
|
||||
--categorise_by can be name or type or spawn_class
|
||||
|
@ -449,7 +282,7 @@ function mcl_mobs:spawn_setup(def)
|
|||
|
||||
local dimension = def.dimension or "overworld"
|
||||
local type_of_spawning = def.type_of_spawning or "ground"
|
||||
local biomes = def.biomes or list_of_all_biomes
|
||||
local biomes = def.biomes or nil
|
||||
local min_light = def.min_light or 0
|
||||
local max_light = def.max_light or (minetest.LIGHT_MAX + 1)
|
||||
local chance = def.chance or 1000
|
||||
|
@ -616,14 +449,14 @@ local function get_next_mob_spawn_pos(pos)
|
|||
xoff, yoff, zoff = xoff * dd, yoff * dd, zoff * dd
|
||||
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))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Calculate upper/lower y limits
|
||||
local d2 = xoff*xoff + zoff*zoff -- squared distance in x,z plane only
|
||||
local y1 = math_sqrt( MOB_SPAWN_ZONE_OUTER_SQ - d2 ) -- absolue value of distance to outer sphere
|
||||
local y1 = math_sqrt(MOB_SPAWN_ZONE_OUTER_SQ - d2) -- absolue value of distance to outer sphere
|
||||
|
||||
local y_min, y_max
|
||||
if d2 >= MOB_SPAWN_ZONE_INNER_SQ then
|
||||
|
@ -632,7 +465,7 @@ local function get_next_mob_spawn_pos(pos)
|
|||
y_max = pos.y + y1
|
||||
else
|
||||
-- Inner region, y range spans between inner and outer spheres
|
||||
local y2 = math_sqrt( MOB_SPAWN_ZONE_INNER_SQ - d2 )
|
||||
local y2 = math_sqrt(MOB_SPAWN_ZONE_INNER_SQ - d2)
|
||||
if goal_pos.y > pos.y then
|
||||
-- Upper hemisphere
|
||||
y_min = pos.y + y2
|
||||
|
@ -683,7 +516,6 @@ local function biome_check(biome_list, biome_goal)
|
|||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -698,8 +530,8 @@ local function get_water_spawn(p)
|
|||
end
|
||||
end
|
||||
|
||||
local function has_room(self,pos)
|
||||
local cb = self.collisionbox
|
||||
local function has_room(self, pos)
|
||||
local cb = self.spawnbox or self.collisionbox
|
||||
local nodes = {}
|
||||
if self.fly_in then
|
||||
local t = type(self.fly_in)
|
||||
|
@ -710,18 +542,74 @@ local function has_room(self,pos)
|
|||
end
|
||||
end
|
||||
table.insert(nodes,"air")
|
||||
local x = cb[4] - cb[1]
|
||||
local y = cb[5] - cb[2]
|
||||
local z = cb[6] - cb[3]
|
||||
local r = math.ceil(x * y * z)
|
||||
local p1 = vector.offset(pos,cb[1],cb[2],cb[3])
|
||||
local p2 = vector.offset(pos,cb[4],cb[5],cb[6])
|
||||
local n = #minetest.find_nodes_in_area(p1,p2,nodes) or 0
|
||||
if r > n then
|
||||
minetest.log("warning","[mcl_mobs] No room for mob "..self.name.." at "..minetest.pos_to_string(vector.round(pos)))
|
||||
return false
|
||||
|
||||
-- Calculate area to check for room
|
||||
local cb_height = cb[5] - cb[2]
|
||||
local p1 = vector.new(
|
||||
math.round(pos.x + cb[1]),
|
||||
math.floor(pos.y),
|
||||
math.round(pos.z + cb[3]))
|
||||
local p2 = vector.new(
|
||||
math.round(pos.x + cb[4]),
|
||||
math.ceil(p1.y + cb_height) - 1,
|
||||
math.round(pos.z + cb[6]))
|
||||
|
||||
-- Check if the entire spawn volume is free
|
||||
local dx = p2.x - p1.x + 1
|
||||
local dy = p2.y - p1.y + 1
|
||||
local dz = p2.z - p1.z + 1
|
||||
local found_nodes = minetest.find_nodes_in_area(p1,p2,nodes) or 0
|
||||
local n = #found_nodes
|
||||
if n == dx * dy * dz then
|
||||
return true
|
||||
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
|
||||
|
||||
mcl_mobs.custom_biomecheck = nil
|
||||
|
@ -761,15 +649,15 @@ local function spawn_check(pos, spawn_def)
|
|||
-- do not spawn ground mobs on leaves
|
||||
if spawn_def.type_of_spawning == "ground" and (not node_def.groups.solid or node_def.groups.leaves) then return end
|
||||
-- water mobs only on water
|
||||
if spawn_def.type_of_spawning == "water" and node_def.groups.water then return end
|
||||
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 node_def.groups.lava then return end
|
||||
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 node_def.groups.grass_block then return end
|
||||
if is_farm_animal(spawn_def.name) and not node_def.groups.grass_block then return end
|
||||
|
||||
---- More expensive calls:
|
||||
-- check the biome
|
||||
if not biome_check(spawn_def.biomes, get_biome_name(pos)) then return end
|
||||
if spawn_def.biomes and not biome_check(spawn_def.biomes, get_biome_name(pos)) then return end
|
||||
-- check if there is enough room
|
||||
local mob_def = minetest.registered_entities[spawn_def.name]
|
||||
if not has_room(mob_def,pos) then return end
|
||||
|
@ -805,19 +693,17 @@ local function spawn_check(pos, spawn_def)
|
|||
return false
|
||||
end
|
||||
-- passive threshold is apparently the same in all dimensions ...
|
||||
return gotten_light < overworld_passive_threshold
|
||||
return gotten_light > overworld_passive_threshold
|
||||
end
|
||||
|
||||
function mcl_mobs.spawn(pos,id)
|
||||
if not pos or not id then return false end
|
||||
local def = minetest.registered_entities[id] or minetest.registered_entities["mobs_mc:"..id] or minetest.registered_entities["extra_mobs:"..id]
|
||||
if not def or (def.can_spawn and not def.can_spawn(pos)) or not def.is_mob then
|
||||
return false
|
||||
end
|
||||
dbg_spawn_counts[def.name] = (dbg_spawn_counts[def.name] or 0) + 1
|
||||
if not def or not def.is_mob or (def.can_spawn and not def.can_spawn(pos)) then return false end
|
||||
if not has_room(def, pos) then return false end
|
||||
return minetest.add_entity(pos, def.name)
|
||||
end
|
||||
|
||||
|
||||
local function spawn_group(p,mob,spawn_on,amount_to_spawn)
|
||||
local nn= minetest.find_nodes_in_area_under_air(vector.offset(p,-5,-3,-5),vector.offset(p,5,3,5),spawn_on)
|
||||
local o
|
||||
|
@ -1211,7 +1097,6 @@ end
|
|||
minetest.register_chatcommand("mobstats",{
|
||||
privs = { debug = true },
|
||||
func = function(n,param)
|
||||
--minetest.chat_send_player(n,dump(dbg_spawn_counts))
|
||||
local pos = minetest.get_player_by_name(n):get_pos()
|
||||
minetest.chat_send_player(n,"mobs: within 32 radius of player/total loaded :"..count_mobs(pos,MOB_CAP_INNER_RADIUS) .. "/" .. count_mobs_total())
|
||||
minetest.chat_send_player(n,"spawning attempts since server start:" .. dbg_spawn_succ .. "/" .. dbg_spawn_attempts)
|
||||
|
|
|
@ -72,18 +72,24 @@ local axolotl = {
|
|||
fly = true,
|
||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
||||
breathes_in_water = true,
|
||||
jump = true,
|
||||
jump = false, -- would get them out of the water too often
|
||||
damage = 2,
|
||||
reach = 2,
|
||||
attack_type = "dogfight",
|
||||
attack_animals = true,
|
||||
specific_attack = {
|
||||
"extra_mobs_cod",
|
||||
"extra_mobs_glow_squid",
|
||||
"extra_mobs_salmon",
|
||||
"extra_mobs_tropical_fish",
|
||||
"mobs_mc_squid"
|
||||
},
|
||||
"mobs_mc:cod",
|
||||
"mobs_mc:glow_squid",
|
||||
"mobs_mc:salmon",
|
||||
"mobs_mc:tropical_fish",
|
||||
"mobs_mc:squid",
|
||||
"mobs_mc:zombie", -- todo: only drowned?
|
||||
"mobs_mc:baby_zombie",
|
||||
"mobs_mc:husk",
|
||||
"mobs_mc:baby_husk",
|
||||
"mobs_mc:guardian_elder",
|
||||
"mobs_mc:guardian",
|
||||
},
|
||||
runaway = true,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name = mobs_mc
|
||||
author = maikerumine
|
||||
description = Adds Minecraft-like monsters and animals.
|
||||
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core, mcl_util
|
||||
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
|
||||
|
|
|
@ -103,6 +103,7 @@ local skeleton = {
|
|||
return true
|
||||
end,
|
||||
ignited_by_sunlight = true,
|
||||
floats = 0,
|
||||
view_range = 16,
|
||||
fear_height = 4,
|
||||
attack_type = "dogshoot",
|
||||
|
|
|
@ -94,6 +94,7 @@ mcl_mobs.register_mob("mobs_mc:witherskeleton", {
|
|||
dogshoot_switch = 1,
|
||||
dogshoot_count_max =0.5,
|
||||
fear_height = 4,
|
||||
floats = 0,
|
||||
harmed_by_heal = true,
|
||||
fire_resistant = true,
|
||||
dealt_effect = {
|
||||
|
|
|
@ -53,12 +53,14 @@ local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed)
|
|||
eject_speed = eject_speed * 0.5
|
||||
end
|
||||
end
|
||||
local mob = minetest.add_entity(newpos, child_mob)
|
||||
if not mother_stuck then
|
||||
mob:set_velocity(dir * eject_speed)
|
||||
local mob = mcl_mobs.spawn(newpos, child_mob)
|
||||
if mob then
|
||||
if not mother_stuck then
|
||||
mob:set_velocity(dir * eject_speed)
|
||||
end
|
||||
mob:set_yaw(angle - math.pi/2)
|
||||
table.insert(children, mob)
|
||||
end
|
||||
mob:set_yaw(angle - math.pi/2)
|
||||
table.insert(children, mob)
|
||||
angle = angle + (math.pi*2) / spawn_count
|
||||
end
|
||||
-- If mother was murdered, children attack the killer after 1 second
|
||||
|
|
|
@ -67,6 +67,7 @@ local spider = {
|
|||
curiosity = 10,
|
||||
head_yaw="z",
|
||||
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.89, 0.7},
|
||||
spawnbox = {-1.2, -0.01, -1.2, 1.2, 0.89, 1.2},
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_spider.b3d",
|
||||
textures = {
|
||||
|
|
|
@ -818,7 +818,7 @@ local function find_closest_bed (self)
|
|||
|
||||
if (owned_by and owned_by == self._id) then
|
||||
mcl_log("Clear as already owned by me.")
|
||||
bed_meta:set_string("villager", nil)
|
||||
bed_meta:set_string("villager", "")
|
||||
owned_by = nil
|
||||
end
|
||||
|
||||
|
@ -1049,14 +1049,18 @@ local function has_summon_participants(self)
|
|||
end
|
||||
|
||||
local function summon_golem(self)
|
||||
vector.offset(self.object:get_pos(),-10,-10,-10)
|
||||
local nn = minetest.find_nodes_in_area_under_air(vector.offset(self.object:get_pos(),-10,-10,-10),vector.offset(self.object:get_pos(),10,10,10),{"group:solid","group:water"})
|
||||
table.shuffle(nn)
|
||||
for _,n in pairs(nn) do
|
||||
local up = minetest.find_nodes_in_area(vector.offset(n,0,1,0),vector.offset(n,0,3,0),{"air"})
|
||||
if up and #up >= 3 then
|
||||
local pos = self.object:get_pos()
|
||||
local p1 = vector.offset(pos, -10, -10, -10)
|
||||
local p2 = vector.offset(pos, 10, 10, 10)
|
||||
local nn = minetest.find_nodes_in_area_under_air(p1, p2,{"group:solid","group:water"})
|
||||
while #nn > 0 do
|
||||
local n = table.remove_random_element(nn)
|
||||
n.y = n.y + 1
|
||||
|
||||
local summon = mcl_mobs.spawn(n, "mobs_mc:iron_golem")
|
||||
if summon then
|
||||
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
|
||||
|
@ -1279,7 +1283,7 @@ local function validate_jobsite(self)
|
|||
mcl_log("Jobsite far, so resettle: " .. tostring(resettle))
|
||||
if resettle then
|
||||
local m = minetest.get_meta(self._jobsite)
|
||||
m:set_string("villager", nil)
|
||||
m:set_string("villager", "")
|
||||
remove_job (self)
|
||||
return false
|
||||
end
|
||||
|
@ -1421,7 +1425,7 @@ local function validate_bed(self)
|
|||
mcl_log("Bed far, so resettle: " .. tostring(resettle))
|
||||
if resettle then
|
||||
mcl_log("Resettled. Ditch bed.")
|
||||
m:set_string("villager", nil)
|
||||
m:set_string("villager", "")
|
||||
self._bed = nil
|
||||
bed_valid = false
|
||||
return false
|
||||
|
@ -1431,7 +1435,7 @@ local function validate_bed(self)
|
|||
mcl_log("Player owner: " .. owned_by_player)
|
||||
if owned_by_player ~= "" then
|
||||
mcl_log("Player owns this. Villager won't take this.")
|
||||
m:set_string("villager", nil)
|
||||
m:set_string("villager", "")
|
||||
self._bed = nil
|
||||
bed_valid = false
|
||||
return false
|
||||
|
@ -2300,13 +2304,13 @@ mcl_mobs.register_mob("mobs_mc:villager", {
|
|||
local bed = self._bed
|
||||
if bed then
|
||||
local bed_meta = minetest.get_meta(bed)
|
||||
bed_meta:set_string("villager", nil)
|
||||
bed_meta:set_string("villager", "")
|
||||
mcl_log("Died, so bye bye bed")
|
||||
end
|
||||
local jobsite = self._jobsite
|
||||
if jobsite then
|
||||
local jobsite_meta = minetest.get_meta(jobsite)
|
||||
jobsite_meta:set_string("villager", nil)
|
||||
jobsite_meta:set_string("villager", "")
|
||||
mcl_log("Died, so bye bye jobsite")
|
||||
end
|
||||
|
||||
|
|
|
@ -55,14 +55,16 @@ mcl_mobs.register_mob("mobs_mc:evoker", {
|
|||
basepos.y = basepos.y + 1
|
||||
for i=1, r do
|
||||
local spawnpos = vector.add(basepos, minetest.yaw_to_dir(pr:next(0,360)))
|
||||
local vex = minetest.add_entity(spawnpos, "mobs_mc:vex")
|
||||
local ent = vex:get_luaentity()
|
||||
local vex = mcl_mobs.spawn(spawnpos, "mobs_mc:vex")
|
||||
if vex then
|
||||
local ent = vex:get_luaentity()
|
||||
|
||||
-- Mark vexes as summoned and start their life clock (they take damage it reaches 0)
|
||||
ent._summoned = true
|
||||
ent._lifetimer = pr:next(33, 108)
|
||||
-- Mark vexes as summoned and start their life clock (they take damage it reaches 0)
|
||||
ent._summoned = true
|
||||
ent._lifetimer = pr:next(33, 108)
|
||||
|
||||
table.insert(spawned_vexes[self],ent)
|
||||
table.insert(spawned_vexes[self],ent)
|
||||
end
|
||||
end
|
||||
end,
|
||||
passive = false,
|
||||
|
|
|
@ -134,6 +134,7 @@ mcl_mobs.register_mob("mobs_mc:villager_zombie", {
|
|||
end,
|
||||
sunlight_damage = 2,
|
||||
ignited_by_sunlight = true,
|
||||
floats = 0,
|
||||
view_range = 16,
|
||||
fear_height = 4,
|
||||
harmed_by_heal = true,
|
||||
|
|
|
@ -96,6 +96,7 @@ local zombie = {
|
|||
},
|
||||
ignited_by_sunlight = true,
|
||||
sunlight_damage = 2,
|
||||
floats = 0,
|
||||
view_range = 16,
|
||||
attack_type = "dogfight",
|
||||
harmed_by_heal = true,
|
||||
|
|
|
@ -19,7 +19,6 @@ local set_node = minetest.set_node
|
|||
local sound_play = minetest.sound_play
|
||||
local add_particlespawner = minetest.add_particlespawner
|
||||
local after = minetest.after
|
||||
local add_entity = minetest.add_entity
|
||||
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||
local get_item_group = minetest.get_item_group
|
||||
|
||||
|
@ -165,7 +164,7 @@ function lightning.strike_func(pos, pos2, objects)
|
|||
|
||||
-- Events caused by the lightning strike: Fire, damage, mob transformations, rare skeleton spawn
|
||||
|
||||
pos2.y = pos2.y + 1/2
|
||||
pos2.y = pos2.y + 1
|
||||
local skeleton_lightning = false
|
||||
if rng:next(1,100) <= 3 then
|
||||
skeleton_lightning = true
|
||||
|
@ -174,14 +173,14 @@ function lightning.strike_func(pos, pos2, objects)
|
|||
if get_node(pos2).name == "air" then
|
||||
-- Low chance for a lightning to spawn skeleton horse + skeletons
|
||||
if skeleton_lightning then
|
||||
add_entity(pos2, "mobs_mc:skeleton_horse")
|
||||
mcl_mobs.spawn(pos2, "mobs_mc:skeleton_horse")
|
||||
|
||||
local angle, posadd
|
||||
angle = math.random() * math.pi * 2
|
||||
for i=1,3 do
|
||||
posadd = { x=math.cos(angle),y=0,z=math.sin(angle) }
|
||||
posadd = vector.normalize(posadd)
|
||||
local mob = add_entity(vector.add(pos2, posadd), "mobs_mc:skeleton")
|
||||
local mob = mcl_mobs.spawn(vector.add(pos2, posadd), "mobs_mc:skeleton")
|
||||
if mob then
|
||||
mob:set_yaw(angle-math.pi/2)
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mcl_void_damage
|
||||
author = Wuzzy
|
||||
description = Deal damage to entities stuck in the deep void
|
||||
depends = mcl_worlds
|
||||
depends = mcl_worlds, mcl_spawn
|
||||
|
|
|
@ -60,13 +60,15 @@ end
|
|||
-- set skybox based on time (uses skycolor api)
|
||||
function mcl_weather.rain.set_sky_box()
|
||||
if mcl_weather.state == "rain" then
|
||||
mcl_weather.skycolor.add_layer(
|
||||
"weather-pack-rain-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=98},
|
||||
{r=135, g=135, b=151},
|
||||
{r=85, g=86, b=98},
|
||||
{r=0, g=0, b=0}})
|
||||
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-rain-sky" then
|
||||
mcl_weather.skycolor.add_layer(
|
||||
"weather-pack-rain-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=98},
|
||||
{r=135, g=135, b=151},
|
||||
{r=85, g=86, b=98},
|
||||
{r=0, g=0, b=0}})
|
||||
end
|
||||
mcl_weather.skycolor.active = true
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
player:set_clouds({color="#5D5D5FE8"})
|
||||
|
@ -155,6 +157,7 @@ function mcl_weather.rain.clear()
|
|||
mcl_weather.rain.remove_sound(player)
|
||||
mcl_weather.rain.remove_player(player)
|
||||
mcl_weather.remove_spawners_player(player)
|
||||
player:set_clouds({color="#FFF0EF"})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
local NIGHT_VISION_RATIO = 0.45
|
||||
local DEBUG = false
|
||||
|
||||
-- Settings
|
||||
local minimum_update_interval = { 250e3 }
|
||||
|
@ -190,8 +191,8 @@ end
|
|||
|
||||
function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
|
||||
-- Clamp current_val to valid range
|
||||
current_val = math.min(minval, current_val)
|
||||
current_val = math.max(maxval, current_val)
|
||||
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
|
||||
|
@ -199,7 +200,7 @@ function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
|
|||
-- Get the first color's values
|
||||
local index1 = math.floor(scaled_value)
|
||||
local color1 = colors[index1]
|
||||
local frac1 = scaled_value - 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)
|
||||
|
@ -207,11 +208,32 @@ function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
|
|||
local color2 = colors[index2]
|
||||
|
||||
-- Interpolate between color1 and color2
|
||||
return {
|
||||
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
|
||||
|
|
|
@ -40,18 +40,21 @@ function dimension_handlers.overworld(player, sky_data)
|
|||
end
|
||||
|
||||
-- Use overworld defaults
|
||||
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.15)
|
||||
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,
|
||||
day_horizon = day_color,
|
||||
dawn_sky = dawn_color,
|
||||
dawn_horizon = dawn_color,
|
||||
night_sky = night_color,
|
||||
night_horizon = night_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,
|
||||
}
|
||||
|
@ -75,18 +78,15 @@ function dimension_handlers.overworld(player, sky_data)
|
|||
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)
|
||||
sky_data.sky = {
|
||||
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,
|
||||
}
|
||||
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}
|
||||
|
@ -164,7 +164,8 @@ function dimension_handlers.nether(player, sky_data)
|
|||
end
|
||||
|
||||
function dimension_handlers.void(player, sky_data)
|
||||
sky_data.sky = { type = "plain",
|
||||
sky_data.sky = {
|
||||
type = "plain",
|
||||
base_color = "#000000",
|
||||
clouds = false,
|
||||
}
|
||||
|
|
|
@ -75,13 +75,15 @@ function mcl_weather.has_snow(pos)
|
|||
end
|
||||
|
||||
function mcl_weather.snow.set_sky_box()
|
||||
mcl_weather.skycolor.add_layer(
|
||||
"weather-pack-snow-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=86},
|
||||
{r=135, g=135, b=135},
|
||||
{r=85, g=86, b=86},
|
||||
{r=0, g=0, b=0}})
|
||||
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-snow-sky" then
|
||||
mcl_weather.skycolor.add_layer(
|
||||
"weather-pack-snow-sky",
|
||||
{{r=0, g=0, b=0},
|
||||
{r=85, g=86, b=86},
|
||||
{r=135, g=135, b=135},
|
||||
{r=85, g=86, b=86},
|
||||
{r=0, g=0, b=0}})
|
||||
end
|
||||
mcl_weather.skycolor.active = true
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
player:set_clouds({color="#ADADADE8"})
|
||||
|
|
|
@ -23,13 +23,15 @@ minetest.register_globalstep(function(dtime)
|
|||
mcl_weather.rain.make_weather()
|
||||
|
||||
if mcl_weather.thunder.init_done == false then
|
||||
mcl_weather.skycolor.add_layer("weather-pack-thunder-sky", {
|
||||
{r=0, g=0, b=0},
|
||||
{r=40, g=40, b=40},
|
||||
{r=85, g=86, b=86},
|
||||
{r=40, g=40, b=40},
|
||||
{r=0, g=0, b=0},
|
||||
})
|
||||
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-thunder-sky" then
|
||||
mcl_weather.skycolor.add_layer("weather-pack-thunder-sky", {
|
||||
{r=0, g=0, b=0},
|
||||
{r=40, g=40, b=40},
|
||||
{r=85, g=86, b=86},
|
||||
{r=40, g=40, b=40},
|
||||
{r=0, g=0, b=0},
|
||||
})
|
||||
end
|
||||
mcl_weather.skycolor.active = true
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
player:set_clouds({color="#3D3D3FE8"})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mcl_death_messages
|
||||
author = 4Evergreen4
|
||||
description = Shows messages in chat when a player dies.
|
||||
depends = mcl_colors
|
||||
depends = mcl_colors, mcl_damage
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
## 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)
|
||||
Debug field defintion example:
|
||||
|
||||
```
|
||||
{
|
||||
level = 3,
|
||||
--show with debug level 3 and upwards
|
||||
|
@ -13,6 +15,7 @@ Debug field defintion example:
|
|||
-- It should output a string and determines
|
||||
-- the content of the debug field.
|
||||
}
|
||||
```
|
||||
|
||||
### mcl_info.registered_debug_fields
|
||||
Table the debug definitions are stored in. Do not modify this directly. If you need to overwrite a field just set it again with mcl_info.register_debug_field().
|
||||
|
|
|
@ -32,4 +32,5 @@ mcl_inventory.register_survival_inventory_tab({
|
|||
-- Returns true by default
|
||||
access = function(player)
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
|
|
@ -8,7 +8,7 @@ Show a hud message of `type` to player `player` with `data` as params.
|
|||
|
||||
The element will stay for the per-player param `stay` or `data.stay` (in gametick which is 1/20 second).
|
||||
|
||||
Here is a usage exemple:
|
||||
Here is a usage example:
|
||||
|
||||
```lua
|
||||
--show a title in the HUD with minecraft color "gold"
|
||||
|
@ -35,7 +35,7 @@ Basicaly run `mcl_title.remove(player, type)` for every type.
|
|||
|
||||
## mcl_title.params_set(player, params)
|
||||
|
||||
Allow mods to set `stay` and upcomming `fadeIn`/`fadeOut` params.
|
||||
Allow mods to set `stay` and upcoming `fadeIn`/`fadeOut` params.
|
||||
|
||||
```lua
|
||||
mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field will stay during 30s (600/20)
|
||||
|
@ -43,8 +43,8 @@ mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field
|
|||
|
||||
## mcl_title.params_get(player)
|
||||
|
||||
Get `stay` and 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
|
||||
mcl_title.params_get(player)
|
||||
```
|
||||
```
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mcl_title
|
||||
description = Add an API to add in HUD title
|
||||
depends = mcl_colors
|
||||
description = Add an API to add in HUD title
|
||||
depends = mcl_colors, mcl_util
|
||||
author = AFCMS
|
|
@ -1,3 +1,3 @@
|
|||
name = mesecons_button
|
||||
depends = mesecons
|
||||
depends = mesecons, mesecons_mvps
|
||||
optional_depends = doc
|
||||
|
|
|
@ -119,7 +119,7 @@ mcl_armor.register_set({
|
|||
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
|
||||
craft_material = "mcl_mobitems:leather",
|
||||
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
|
||||
|
||||
-- CONSTS
|
||||
local DOUBLE_DROP_CHANCE = 8
|
||||
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
|
||||
-- "BAMBOO" goes here.
|
||||
local BAMBOO = "mcl_bamboo:bamboo"
|
||||
local BAMBOO_ENDCAP_NAME = "mcl_bamboo:bamboo_endcap"
|
||||
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
||||
|
@ -16,7 +13,7 @@ local BAMBOO_PLANK = BAMBOO .. "_plank"
|
|||
local modname = minetest.get_current_modname()
|
||||
local S = minetest.get_translator(modname)
|
||||
local node_sound = mcl_sounds.node_sound_wood_defaults()
|
||||
local pr = PseudoRandom((os.time() + 15766) * 12) -- switched from math.random() to PseudoRandom because the random wasn't very random.
|
||||
local pr = PseudoRandom((os.time() + 15766) * 12)
|
||||
|
||||
local on_rotate
|
||||
if minetest.get_modpath("screwdriver") then
|
||||
|
@ -31,33 +28,7 @@ local bamboo_def = {
|
|||
paramtype = "light",
|
||||
groups = {handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3},
|
||||
sounds = node_sound,
|
||||
|
||||
drop = {
|
||||
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},
|
||||
},
|
||||
},
|
||||
},
|
||||
drop = BAMBOO,
|
||||
|
||||
inventory_image = "mcl_bamboo_bamboo_shoot.png",
|
||||
wield_image = "mcl_bamboo_bamboo_shoot.png",
|
||||
|
@ -86,7 +57,6 @@ local bamboo_def = {
|
|||
on_rotate = on_rotate,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
|
||||
if not pointed_thing then
|
||||
return itemstack
|
||||
end
|
||||
|
@ -241,9 +211,6 @@ local bamboo_def = {
|
|||
if node_above and ((bamboo_node and bamboo_node > 0) or node_above.name == BAMBOO_ENDCAP_NAME) then
|
||||
minetest.remove_node(new_pos)
|
||||
minetest.sound_play(node_sound.dug, sound_params, true)
|
||||
if pr:next(1, DOUBLE_DROP_CHANCE) == 1 then
|
||||
minetest.add_item(new_pos, istack)
|
||||
end
|
||||
minetest.add_item(new_pos, istack)
|
||||
end
|
||||
end,
|
||||
|
|
|
@ -9,8 +9,6 @@ local SIDE_SCAFFOLDING = false
|
|||
local SIDE_SCAFFOLD_NAME = "mcl_bamboo:scaffolding_horizontal"
|
||||
-- ---------------------------------------------------------------------------
|
||||
local SCAFFOLDING_NAME = "mcl_bamboo:scaffolding"
|
||||
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
|
||||
-- "BAMBOO" goes here.
|
||||
local BAMBOO = "mcl_bamboo:bamboo"
|
||||
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ function mcl_bamboo.break_orphaned(pos)
|
|||
local node_name = node_below.name
|
||||
|
||||
-- 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
|
||||
end
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
-- LOCALS
|
||||
local modname = minetest.get_current_modname()
|
||||
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
|
||||
-- "BAMBOO" goes here.
|
||||
local BAMBOO = "mcl_bamboo:bamboo"
|
||||
|
||||
mcl_bamboo = {}
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
--- These are all of the fuel recipes and all of the crafting recipes, consolidated into one place.
|
||||
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
|
||||
|
||||
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
|
||||
-- "BAMBOO" goes here.
|
||||
local BAMBOO = "mcl_bamboo:bamboo"
|
||||
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
||||
-- Craftings
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
# mcl_buckets
|
||||
Add an API to register buckets to mcl
|
||||
Adds an API to register buckets to VL
|
||||
|
||||
## mcl_buckets.register_liquid(def)
|
||||
|
||||
Register a new liquid
|
||||
Accept folowing params:
|
||||
* source_place: a string or function.
|
||||
* string: name of the node to place
|
||||
* function(pos): will returns name of the node to place with pos being the placement position
|
||||
* source_take: table of liquid source node names to take
|
||||
* bucketname: itemstring of the new bucket item
|
||||
* inventory_image: texture of the new bucket item (ignored if itemname == nil)
|
||||
* name: user-visible bucket description
|
||||
* longdesc: long explanatory description (for help)
|
||||
* usagehelp: short usage explanation (for help)
|
||||
* tt_help: very short tooltip help
|
||||
* extra_check(pos, placer): (optional) function(pos)
|
||||
* groups: optional list of item groups
|
||||
Register a new liquid.
|
||||
|
||||
Accepts the following parameters:
|
||||
|
||||
* `source_place`: a string or a function
|
||||
* `string`: name of the node to place
|
||||
* `function(pos)`: will return name of the node to place with pos being the placement position
|
||||
* `source_take`: table of liquid source node names to take
|
||||
* `bucketname`: itemstring of the new bucket item
|
||||
* `inventory_image`: texture of the new bucket item (ignored if itemname == nil)
|
||||
* `name`: user-visible bucket description
|
||||
* `longdesc`: long explanatory description (for help)
|
||||
* `usagehelp`: short usage explanation (for help)
|
||||
* `tt_help`: very short tooltip help
|
||||
* `extra_check(pos, placer)`: (optional) additional check before liquid placement (return 2 booleans: (1) whether to place the liquid source and (2) whether to empty the bucket)
|
||||
* `groups`: optional list of item groups
|
||||
|
||||
|
||||
**Usage exemple:**
|
||||
**Usage example:**
|
||||
|
||||
```lua
|
||||
mcl_buckets.register_liquid({
|
||||
bucketname = "dummy:bucket_dummy",
|
||||
|
@ -39,7 +42,7 @@ mcl_buckets.register_liquid({
|
|||
tt_help = S("Places a dummy liquid source"),
|
||||
extra_check = function(pos, placer)
|
||||
--pos = pos where the liquid should be placed
|
||||
--placer people who tried to place the bucket (can be nil)
|
||||
--placer who tried to place the bucket (can be nil)
|
||||
|
||||
--no liquid node will be placed
|
||||
--the bucket will not be emptied
|
||||
|
@ -51,4 +54,4 @@ mcl_buckets.register_liquid({
|
|||
end,
|
||||
groups = { dummy_group = 123 },
|
||||
})
|
||||
```
|
||||
```
|
||||
|
|
|
@ -39,9 +39,9 @@ local function drop_items(pos, node, oldmeta)
|
|||
if food_entity:get_luaentity().name == "mcl_campfires:food_entity" then
|
||||
food_entity:remove()
|
||||
for i = 1, 4 do
|
||||
meta:set_string("food_x_"..tostring(i), nil)
|
||||
meta:set_string("food_y_"..tostring(i), nil)
|
||||
meta:set_string("food_z_"..tostring(i), nil)
|
||||
meta:set_string("food_x_"..tostring(i), "")
|
||||
meta:set_string("food_y_"..tostring(i), "")
|
||||
meta:set_string("food_z_"..tostring(i), "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -135,9 +135,9 @@ function mcl_campfires.cook_item(pos, elapsed)
|
|||
if cooked then
|
||||
if food_entity then
|
||||
food_entity:remove() -- Remove visual food entity
|
||||
meta:set_string("food_x_"..tostring(i), nil)
|
||||
meta:set_string("food_y_"..tostring(i), nil)
|
||||
meta:set_string("food_z_"..tostring(i), nil)
|
||||
meta:set_string("food_x_"..tostring(i), "")
|
||||
meta:set_string("food_y_"..tostring(i), "")
|
||||
meta:set_string("food_z_"..tostring(i), "")
|
||||
minetest.add_item(pos, cooked.item) -- Drop Cooked Item
|
||||
-- Throw some Experience Points because why not?
|
||||
-- Food is cooked, xp is deserved for using this unique cooking method. Take that Minecraft ;)
|
||||
|
|
|
@ -5,9 +5,9 @@ animations are achieved by giving each chest node an entity, as Minetest (as of
|
|||
5.8.1) doesn't support giving nodes animated meshes, only static ones.
|
||||
|
||||
Because of that, a lot of parameters passed through the exposed functions are
|
||||
be related to nodes and entities.
|
||||
related to nodes and entities.
|
||||
|
||||
Please refer to [Minetest documentation](http://api.minetest.net/) and the code
|
||||
Please refer to the [Minetest documentation](http://api.minetest.net/) and code
|
||||
comments in `api.lua`.
|
||||
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ minetest.register_lbm({
|
|||
local node_name = node.name
|
||||
node.name = node_name .. "_small"
|
||||
minetest.swap_node(pos, node)
|
||||
select_and_spawn_entity(pos, node)
|
||||
mcl_chests.select_and_spawn_entity(pos, node)
|
||||
if node_name == "mcl_chests:trapped_chest_on" then
|
||||
minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " .. minetest.pos_to_string(pos))
|
||||
mcl_chests.chest_update_after_close(pos)
|
||||
|
|
|
@ -161,7 +161,7 @@ minetest.register_abm({
|
|||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
liquid_flow_action(pos, "water", function(pos)
|
||||
drop_attached_node(pos)
|
||||
minetest.dig_node(pos)
|
||||
minetest.remove_node(pos)
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
@ -217,7 +217,8 @@ minetest.register_abm({
|
|||
while true do
|
||||
local node = minetest.get_node(lpos)
|
||||
if not node or node.name ~= "mcl_core:cactus" then break end
|
||||
minetest.dig_node(lpos)
|
||||
-- minetest.dig_node ignores protected nodes and causes infinite drop (#4628)
|
||||
minetest.remove_node(lpos)
|
||||
dx = dx or ((math.random(0,1)-0.5) * math.sqrt(math.random())) * 1.5
|
||||
dy = dy or ((math.random(0,1)-0.5) * math.sqrt(math.random())) * 1.5
|
||||
local obj = minetest.add_item(vector.offset(lpos, dx, 0.25, dy), "mcl_core:cactus")
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
name = mcl_crimson
|
||||
author = debiankaios, Exhale
|
||||
depends = mcl_core, mcl_stairs, mobs_mc, mcl_util, mcl_dye, mcl_flowerpots
|
||||
depends = mcl_core, mcl_fences, mcl_stairs, mobs_mc, mcl_util, mcl_dye, mcl_flowerpots, mcl_doors
|
||||
|
|
|
@ -111,7 +111,7 @@ for _, p in pairs(deepslate_ores) do
|
|||
end
|
||||
|
||||
if copper_mod then
|
||||
register_deepslate_ore("Copper", "mcl_copper:raw_copper", "mcl_copper:copper_ingot", 4, 4)
|
||||
register_deepslate_ore("Copper", "mcl_copper:raw_copper", "mcl_copper:copper_ingot", 3, 4)
|
||||
end
|
||||
|
||||
local redstone_timer = 68.28
|
||||
|
|
|
@ -25,7 +25,7 @@ end
|
|||
|
||||
-- Registers a door
|
||||
-- name: The name of the door
|
||||
-- def: a table with the folowing fields:
|
||||
-- def: a table with the following fields:
|
||||
-- description
|
||||
-- inventory_image
|
||||
-- groups
|
||||
|
|
|
@ -283,7 +283,7 @@ local function apply_bone_meal(pointed_thing, user)
|
|||
if n.name == "mcl_farming:sweet_berry_bush_3" then
|
||||
return minetest.add_item(vector.offset(pos,math.random()-0.5,math.random()-0.5,math.random()-0.5),"mcl_farming:sweet_berry")
|
||||
else
|
||||
return mcl_farming:grow_plant("plant_sweet_berry_bush", pos, n, 0, true)
|
||||
return mcl_farming:grow_plant("plant_sweet_berry_bush", pos, n, 1, true)
|
||||
end
|
||||
elseif n.name == "mcl_cocoas:cocoa_1" or n.name == "mcl_cocoas:cocoa_2" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
|
|
|
@ -691,7 +691,7 @@ mcl_enchanting.enchantments.soul_speed = {
|
|||
disallow = {non_combat_armor = true},
|
||||
incompatible = {frost_walker = true},
|
||||
weight = 2,
|
||||
description = S("Increases walking speed on soul sand."),
|
||||
description = S("Increases walking speed on soul sand and soul soil."),
|
||||
curse = false,
|
||||
on_enchant = function() end,
|
||||
requires_tool = false,
|
||||
|
|
|
@ -68,7 +68,7 @@ Mined blocks drop themselves.=Abgebaute Blöcke werfen sich selbst ab.
|
|||
Smite=Qual
|
||||
Increases damage to undead mobs.=Erhöht Schaden für untote Mobs.
|
||||
Soul Speed=Schnelle Seele
|
||||
Increases walking speed on soul sand.=Erhöht Gehgeschwindigkeit auf Seelensand.
|
||||
Increases walking speed on soul sand and soul soil.=Erhöht Gehgeschwindigkeit auf Seelensand.
|
||||
Sweeping Edge=Schwungklinge
|
||||
Increases sweeping attack damage.=Erhöht Schwungangriffsschaden.
|
||||
Thorns=Dornen
|
||||
|
|
|
@ -36,7 +36,7 @@ Increases mob loot.=Incrementa el botín de los enemigos.
|
|||
Increases rate of good loot (enchanting books, etc.)=Incrementa la probabilidad de encontrar tesoros.
|
||||
Increases sweeping attack damage.=Incrementa el daño de efecto area.
|
||||
Increases underwater movement speed.=Incrementa la velocidad de nado bajo el agua.
|
||||
Increases walking speed on soul sand.=Incrementa la velocidad al caminar sobre arena de Almas.
|
||||
Increases walking speed on soul sand and soul soil.=Incrementa la velocidad al caminar sobre arena de Almas.
|
||||
Infinity=Infinidad
|
||||
Item destroyed on death.=El objeto se destruye tras tu muerte.
|
||||
Knockback=Empuje
|
||||
|
|
|
@ -36,7 +36,7 @@ Increases mob loot.=Augmente le butin des mobs.
|
|||
Increases rate of good loot (enchanting books, etc.)=Augmente le taux de bon butin (livres enchanteurs, etc.)
|
||||
Increases sweeping attack damage.=Augmente les dégâts de l'épée
|
||||
Increases underwater movement speed.=Augmente la vitesse de déplacement sous l'eau.
|
||||
Increases walking speed on soul sand.=Augmente la vitesse de marche sur le sable des âmes.
|
||||
Increases walking speed on soul sand and soul soil.=Augmente la vitesse de marche sur le sable des âmes.
|
||||
Infinity=Infinité
|
||||
Item destroyed on death.=Objet détruit à la mort.
|
||||
Knockback=Recul
|
||||
|
|
|
@ -36,7 +36,7 @@ Increases mob loot.=MOBの戦利品が増加します。
|
|||
Increases rate of good loot (enchanting books, etc.)=釣果の質が良くなります(エンチャントの本など)。
|
||||
Increases sweeping attack damage.=なぎ払い攻撃のダメージが増加します。
|
||||
Increases underwater movement speed.=水中での横移動速度が増加します。
|
||||
Increases walking speed on soul sand.=ソウルサンドとソウルソイルの上を歩く速度が増加します。
|
||||
Increases walking speed on soul sand and soul soil.=ソウルサンドとソウルソイルの上を歩く速度が増加します。
|
||||
Infinity=無限
|
||||
Item destroyed on death.=死亡時にアイテムが消滅します。
|
||||
Knockback=ノックバック
|
||||
|
|
|
@ -36,7 +36,7 @@ Increases mob loot.=
|
|||
Increases rate of good loot (enchanting books, etc.)=
|
||||
Increases sweeping attack damage.=
|
||||
Increases underwater movement speed.=
|
||||
Increases walking speed on soul sand.=
|
||||
Increases walking speed on soul sand and soul soil.=
|
||||
Infinity=
|
||||
Item destroyed on death.=
|
||||
Knockback=
|
||||
|
|
|
@ -1,81 +1,79 @@
|
|||
-- Possible future improvements:
|
||||
-- * rewrite to use node timers instead of ABMs, but needs benchmarking
|
||||
-- * redesign the catch-up logic
|
||||
-- * switch to exponentially-weighted moving average for light instead using a single variable to conserve IO
|
||||
--
|
||||
local math = math
|
||||
local tostring = tostring
|
||||
|
||||
mcl_farming.plant_lists = {}
|
||||
local vector = vector
|
||||
|
||||
local plant_lists = {}
|
||||
|
||||
mcl_farming.plant_lists = plant_lists -- export
|
||||
local plant_nodename_to_id_list = {}
|
||||
|
||||
local function get_intervals_counter(pos, interval, chance)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local time_speed = tonumber(minetest.settings:get("time_speed") or 72)
|
||||
local current_game_time
|
||||
if time_speed == nil then
|
||||
return 1
|
||||
end
|
||||
if (time_speed < 0.1) then
|
||||
return 1
|
||||
end
|
||||
local time_multiplier = 86400 / time_speed
|
||||
current_game_time = .0 + ((minetest.get_day_count() + minetest.get_timeofday()) * time_multiplier)
|
||||
local time_speed = tonumber(minetest.settings:get("time_speed")) or 72
|
||||
local time_multiplier = time_speed > 0 and (86400 / time_speed) or 0
|
||||
|
||||
local function get_intervals_counter(pos, interval, chance)
|
||||
if time_multiplier == 0 then return 0 end
|
||||
-- "wall clock time", so plants continue to grow while sleeping
|
||||
local current_game_time = (minetest.get_day_count() + minetest.get_timeofday()) * time_multiplier
|
||||
local approx_interval = math.max(interval, 1) * math.max(chance, 1)
|
||||
|
||||
local last_game_time = meta:get_string("last_gametime")
|
||||
if last_game_time then
|
||||
last_game_time = tonumber(last_game_time)
|
||||
end
|
||||
if not last_game_time or last_game_time < 1 then
|
||||
last_game_time = current_game_time - approx_interval / 10
|
||||
local meta = minetest.get_meta(pos)
|
||||
local last_game_time = meta:get_float("last_gametime")
|
||||
if last_game_time < 1 then
|
||||
last_game_time = current_game_time - approx_interval * 0.5
|
||||
elseif last_game_time == current_game_time then
|
||||
current_game_time = current_game_time + approx_interval
|
||||
end
|
||||
|
||||
local elapsed_game_time = .0 + current_game_time - last_game_time
|
||||
|
||||
meta:set_string("last_gametime", tostring(current_game_time))
|
||||
|
||||
return elapsed_game_time / approx_interval
|
||||
meta:set_float("last_gametime", current_game_time)
|
||||
return (current_game_time - last_game_time) / approx_interval
|
||||
end
|
||||
|
||||
local function get_avg_light_level(pos)
|
||||
local node_light = tonumber(minetest.get_node_light(pos) or 0)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local counter = meta:get_int("avg_light_count")
|
||||
-- EWMA would use a single variable:
|
||||
-- local avg = meta:get_float("avg_light")
|
||||
-- avg = avg + (node_light - avg) * 0.985
|
||||
-- meta.set_float("avg_light", avg)
|
||||
local summary = meta:get_int("avg_light_summary")
|
||||
local counter = meta:get_int("avg_light_count")
|
||||
if counter > 99 then
|
||||
counter = 51
|
||||
summary = math.ceil((summary + 0.0) / 2.0)
|
||||
else
|
||||
counter = counter + 1
|
||||
summary, counter = math.ceil(summary * 0.5), 50
|
||||
end
|
||||
summary = summary + node_light
|
||||
meta:set_int("avg_light_count", counter)
|
||||
meta:set_int("avg_light_summary", summary)
|
||||
return math.ceil((summary + 0.0) / counter)
|
||||
local node_light = minetest.get_node_light(pos)
|
||||
if node_light ~= nil then
|
||||
summary, counter = summary + node_light, counter + 1
|
||||
meta:set_int("avg_light_summary", summary)
|
||||
meta:set_int("avg_light_count", counter)
|
||||
end
|
||||
return math.ceil(summary / counter)
|
||||
end
|
||||
|
||||
function mcl_farming:add_plant(identifier, full_grown, names, interval, chance)
|
||||
mcl_farming.plant_lists[identifier] = {}
|
||||
mcl_farming.plant_lists[identifier].full_grown = full_grown
|
||||
mcl_farming.plant_lists[identifier].names = names
|
||||
mcl_farming.plant_lists[identifier].interval = interval
|
||||
mcl_farming.plant_lists[identifier].chance = chance
|
||||
plant_lists = mcl_farming.plant_lists --provide local copy of plant lists (performances)
|
||||
local plant_info = {}
|
||||
plant_info.full_grown = full_grown
|
||||
plant_info.names = names
|
||||
plant_info.interval = interval
|
||||
plant_info.chance = chance
|
||||
for _, nodename in pairs(names) do
|
||||
plant_nodename_to_id_list[nodename] = identifier
|
||||
end
|
||||
plant_info.step_from_name = {}
|
||||
for i, name in ipairs(names) do
|
||||
plant_info.step_from_name[name] = i
|
||||
end
|
||||
plant_lists[identifier] = plant_info
|
||||
minetest.register_abm({
|
||||
label = string.format("Farming plant growth (%s)", identifier),
|
||||
nodenames = names,
|
||||
interval = interval,
|
||||
chance = chance,
|
||||
action = function(pos, node)
|
||||
local low_speed = minetest.get_node({ x = pos.x, y = pos.y - 1, z = pos.z }).name ~= "mcl_farming:soil_wet"
|
||||
mcl_farming:grow_plant(identifier, pos, node, false, false, low_speed)
|
||||
local low_speed = minetest.get_node(vector.offset(pos, 0, -1, 0)).name ~= "mcl_farming:soil_wet"
|
||||
mcl_farming:grow_plant(identifier, pos, node, 1, false, low_speed)
|
||||
end,
|
||||
})
|
||||
for _, nodename in pairs(names) do
|
||||
plant_nodename_to_id_list[nodename] = identifier
|
||||
end
|
||||
end
|
||||
|
||||
-- Attempts to advance a plant at pos by one or more growth stages (if possible)
|
||||
|
@ -84,56 +82,36 @@ end
|
|||
-- node: Node table
|
||||
-- stages: Number of stages to advance (optional, defaults to 1)
|
||||
-- ignore_light: if true, ignore light requirements for growing
|
||||
|
||||
-- low_speed: grow more slowly (not wet), default false
|
||||
-- Returns true if plant has been grown by 1 or more stages.
|
||||
-- Returns false if nothing changed.
|
||||
function mcl_farming:grow_plant(identifier, pos, node, stages, ignore_light, low_speed)
|
||||
local average_light_level = get_avg_light_level(pos)
|
||||
stages = stages or 1
|
||||
local plant_info = plant_lists[identifier]
|
||||
local intervals_counter = get_intervals_counter(pos, plant_info.interval, plant_info.chance)
|
||||
local low_speed = low_speed or false
|
||||
if low_speed then
|
||||
if intervals_counter < 1.01 and math.random(0, 9) > 0 then
|
||||
return
|
||||
else
|
||||
intervals_counter = intervals_counter / 10
|
||||
end
|
||||
if stages > 0 then intervals_counters = intervals_counter - 1 end
|
||||
if low_speed then -- 10% speed approximately
|
||||
if intervals_counter < 1.01 and math.random(0, 9) > 0 then return false end
|
||||
intervals_counter = intervals_counter / 10
|
||||
end
|
||||
if not minetest.get_node_light(pos) and not ignore_light and intervals_counter < 1.5 then
|
||||
return false
|
||||
end
|
||||
if minetest.get_node_light(pos) < 10 and not ignore_light and intervals_counter < 1.5 then
|
||||
return false
|
||||
if not ignore_light and intervals_counter < 1.5 then
|
||||
local light = minetest.get_node_light(pos)
|
||||
if not light or light < 10 then return false end
|
||||
end
|
||||
|
||||
if intervals_counter >= 1.5 then
|
||||
if average_light_level < 0.1 then
|
||||
return false
|
||||
end
|
||||
local average_light_level = get_avg_light_level(pos)
|
||||
if average_light_level < 0.1 then return false end
|
||||
if average_light_level < 10 then
|
||||
intervals_counter = intervals_counter * average_light_level / 10
|
||||
end
|
||||
end
|
||||
|
||||
local step = nil
|
||||
|
||||
for i, name in ipairs(plant_info.names) do
|
||||
if name == node.name then
|
||||
step = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if step == nil then
|
||||
return false
|
||||
end
|
||||
if not stages then
|
||||
stages = 1
|
||||
end
|
||||
stages = stages + math.ceil(intervals_counter)
|
||||
local new_node = { name = plant_info.names[step + stages] }
|
||||
if new_node.name == nil then
|
||||
new_node.name = plant_info.full_grown
|
||||
end
|
||||
local step = plant_info.step_from_name[node.name]
|
||||
if step == nil then return false end
|
||||
stages = stages + math.floor(intervals_counter)
|
||||
if stages == 0 then return false end
|
||||
local new_node = { name = plant_info.names[step + stages] or plant_info.full_grown }
|
||||
new_node.param = node.param
|
||||
new_node.param2 = node.param2
|
||||
minetest.set_node(pos, new_node)
|
||||
|
@ -142,12 +120,7 @@ end
|
|||
|
||||
function mcl_farming:place_seed(itemstack, placer, pointed_thing, plantname)
|
||||
local pt = pointed_thing
|
||||
if not pt then
|
||||
return
|
||||
end
|
||||
if pt.type ~= "node" then
|
||||
return
|
||||
end
|
||||
if not pt or pt.type ~= "node" then return end
|
||||
|
||||
-- Use pointed node's on_rightclick function first, if present
|
||||
local node = minetest.get_node(pt.under)
|
||||
|
@ -157,22 +130,13 @@ function mcl_farming:place_seed(itemstack, placer, pointed_thing, plantname)
|
|||
end
|
||||
end
|
||||
|
||||
local pos = { x = pt.above.x, y = pt.above.y - 1, z = pt.above.z }
|
||||
local farmland = minetest.get_node(pos)
|
||||
pos = { x = pt.above.x, y = pt.above.y, z = pt.above.z }
|
||||
local place_s = minetest.get_node(pos)
|
||||
if minetest.get_node(pt.above).name ~= "air" then return end
|
||||
local farmland = minetest.registered_nodes[minetest.get_node(vector.offset(pt.above, 0, -1, 0)).name]
|
||||
if not farmland or (farmland.groups.soil or 0) < 2 then return end
|
||||
minetest.sound_play(minetest.registered_nodes[plantname].sounds.place, { pos = pt.above }, true)
|
||||
minetest.add_node(pt.above, { name = plantname, param2 = minetest.registered_nodes[plantname].place_param2 })
|
||||
|
||||
if string.find(farmland.name, "mcl_farming:soil") and string.find(place_s.name, "air") then
|
||||
minetest.sound_play(minetest.registered_nodes[plantname].sounds.place, { pos = pos }, true)
|
||||
minetest.add_node(pos, { name = plantname, param2 = minetest.registered_nodes[plantname].place_param2 })
|
||||
--local intervals_counter = get_intervals_counter(pos, 1, 1)
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then itemstack:take_item() end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
|
@ -189,48 +153,41 @@ end
|
|||
- grow_interval: Will attempt to grow a gourd periodically at this interval in seconds
|
||||
- grow_chance: Chance of 1/grow_chance to grow a gourd next to the full unconnected stem after grow_interval has passed. Must be a natural number
|
||||
- connected_stem_texture: Texture of the connected stem
|
||||
- gourd_on_construct_extra: Custom on_construct extra function for the gourd. Will be called after the stem check code
|
||||
]]
|
||||
|
||||
function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, stem_itemstring, stem_def, stem_drop, gourd_itemstring, gourd_def, grow_interval, grow_chance, connected_stem_texture, gourd_on_construct_extra)
|
||||
|
||||
function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, stem_itemstring, stem_def, stem_drop, gourd_itemstring, gourd_def, grow_interval, grow_chance, connected_stem_texture)
|
||||
local connected_stem_names = {
|
||||
connected_stem_basename .. "_r",
|
||||
connected_stem_basename .. "_l",
|
||||
connected_stem_basename .. "_t",
|
||||
connected_stem_basename .. "_b",
|
||||
}
|
||||
|
||||
local neighbors = {
|
||||
{ x = -1, y = 0, z = 0 },
|
||||
{ x = 1, y = 0, z = 0 },
|
||||
{ x = 0, y = 0, z = -1 },
|
||||
{ x = 0, y = 0, z = 1 },
|
||||
}
|
||||
connected_stem_basename .. "_b" }
|
||||
|
||||
-- Connect the stem at stempos to the first neighboring gourd block.
|
||||
-- No-op if not a stem or no gourd block found
|
||||
local function try_connect_stem(stempos)
|
||||
local stem = minetest.get_node(stempos)
|
||||
if stem.name ~= full_unconnected_stem then
|
||||
return false
|
||||
if stem.name ~= full_unconnected_stem then return false end
|
||||
-- four directions, but avoid table allocation
|
||||
local neighbor = vector.offset(stempos, 1, 0, 0)
|
||||
if minetest.get_node(neighbor).name == gourd_itemstring then
|
||||
minetest.swap_node(stempos, { name = connected_stem_names[1] })
|
||||
return true
|
||||
end
|
||||
for n = 1, #neighbors do
|
||||
local offset = neighbors[n]
|
||||
local blockpos = vector.add(stempos, offset)
|
||||
local block = minetest.get_node(blockpos)
|
||||
if block.name == gourd_itemstring then
|
||||
if offset.x == 1 then
|
||||
minetest.set_node(stempos, { name = connected_stem_names[1] })
|
||||
elseif offset.x == -1 then
|
||||
minetest.set_node(stempos, { name = connected_stem_names[2] })
|
||||
elseif offset.z == 1 then
|
||||
minetest.set_node(stempos, { name = connected_stem_names[3] })
|
||||
elseif offset.z == -1 then
|
||||
minetest.set_node(stempos, { name = connected_stem_names[4] })
|
||||
end
|
||||
return true
|
||||
end
|
||||
local neighbor = vector.offset(stempos, -1, 0, 0)
|
||||
if minetest.get_node(neighbor).name == gourd_itemstring then
|
||||
minetest.swap_node(stempos, { name = connected_stem_names[2] })
|
||||
return true
|
||||
end
|
||||
local neighbor = vector.offset(stempos, 0, 0, 1)
|
||||
if minetest.get_node(neighbor).name == gourd_itemstring then
|
||||
minetest.swap_node(stempos, { name = connected_stem_names[3] })
|
||||
return true
|
||||
end
|
||||
local neighbor = vector.offset(stempos, 0, 0, -1)
|
||||
if minetest.get_node(neighbor).name == gourd_itemstring then
|
||||
minetest.swap_node(stempos, { name = connected_stem_names[4] })
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -238,29 +195,37 @@ function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, s
|
|||
if not gourd_def.after_destruct then
|
||||
gourd_def.after_destruct = function(blockpos, oldnode)
|
||||
-- Disconnect any connected stems, turning them back to normal stems
|
||||
for n = 1, #neighbors do
|
||||
local offset = neighbors[n]
|
||||
local expected_stem = connected_stem_names[n]
|
||||
local stempos = vector.add(blockpos, offset)
|
||||
local stem = minetest.get_node(stempos)
|
||||
if stem.name == expected_stem then
|
||||
minetest.add_node(stempos, { name = full_unconnected_stem })
|
||||
try_connect_stem(stempos)
|
||||
end
|
||||
-- four directions, but avoid using a table
|
||||
-- opposite directions to above, as we go from groud to stem now!
|
||||
local stempos = vector.offset(blockpos, -1, 0, 0)
|
||||
if minetest.get_node(stempos).name == connected_stem_names[1] then
|
||||
minetest.swap_node(stempos, { name = full_unconnected_stem })
|
||||
try_connect_stem(stempos)
|
||||
end
|
||||
local stempos = vector.offset(blockpos, 1, 0, 0)
|
||||
if minetest.get_node(stempos).name == connected_stem_names[2] then
|
||||
minetest.swap_node(stempos, { name = full_unconnected_stem })
|
||||
try_connect_stem(stempos)
|
||||
end
|
||||
local stempos = vector.offset(blockpos, 0, 0, -1)
|
||||
if minetest.get_node(stempos).name == connected_stem_names[3] then
|
||||
minetest.swap_node(stempos, { name = full_unconnected_stem })
|
||||
try_connect_stem(stempos)
|
||||
end
|
||||
local stempos = vector.offset(blockpos, 0, 0, 1)
|
||||
if minetest.get_node(stempos).name == connected_stem_names[4] then
|
||||
minetest.swap_node(stempos, { name = full_unconnected_stem })
|
||||
try_connect_stem(stempos)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not gourd_def.on_construct then
|
||||
function gourd_def.on_construct(blockpos)
|
||||
-- Connect all unconnected stems at full size
|
||||
for n = 1, #neighbors do
|
||||
local stempos = vector.add(blockpos, neighbors[n])
|
||||
try_connect_stem(stempos)
|
||||
end
|
||||
-- Call custom on_construct
|
||||
if gourd_on_construct_extra then
|
||||
gourd_on_construct_extra(blockpos)
|
||||
end
|
||||
try_connect_stem(vector.offset(blockpos, 1, 0, 0))
|
||||
try_connect_stem(vector.offset(blockpos, -1, 0, 0))
|
||||
try_connect_stem(vector.offset(blockpos, 0, 0, 1))
|
||||
try_connect_stem(vector.offset(blockpos, 0, 0, -1))
|
||||
end
|
||||
end
|
||||
minetest.register_node(gourd_itemstring, gourd_def)
|
||||
|
@ -269,72 +234,47 @@ function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, s
|
|||
|
||||
-- Default values for the stem definition
|
||||
if not stem_def.selection_box then
|
||||
stem_def.selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -0.15, -0.5, -0.15, 0.15, 0.5, 0.15 }
|
||||
},
|
||||
}
|
||||
end
|
||||
if not stem_def.paramtype then
|
||||
stem_def.paramtype = "light"
|
||||
end
|
||||
if not stem_def.drawtype then
|
||||
stem_def.drawtype = "plantlike"
|
||||
end
|
||||
if stem_def.walkable == nil then
|
||||
stem_def.walkable = false
|
||||
end
|
||||
if stem_def.sunlight_propagates == nil then
|
||||
stem_def.sunlight_propagates = true
|
||||
end
|
||||
if stem_def.drop == nil then
|
||||
stem_def.drop = stem_drop
|
||||
end
|
||||
if stem_def.groups == nil then
|
||||
stem_def.groups = { dig_immediate = 3, not_in_creative_inventory = 1, plant = 1, attached_node = 1, dig_by_water = 1, destroy_by_lava_flow = 1, }
|
||||
end
|
||||
if stem_def.sounds == nil then
|
||||
stem_def.sounds = mcl_sounds.node_sound_leaves_defaults()
|
||||
end
|
||||
|
||||
if not stem_def.on_construct then
|
||||
function stem_def.on_construct(stempos)
|
||||
-- Connect stem to gourd (if possible)
|
||||
try_connect_stem(stempos)
|
||||
end
|
||||
stem_def.selection_box = { type = "fixed", fixed = { { -0.15, -0.5, -0.15, 0.15, 0.5, 0.15 } } }
|
||||
end
|
||||
stem_def.paramtype = stem_def.paramtype or "light"
|
||||
stem_def.drawtype = stem_def.drawtype or "plantlike"
|
||||
stem_def.walkable = stem_def.walkable or false
|
||||
stem_def.sunlight_propagates = stem_def.sunlight_propagates == nil or stem_def.sunlight_propagates
|
||||
stem_def.drop = stem_def.drop or stem_drop
|
||||
stem_def.groups = stem_def.groups or { dig_immediate = 3, not_in_creative_inventory = 1, plant = 1, attached_node = 1, dig_by_water = 1, destroy_by_lava_flow = 1 }
|
||||
stem_def.sounds = stem_def.sounds or mcl_sounds.node_sound_leaves_defaults()
|
||||
stem_def.on_construct = stem_def.on_construct or try_connect_stem
|
||||
minetest.register_node(stem_itemstring, stem_def)
|
||||
|
||||
-- Register connected stems
|
||||
|
||||
local connected_stem_tiles = {
|
||||
{ "blank.png", --top
|
||||
{ "blank.png", -- top
|
||||
"blank.png", -- bottom
|
||||
"blank.png", -- right
|
||||
"blank.png", -- left
|
||||
connected_stem_texture, -- back
|
||||
connected_stem_texture .. "^[transformFX" --front
|
||||
connected_stem_texture .. "^[transformFX" -- front
|
||||
},
|
||||
{ "blank.png", --top
|
||||
{ "blank.png", -- top
|
||||
"blank.png", -- bottom
|
||||
"blank.png", -- right
|
||||
"blank.png", -- left
|
||||
connected_stem_texture .. "^[transformFX", --back
|
||||
connected_stem_texture .. "^[transformFX", -- back
|
||||
connected_stem_texture, -- front
|
||||
},
|
||||
{ "blank.png", --top
|
||||
{ "blank.png", -- top
|
||||
"blank.png", -- bottom
|
||||
connected_stem_texture .. "^[transformFX", -- right
|
||||
connected_stem_texture, -- left
|
||||
"blank.png", --back
|
||||
"blank.png", -- back
|
||||
"blank.png", -- front
|
||||
},
|
||||
{ "blank.png", --top
|
||||
{ "blank.png", -- top
|
||||
"blank.png", -- bottom
|
||||
connected_stem_texture, -- right
|
||||
connected_stem_texture .. "^[transformFX", -- left
|
||||
"blank.png", --back
|
||||
"blank.png", -- back
|
||||
"blank.png", -- front
|
||||
}
|
||||
}
|
||||
|
@ -359,17 +299,11 @@ function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, s
|
|||
walkable = false,
|
||||
drop = stem_drop,
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = connected_stem_nodebox[i]
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = connected_stem_selectionbox[i]
|
||||
},
|
||||
node_box = { type = "fixed", fixed = connected_stem_nodebox[i] },
|
||||
selection_box = { type = "fixed", fixed = connected_stem_selectionbox[i] },
|
||||
tiles = connected_stem_tiles[i],
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
groups = { dig_immediate = 3, not_in_creative_inventory = 1, plant = 1, attached_node = 1, dig_by_water = 1, destroy_by_lava_flow = 1, },
|
||||
groups = { dig_immediate = 3, not_in_creative_inventory = 1, plant = 1, attached_node = 1, dig_by_water = 1, destroy_by_lava_flow = 1 },
|
||||
sounds = mcl_sounds.node_sound_leaves_defaults(),
|
||||
_mcl_blast_resistance = 0,
|
||||
})
|
||||
|
@ -379,6 +313,16 @@ function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, s
|
|||
end
|
||||
end
|
||||
|
||||
-- Check for a suitable spot to grow
|
||||
local function check_neighbor_soil(blockpos)
|
||||
if minetest.get_node(blockpos).name ~= "air" then return false end
|
||||
local floorpos = vector.offset(blockpos, 0, -1, 0)
|
||||
local floorname = minetest.get_node(floorpos).name
|
||||
if floorname == "mcl_core:dirt" then return true end
|
||||
local floordef = minetest.registered_nodes[floorname]
|
||||
return floordef.groups.grass_block or floordef.groups.dirt or (floordef.groups.soil or 0) >= 2
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
label = "Grow gourd stem to gourd (" .. full_unconnected_stem .. " → " .. gourd_itemstring .. ")",
|
||||
nodenames = { full_unconnected_stem },
|
||||
|
@ -387,67 +331,50 @@ function mcl_farming:add_gourd(full_unconnected_stem, connected_stem_basename, s
|
|||
chance = grow_chance,
|
||||
action = function(stempos)
|
||||
local light = minetest.get_node_light(stempos)
|
||||
if light and light > 10 then
|
||||
-- Check the four neighbors and filter out neighbors where gourds can't grow
|
||||
local neighbors = {
|
||||
{ x = -1, y = 0, z = 0 },
|
||||
{ x = 1, y = 0, z = 0 },
|
||||
{ x = 0, y = 0, z = -1 },
|
||||
{ x = 0, y = 0, z = 1 },
|
||||
}
|
||||
local floorpos, floor
|
||||
for n = #neighbors, 1, -1 do
|
||||
local offset = neighbors[n]
|
||||
local blockpos = vector.add(stempos, offset)
|
||||
floorpos = vector.offset (blockpos, 0, -1,0) -- replaces { x = blockpos.x, y = blockpos.y - 1, z = blockpos.z }
|
||||
floor = minetest.get_node(floorpos)
|
||||
local block = minetest.get_node(blockpos)
|
||||
local soilgroup = minetest.get_item_group(floor.name, "soil")
|
||||
if not ((minetest.get_item_group(floor.name, "grass_block") == 1 or floor.name == "mcl_core:dirt" or soilgroup == 2 or soilgroup == 3) and block.name == "air") then
|
||||
table.remove(neighbors, n)
|
||||
end
|
||||
if not light or light <= 10 then return end
|
||||
-- Check the four neighbors and filter out neighbors where gourds can't grow
|
||||
local neighbor, dir, nchance = nil, -1, 1 -- reservoir sampling
|
||||
if nchance == 1 or math.random(1, nchance) == 1 then
|
||||
local blockpos = vector.offset(stempos, 1, 0, 0)
|
||||
if check_neighbor_soil(blockpos) then
|
||||
neighbor, dir, nchance = blockpos, 1, nchance + 1
|
||||
end
|
||||
|
||||
-- Gourd needs at least 1 free neighbor to grow
|
||||
if #neighbors > 0 then
|
||||
-- From the remaining neighbors, grow randomly
|
||||
local r = math.random(1, #neighbors)
|
||||
local offset = neighbors[r]
|
||||
local blockpos = vector.add(stempos, offset)
|
||||
local p2
|
||||
if offset.x == 1 then
|
||||
minetest.set_node(stempos, { name = connected_stem_names[1] })
|
||||
p2 = 3
|
||||
elseif offset.x == -1 then
|
||||
minetest.set_node(stempos, { name = connected_stem_names[2] })
|
||||
p2 = 1
|
||||
elseif offset.z == 1 then
|
||||
minetest.set_node(stempos, { name = connected_stem_names[3] })
|
||||
p2 = 2
|
||||
elseif offset.z == -1 then
|
||||
minetest.set_node(stempos, { name = connected_stem_names[4] })
|
||||
p2 = 0
|
||||
end
|
||||
-- Place the gourd
|
||||
if gourd_def.paramtype2 == "facedir" then
|
||||
minetest.add_node(blockpos, { name = gourd_itemstring, param2 = p2 })
|
||||
else
|
||||
minetest.add_node(blockpos, { name = gourd_itemstring })
|
||||
end
|
||||
|
||||
-- Reset farmland, etc. to dirt when the gourd grows on top
|
||||
|
||||
-- FIXED: The following 2 lines were missing, and wasn't being set (outside of the above loop that
|
||||
-- finds the neighbors.)
|
||||
-- FYI - don't factor this out thinking that the loop above is setting the positions correctly.
|
||||
floorpos = vector.offset (blockpos, 0, -1,0) -- replaces { x = blockpos.x, y = blockpos.y - 1, z = blockpos.z }
|
||||
floor = minetest.get_node(floorpos)
|
||||
-- END OF FIX -------------------------------------
|
||||
if minetest.get_item_group(floor.name, "dirtifies_below_solid") == 1 then
|
||||
minetest.set_node(floorpos, { name = "mcl_core:dirt" })
|
||||
end
|
||||
end
|
||||
if nchance == 1 or math.random(1, nchance) == 1 then
|
||||
local blockpos = vector.offset(stempos, -1, 0, 0)
|
||||
if check_neighbor_soil(blockpos) then
|
||||
neighbor, dir, nchance = blockpos, 2, nchance + 1
|
||||
end
|
||||
end
|
||||
if nchance == 1 or math.random(1, nchance) == 1 then
|
||||
local blockpos = vector.offset(stempos, 0, 0, 1)
|
||||
if check_neighbor_soil(blockpos) then
|
||||
neighbor, dir, nchance = blockpos, 3, nchance + 1
|
||||
end
|
||||
end
|
||||
if nchance == 1 or math.random(1, nchance) == 1 then
|
||||
local blockpos = vector.offset(stempos, 0, 0, -1)
|
||||
if check_neighbor_soil(blockpos) then
|
||||
neighbor, dir, nchance = blockpos, 4, nchance + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Gourd needs at least 1 free neighbor to grow
|
||||
if not neighbor then return end
|
||||
minetest.swap_node(stempos, { name = connected_stem_names[dir] })
|
||||
-- Place the gourd
|
||||
if gourd_def.paramtype2 == "facedir" then
|
||||
local p2 = (dir == 1 and 3) or (dir == 2 and 1) or (dir == 3 and 2) or 0
|
||||
minetest.add_node(neighbor, { name = gourd_itemstring, param2 = p2 })
|
||||
else
|
||||
minetest.add_node(neighbor, { name = gourd_itemstring })
|
||||
end
|
||||
|
||||
-- Reset farmland, etc. to dirt when the gourd grows on top
|
||||
local floorpos = vector.offset(neighbor, 0, -1, 0)
|
||||
if minetest.get_item_group(minetest.get_node(floorpos).name, "dirtifies_below_solid") == 1 then
|
||||
minetest.set_node(floorpos, { name = "mcl_core:dirt" })
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
@ -458,15 +385,11 @@ end
|
|||
-- * step: The nth growth step. Counting starts at 1
|
||||
-- * step_count: The number of total growth steps
|
||||
function mcl_farming:stem_color(startcolor, endcolor, step, step_count)
|
||||
local color = {}
|
||||
local function get_component(startt, endd, step, step_count)
|
||||
return math.floor(math.max(0, math.min(255, (startt + (((step - 1) / step_count) * endd)))))
|
||||
end
|
||||
color.r = get_component(startcolor.r, endcolor.r, step, step_count)
|
||||
color.g = get_component(startcolor.g, endcolor.g, step, step_count)
|
||||
color.b = get_component(startcolor.b, endcolor.b, step, step_count)
|
||||
local colorstring = string.format("#%02X%02X%02X", color.r, color.g, color.b)
|
||||
return colorstring
|
||||
local mix = (step - 1) / (step_count - 1)
|
||||
return string.format("#%02X%02X%02X",
|
||||
math.max(0, math.min(255, math.round((1 - mix) * startcolor.r + mix * endcolor.r))),
|
||||
math.max(0, math.min(255, math.round((1 - mix) * startcolor.g + mix * endcolor.g))),
|
||||
math.max(0, math.min(255, math.round((1 - mix) * startcolor.b + mix * endcolor.b))))
|
||||
end
|
||||
|
||||
--[[Get a callback that either eats the item or plants it.
|
||||
|
@ -475,12 +398,8 @@ Used for on_place callbacks for craft items which are seeds that can also be con
|
|||
]]
|
||||
function mcl_farming:get_seed_or_eat_callback(plantname, hp_change)
|
||||
return function(itemstack, placer, pointed_thing)
|
||||
local new = mcl_farming:place_seed(itemstack, placer, pointed_thing, plantname)
|
||||
if new then
|
||||
return new
|
||||
else
|
||||
return minetest.do_item_eat(hp_change, nil, itemstack, placer, pointed_thing)
|
||||
end
|
||||
return mcl_farming:place_seed(itemstack, placer, pointed_thing, plantname)
|
||||
or minetest.do_item_eat(hp_change, nil, itemstack, placer, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -489,12 +408,11 @@ minetest.register_lbm({
|
|||
name = "mcl_farming:growth",
|
||||
nodenames = { "group:plant" },
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
action = function(pos, node, dtime_s)
|
||||
local identifier = plant_nodename_to_id_list[node.name]
|
||||
if not identifier then
|
||||
return
|
||||
end
|
||||
local low_speed = minetest.get_node({ x = pos.x, y = pos.y - 1, z = pos.z }).name ~= "mcl_farming:soil_wet"
|
||||
mcl_farming:grow_plant(identifier, pos, node, false, false, low_speed)
|
||||
if not identifier then return end
|
||||
local low_speed = minetest.get_node(vector.offset(pos, 0, -1, 0)).name ~= "mcl_farming:soil_wet"
|
||||
mcl_farming:grow_plant(identifier, pos, node, 0, false, low_speed)
|
||||
end,
|
||||
})
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
name = mcl_fences
|
||||
depends = mcl_core, mcl_sounds
|
||||
depends = mcl_core, mcl_nether, mcl_sounds
|
||||
optional_depends = doc, screwdriver
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mcl_grindstone
|
||||
author = TheRandomLegoBrick, ChrisPHP
|
||||
depends = mcl_experience, mcl_sounds
|
||||
depends = mcl_experience, mcl_formspec, mcl_sounds
|
||||
description = Add block that disenchants tools and armour except for curses, and repairs two items of the same type it is also the weapon smith's work station.
|
||||
|
|
|
@ -147,20 +147,14 @@ local function spawn_mobs(pos, elapsed)
|
|||
-- get meta
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
-- get settings
|
||||
local mob = meta:get_string("Mob")
|
||||
local mlig = meta:get_int("MinLight")
|
||||
local xlig = meta:get_int("MaxLight")
|
||||
local num = meta:get_int("MaxMobsInArea")
|
||||
local pla = meta:get_int("PlayerDistance")
|
||||
local yof = meta:get_int("YOffset")
|
||||
|
||||
-- if amount is 0 then do nothing
|
||||
local num = meta:get_int("MaxMobsInArea")
|
||||
if num == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- are we spawning a registered mob?
|
||||
local mob = meta:get_string("Mob")
|
||||
if not mcl_mobs.spawning_mobs[mob] then
|
||||
minetest.log("error", "[mcl_mobspawners] Mob Spawner: Mob doesn't exist: "..mob)
|
||||
return
|
||||
|
@ -174,17 +168,14 @@ local function spawn_mobs(pos, elapsed)
|
|||
local timer = minetest.get_node_timer(pos)
|
||||
|
||||
-- spawn mob if player detected and in range
|
||||
local pla = meta:get_int("PlayerDistance")
|
||||
if pla > 0 then
|
||||
|
||||
local in_range = 0
|
||||
local objs = minetest.get_objects_inside_radius(pos, pla)
|
||||
|
||||
for _,oir in pairs(objs) do
|
||||
|
||||
if oir:is_player() then
|
||||
|
||||
in_range = 1
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
@ -213,7 +204,6 @@ local function spawn_mobs(pos, elapsed)
|
|||
|
||||
-- count mob objects of same type in area
|
||||
for k, obj in ipairs(objs) do
|
||||
|
||||
ent = obj:get_luaentity()
|
||||
|
||||
if ent and ent.name and ent.name == mob then
|
||||
|
@ -228,33 +218,28 @@ local function spawn_mobs(pos, elapsed)
|
|||
end
|
||||
|
||||
-- find air blocks within 8×3×8 nodes of spawner
|
||||
local yof = meta:get_int("YOffset")
|
||||
local air = minetest.find_nodes_in_area(
|
||||
{x = pos.x - 4, y = pos.y - 1 + yof, z = pos.z - 4},
|
||||
{x = pos.x + 4, y = pos.y + 1 + yof, z = pos.z + 4},
|
||||
{"air"})
|
||||
|
||||
-- spawn up to 4 mobs in random air blocks
|
||||
-- spawn mobs in random air blocks. Default max of 4
|
||||
if air then
|
||||
local max = 4
|
||||
if spawn_count_overrides[mob] then
|
||||
max = spawn_count_overrides[mob]
|
||||
end
|
||||
for a=1, max do
|
||||
if #air <= 0 then
|
||||
-- We're out of space! Stop spawning
|
||||
break
|
||||
end
|
||||
local air_index = math.random(#air)
|
||||
local pos2 = air[air_index]
|
||||
local lig = minetest.get_node_light(pos2) or 0
|
||||
|
||||
pos2.y = pos2.y + 0.5
|
||||
local num_to_spawn = spawn_count_overrides[mob] or 4
|
||||
local mlig = meta:get_int("MinLight")
|
||||
local xlig = meta:get_int("MaxLight")
|
||||
|
||||
while #air > 0 do
|
||||
local pos2 = table.remove_random_element(air)
|
||||
-- only if light levels are within range
|
||||
local lig = minetest.get_node_light(pos2) or 0
|
||||
if lig >= mlig and lig <= xlig then
|
||||
minetest.add_entity(pos2, mob)
|
||||
if mcl_mobs.spawn(pos2, mob) then
|
||||
num_to_spawn = num_to_spawn - 1
|
||||
if num_to_spawn == 0 then break end
|
||||
end
|
||||
end
|
||||
table.remove(air, air_index)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -401,7 +386,7 @@ minetest.register_lbm({
|
|||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
for name,mobinfo in pairs(minetest.registered_entities) do
|
||||
if ( mobinfo.is_mob or name:find("mobs_mc") ) and not ( mobinfo.visual_size or mobinfo._convert_to ) then
|
||||
if mobinfo.is_mob and not ( mobinfo.visual_size or mobinfo._convert_to ) then
|
||||
minetest.log("warning", "Definition for "..tostring(name).." is missing field 'visual_size', mob spawners will not work properly")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -196,7 +196,7 @@ function kelp.find_unsubmerged(pos, node, height)
|
|||
for i=1,height do
|
||||
walk_pos.y = y + i
|
||||
local walk_node = mt_get_node(walk_pos)
|
||||
if not kelp.is_submerged(walk_node) then
|
||||
if walk_node.name ~= "ignore" and not kelp.is_submerged(walk_node) then
|
||||
return walk_pos, walk_node, height, i
|
||||
end
|
||||
end
|
||||
|
|
|
@ -279,7 +279,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||
for field_name, value in pairs(fields) do
|
||||
if field_name ~= "scroll" then
|
||||
local itemname = fieldname_to_itemname(field_name)
|
||||
player:get_meta():set_string("mcl_stonecutter:selected", itemname)
|
||||
set_selected_item(player, itemname)
|
||||
update_stonecutter_slots(player)
|
||||
mcl_stonecutter.show_stonecutter_form(player)
|
||||
|
|
|
@ -229,14 +229,14 @@ function mcl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water)
|
|||
sp = minetest.find_nodes_in_area_under_air(p1,p2,spawnon)
|
||||
end
|
||||
table.shuffle(sp)
|
||||
for i,node in pairs(sp) do
|
||||
if not peaceful and i <= n then
|
||||
local pos = vector.offset(node,0,1,0)
|
||||
if pos then
|
||||
minetest.add_entity(pos,mob)
|
||||
end
|
||||
local count = 0
|
||||
local mob_def = minetest.registered_entities[mob]
|
||||
local enabled = (not peaceful) or (mob_def and mob_def.spawn_class ~= "hostile")
|
||||
for _,node in pairs(sp) do
|
||||
if enabled and count < n and minetest.add_entity(vector.offset(node, 0, 1, 0), mob) then
|
||||
count = count + 1
|
||||
end
|
||||
minetest.get_meta(node):set_string("spawnblock","yes")
|
||||
minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -371,7 +371,12 @@ function mcl_structures.register_structure_spawn(def)
|
|||
if active_object_count_wider > limit + mob_cap_animal then return end
|
||||
if active_object_count_wider > mob_cap_player then return end
|
||||
local p = vector.offset(pos,0,1,0)
|
||||
if minetest.get_node(p).name ~= "air" then return end
|
||||
local pname = minetest.get_node(p).name
|
||||
if def.type_of_spawning == "water" then
|
||||
if pname ~= "mcl_core:water_source" and pname ~= "mclx_core:river_water_source" then return end
|
||||
else
|
||||
if pname ~= "air" then return end
|
||||
end
|
||||
if minetest.get_meta(pos):get_string("spawnblock") == "" then return end
|
||||
if mg_name ~= "v6" and mg_name ~= "singlenode" and def.biomes then
|
||||
if table.indexof(def.biomes,minetest.get_biome_name(minetest.get_biome_data(p).biome)) == -1 then
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
# mcl_wip
|
||||
Used to mark items or nodes as WIP.
|
||||
|
||||
## mcl_wip.register_wip_item(itemname)
|
||||
Register <itemname> as a WIP item.
|
||||
If <itemname> isn't a valid itemname, an error will be shown after mods loaded.
|
||||
## `mcl_wip.register_wip_item(itemname)`
|
||||
Register `itemname` as a WIP item.
|
||||
If `itemname` isn't a valid itemname, an error will be shown after mods loaded.
|
||||
|
||||
## mcl_wip.register_experimental_item(itemname)
|
||||
Register <itemname> as a experimental item.
|
||||
If <itemname> isn't a valid itemname, an error will be shown after mods loaded.
|
||||
## `mcl_wip.register_experimental_item(itemname)`
|
||||
Register `itemname` as a experimental item.
|
||||
If `itemname` isn't a valid itemname, an error will be shown after mods loaded.
|
||||
|
||||
## mcl_wip.registered_wip_items
|
||||
## `mcl_wip.registered_wip_items`
|
||||
Table containing WIP items names.
|
||||
|
||||
## mcl_wip.registered_experimental_items
|
||||
Table containing experimental items names.
|
||||
## `mcl_wip.registered_experimental_items`
|
||||
Table containing experimental items names.
|
||||
|
|
|
@ -514,21 +514,29 @@ minetest.register_globalstep(function(dtime)
|
|||
return
|
||||
end
|
||||
|
||||
-- Standing on soul sand? If so, walk slower (unless player wears Soul Speed boots)
|
||||
if node_stand == "mcl_nether:soul_sand" then
|
||||
-- TODO: Tweak walk speed
|
||||
-- TODO: Also slow down mobs
|
||||
-- Slow down even more when soul sand is above certain block
|
||||
local boots = player:get_inventory():get_stack("armor", 5)
|
||||
local soul_speed = mcl_enchanting.get_enchantment(boots, "soul_speed")
|
||||
if soul_speed > 0 then
|
||||
playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:soul_speed", soul_speed * 0.105 + 1.3)
|
||||
else
|
||||
if node_stand_below == "mcl_core:ice" or node_stand_below == "mcl_core:packed_ice" or node_stand_below == "mcl_core:slimeblock" or node_stand_below == "mcl_core:water_source" then
|
||||
playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:soul_speed", 0.1)
|
||||
local boots = player:get_inventory():get_stack("armor", 5)
|
||||
local soul_speed = mcl_enchanting.get_enchantment(boots, "soul_speed")
|
||||
|
||||
-- Standing on a soul block? If so, check for speed bonus / penalty
|
||||
if get_item_group(node_stand, "soul_block") ~= 0 then
|
||||
|
||||
-- Standing on soul sand? If so, walk slower (unless player wears Soul Speed boots, then apply bonus)
|
||||
if node_stand == "mcl_nether:soul_sand" then
|
||||
-- TODO: Tweak walk speed
|
||||
-- TODO: Also slow down mobs
|
||||
-- Slow down even more when soul sand is above certain block
|
||||
if soul_speed > 0 then
|
||||
playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:soul_speed", soul_speed * 0.105 + 1.3)
|
||||
else
|
||||
playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:soul_speed", 0.4)
|
||||
if node_stand_below == "mcl_core:ice" or node_stand_below == "mcl_core:packed_ice" or node_stand_below == "mcl_core:slimeblock" or node_stand_below == "mcl_core:water_source" then
|
||||
playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:soul_speed", 0.1)
|
||||
else
|
||||
playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:soul_speed", 0.4)
|
||||
end
|
||||
end
|
||||
elseif soul_speed > 0 then
|
||||
-- Standing on a different soul block? If so, apply Soul Speed bonus unconditionally
|
||||
playerphysics.add_physics_factor(player, "speed", "mcl_playerplus:soul_speed", soul_speed * 0.105 + 1.3)
|
||||
end
|
||||
else
|
||||
playerphysics.remove_physics_factor(player, "speed", "mcl_playerplus:soul_speed")
|
||||
|
|
|
@ -453,7 +453,7 @@ function mcl_spawn.set_spawn_pos(player, pos, message)
|
|||
-- Pass in villager as arg. Shouldn't know about villagers
|
||||
if bed_bottom_meta then
|
||||
mcl_log("Removing villager from bed bottom meta")
|
||||
bed_bottom_meta:set_string("villager", nil)
|
||||
bed_bottom_meta:set_string("villager", "")
|
||||
else
|
||||
mcl_log("Cannot remove villager from bed bottom meta")
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue