1 local dmes
= minetest
.get_modpath("mcl_death_messages") ~= nil
2 local hung
= minetest
.get_modpath("mcl_hunger") ~= nil
4 local get_falling_depth
= function(self
)
5 if not self
._startpos
then
7 self
._startpos
= self
.object
:get_pos()
9 return self
._startpos
.y
- vector
.round(self
.object
:get_pos()).y
12 local deal_falling_damage
= function(self
, dtime
)
13 if minetest
.get_item_group(self
.node
.name
, "falling_node_damage") == 0 then
16 -- Cause damage to any player it hits.
17 -- Algorithm based on MC anvils.
18 -- TODO: Support smashing other objects, too.
19 local pos
= self
.object
:get_pos()
20 if not self
._startpos
then
24 local objs
= minetest
.get_objects_inside_radius(pos
, 1)
25 for _
,v
in ipairs(objs
) do
27 if v
:is_player() and hp
~= 0 then
28 if not self
._hit_players
then
29 self
._hit_players
= {}
31 local name
= v
:get_player_name()
33 for _
,v
in ipairs(self
._hit_players
) do
39 table.insert(self
._hit_players
, name
)
40 local way
= self
._startpos
.y
- pos
.y
41 local damage
= (way
- 1) * 2
42 damage
= math
.min(40, math
.max(0, damage
))
49 -- TODO: Reduce damage if wearing a helmet
51 if minetest
.get_item_group(self
.node
.name
, "anvil") ~= 0 then
52 msg
= "%s was smashed by a falling anvil."
54 msg
= "%s was smashed by a falling block."
57 mcl_death_messages
.player_damage(v
, string.format(msg
, v
:get_player_name()))
60 mcl_hunger
.exhaust(v
:get_player_name(), mcl_hunger
.EXHAUST_DAMAGE
)
70 minetest
.register_entity(":__builtin:falling_node", {
71 initial_properties
= {
73 visual_size
= {x
= 0.667, y
= 0.667},
77 collide_with_objects
= false,
78 collisionbox
= {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
84 set_node
= function(self
, node
, meta
)
86 self
.meta
= meta
or {}
87 self
.object
:set_properties({
89 textures
= {node
.name
},
91 local def
= core
.registered_nodes
[node
.name
]
92 -- Set correct entity yaw
93 if def
and node
.param2
~= 0 then
94 if (def
.paramtype2
== "facedir" or def
.paramtype2
== "colorfacedir") then
95 self
.object
:set_yaw(core
.dir_to_yaw(core
.facedir_to_dir(node
.param2
)))
96 elseif (def
.paramtype2
== "wallmounted" or def
.paramtype2
== "colorwallmounted") then
97 self
.object
:set_yaw(core
.dir_to_yaw(core
.wallmounted_to_dir(node
.param2
)))
102 get_staticdata
= function(self
)
103 local meta
= self
.meta
104 -- Workaround: Save inventory seperately from metadata.
105 -- Because Minetest crashes when a node with inventory gets deactivated
106 -- (GitHub issue #7020).
107 -- FIXME: Remove the _inv workaround when it is no longer needed
117 _startpos
= self
._startpos
,
118 _hit_players
= self
._hit_players
,
120 return minetest
.serialize(ds
)
123 on_activate
= function(self
, staticdata
)
124 self
.object
:set_armor_groups({immortal
= 1})
126 local ds
= minetest
.deserialize(staticdata
)
128 self
._startpos
= ds
._startpos
129 self
._hit_players
= ds
._hit_players
132 meta
.inventory
= ds
._inv
133 self
:set_node(ds
.node
, meta
)
137 elseif staticdata
~= "" then
138 self
:set_node({name
= staticdata
})
140 if not self
._startpos
then
141 self
._startpos
= self
.object
:get_pos()
143 self
._startpos
= vector
.round(self
._startpos
)
146 on_step
= function(self
, dtime
)
148 local acceleration
= self
.object
:getacceleration()
149 if not vector
.equals(acceleration
, {x
= 0, y
= -10, z
= 0}) then
150 self
.object
:setacceleration({x
= 0, y
= -10, z
= 0})
152 -- Turn to actual node when colliding with ground, or continue to move
153 local pos
= self
.object
:get_pos()
156 local np
= {x
= pos
.x
, y
= pos
.y
+ 0.3, z
= pos
.z
}
157 local n2
= minetest
.get_node(np
)
158 if n2
.name
== "mcl_portals:portal_end" then
159 -- TODO: Teleport falling node.
164 -- Position of bottom center point
165 local bcp
= {x
= pos
.x
, y
= pos
.y
- 0.7, z
= pos
.z
}
166 -- Avoid bugs caused by an unloaded node below
167 local bcn
= minetest
.get_node_or_nil(bcp
)
168 local bcd
= bcn
and minetest
.registered_nodes
[bcn
.name
]
170 -- TODO: At this point, we did 2 get_nodes in 1 tick.
171 -- Figure out how to improve that (if it is a problem).
173 if bcn
and (not bcd
or bcd
.walkable
or
174 (minetest
.get_item_group(self
.node
.name
, "float") ~= 0 and
175 bcd
.liquidtype
~= "none")) then
176 if bcd
and bcd
.leveled
and
177 bcn
.name
== self
.node
.name
then
178 local addlevel
= self
.node
.level
179 if not addlevel
or addlevel
<= 0 then
180 addlevel
= bcd
.leveled
182 if minetest
.add_node_level(bcp
, addlevel
) == 0 then
183 if minetest
.registered_nodes
[self
.node
.name
]._mcl_after_falling
then
184 minetest
.registered_nodes
[self
.node
.name
]._mcl_after_falling(bcp
, get_falling_depth(self
))
186 deal_falling_damage(self
, dtime
)
190 elseif bcd
and bcd
.buildable_to
and
191 (minetest
.get_item_group(self
.node
.name
, "float") == 0 or
192 bcd
.liquidtype
== "none") then
193 minetest
.remove_node(bcp
)
196 local nd
= minetest
.registered_nodes
[n2
.name
]
197 if n2
.name
== "mcl_portals:portal_end" then
198 -- TODO: Teleport falling node.
200 elseif (nd
and nd
.buildable_to
== true) or minetest
.get_item_group(self
.node
.name
, "crush_after_fall") ~= 0 then
201 -- Replace destination node if it's buildable to
202 minetest
.remove_node(np
)
204 for _
, callback
in pairs(minetest
.registered_on_dignodes
) do
207 if minetest
.registered_nodes
[self
.node
.name
] then
208 minetest
.add_node(np
, self
.node
)
209 if minetest
.registered_nodes
[self
.node
.name
]._mcl_after_falling
then
210 minetest
.registered_nodes
[self
.node
.name
]._mcl_after_falling(np
, get_falling_depth(self
))
213 local meta
= minetest
.get_meta(np
)
214 meta
:from_table(self
.meta
)
218 -- Drop the *falling node* as an item if the destination node is NOT buildable to
219 local drops
= minetest
.get_node_drops(self
.node
.name
, "")
220 for _
, dropped_item
in pairs(drops
) do
221 minetest
.add_item(np
, dropped_item
)
224 deal_falling_damage(self
, dtime
)
226 minetest
.check_for_falling(np
)
229 local vel
= self
.object
:getvelocity()
230 -- Fix position if entity does not move
231 if vector
.equals(vel
, {x
= 0, y
= 0, z
= 0}) then
232 local npos
= vector
.round(self
.object
:get_pos())
233 local npos2
= table.copy(npos
)
234 npos2
.y
= npos2
.y
- 2
235 local lownode
= minetest
.get_node(npos2
)
236 -- Special check required for fences and walls, because of their overhigh collision box.
237 if minetest
.get_item_group(lownode
.name
, "fence") == 1 or minetest
.get_item_group(lownode
.name
, "wall") == 1 then
238 -- Instantly stop the node if it is above a fence/wall. This is needed
239 -- because the falling node collides early with a fence/wall node.
240 -- Hacky, because the falling node will teleport a short distance, instead
241 -- of smoothly fall on the fence post.
242 local npos3
= table.copy(npos
)
243 npos3
.y
= npos3
.y
- 1
244 minetest
.add_node(npos3
, self
.node
)
245 if minetest
.registered_nodes
[self
.node
.name
]._mcl_after_falling
then
246 minetest
.registered_nodes
[self
.node
.name
]._mcl_after_falling(npos3
, get_falling_depth(self
))
248 deal_falling_damage(self
, dtime
)
250 minetest
.check_for_falling(npos3
)
253 -- Normal position fix (expected case)
254 self
.object
:set_pos(npos
)
258 deal_falling_damage(self
, dtime
)