init.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. falling = falling or {}
  2. falling.modpath = minetest.get_modpath("falling")
  3. local get_node = core.get_node
  4. local get_node_or_nil = core.get_node_or_nil
  5. local get_node_drops = core.get_node_drops
  6. local add_item = core.add_item
  7. local add_node = core.set_node
  8. local add_node_level = core.add_node_level
  9. local remove_node = core.remove_node
  10. local random = math.random
  11. local vector_round = vector.round
  12. local vector_add = vector.add
  13. local vector_equals = vector.equals
  14. local all_nodes = core.registered_nodes
  15. local string_find = string.find
  16. local get_objects_inside_radius = core.get_objects_inside_radius
  17. local get_item_group = core.get_item_group
  18. local get_meta = core.get_meta
  19. local after = core.after
  20. -- Called to check if a falling node may cause harm when it lands.
  21. -- Must return the amount of harm the node does. Called when the falling node is first spawned.
  22. local function node_harm(name)
  23. if not name or name == "air" or name == "ignore" then
  24. return 0, 0
  25. end
  26. -- Abort if node cannot cause harm.
  27. if name == "bones:bones_type2" or string_find(name, "lava_") or string_find(name, "water_") then
  28. return 0, 0
  29. end
  30. -- Non-walkable nodes cause no harm.
  31. local ndef = all_nodes[name]
  32. if ndef then
  33. if not ndef.walkable then
  34. return 0, 0
  35. end
  36. -- Falling leaves cause a little damage.
  37. if ndef.groups then
  38. local lg = (ndef.groups.leaves or 0)
  39. if lg > 0 then
  40. return 1, 1
  41. end
  42. end
  43. -- If `crushing_damage' is defined, use it.
  44. if ndef.crushing_damage then
  45. local cd = ndef.crushing_damage
  46. -- Mobs always take damage*5.
  47. return cd, cd*5
  48. end
  49. end
  50. -- Default amount of harm to: player, mobs.
  51. return 4, 20
  52. end
  53. local function node_sound(name)
  54. local def = all_nodes[name]
  55. if not def then
  56. return "default_gravel_footstep"
  57. end
  58. if def.no_sound_on_fall then
  59. return
  60. end
  61. if def.sounds then
  62. if def.sounds.footstep then
  63. local s = def.sounds.footstep
  64. if s.name then
  65. return s.name
  66. end
  67. end
  68. end
  69. return "default_gravel_footstep"
  70. end
  71. -- Hardcoded tool capabilities for speed.
  72. local tool_capabilities = {
  73. full_punch_interval = 0.1,
  74. max_drop_level = 3,
  75. groupcaps= {
  76. fleshy = {times={[1] = 0, [2] = 0, [3] = 0}, uses = 0, maxlevel = 3},
  77. choppy = {times={[1] = 0, [2] = 0, [3] = 0}, uses = 0, maxlevel = 3},
  78. bendy = {times={[1] = 0, [2] = 0, [3] = 0}, uses = 0, maxlevel = 3},
  79. cracky = {times={[1] = 0, [2] = 0, [3] = 0}, uses = 0, maxlevel = 3},
  80. crumbly = {times={[1] = 0, [2] = 0, [3] = 0}, uses = 0, maxlevel = 3},
  81. snappy = {times={[1] = 0, [2] = 0, [3] = 0}, uses = 0, maxlevel = 3},
  82. },
  83. damage_groups = {fleshy = mharm},
  84. }
  85. local entity_physics = function(pos, node, pharm, mharm)
  86. if not pharm or pharm < 1 then
  87. return
  88. end
  89. if not mharm or mharm < 1 then
  90. return
  91. end
  92. local objects = get_objects_inside_radius(pos, 1.2)
  93. for i = 1, #objects do
  94. local r = objects[i]
  95. if r:is_player() then
  96. if not gdac.player_is_admin(r) then
  97. local hp = r:get_hp()
  98. if hp > 0 then
  99. r:set_hp(hp - pharm) -- Damage players.
  100. if r:get_hp() <= 0 then
  101. -- Player will die.
  102. minetest.chat_send_all("# Server: Player <" .. rename.gpn(r:get_player_name()) .. "> was crushed to death.")
  103. end
  104. end
  105. end
  106. else
  107. local l = r:get_luaentity()
  108. if l then
  109. if l.mob and l.mob == true then
  110. r:punch(r, 1, tool_capabilities, nil)
  111. elseif l.name == "__builtin:item" then
  112. droplift.invoke(r)
  113. end
  114. end
  115. end
  116. end
  117. end
  118. local function node_not_walkable(pos)
  119. local nn = get_node(pos).name
  120. if nn == "air" then return true end
  121. local def = all_nodes[nn]
  122. if def and not def.walkable then return true end
  123. end
  124. local adjacency = {
  125. {x=0, y=0, z=0},
  126. {x=0, y=0, z=0},
  127. {x=0, y=0, z=0},
  128. {x=0, y=0, z=0},
  129. }
  130. local function outof_bounds(pos)
  131. if pos.z < -30912 then
  132. return true
  133. end
  134. if pos.z > 30927 then
  135. return true
  136. end
  137. if pos.x > 30927 then
  138. return true
  139. end
  140. if pos.x < -30912 then
  141. return true
  142. end
  143. return false
  144. end
  145. local find_slope = function(pos)
  146. adjacency[1].x=pos.x-1 adjacency[1].y=pos.y adjacency[1].z=pos.z
  147. adjacency[2].x=pos.x+1 adjacency[2].y=pos.y adjacency[2].z=pos.z
  148. adjacency[3].x=pos.x adjacency[3].y=pos.y adjacency[3].z=pos.z+1
  149. adjacency[4].x=pos.x adjacency[4].y=pos.y adjacency[4].z=pos.z-1
  150. local targets = {}
  151. for i = 1, 4 do
  152. local p = adjacency[i]
  153. if node_not_walkable(p) then
  154. p.y = p.y + 1
  155. if node_not_walkable(p) and not outof_bounds(p) then
  156. targets[#targets+1] = {x=p.x, y=p.y-1, z=p.z}
  157. end
  158. p.y = p.y - 1
  159. end
  160. end
  161. if #targets == 0 then
  162. return nil
  163. end
  164. return targets[random(1, #targets)]
  165. end
  166. minetest.register_entity(":__builtin:falling_node", {
  167. initial_properties = {
  168. visual = "wielditem",
  169. visual_size = {x = 0.667, y = 0.667},
  170. textures = {},
  171. physical = true,
  172. is_visible = false,
  173. collide_with_objects = false,
  174. collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
  175. },
  176. node = {},
  177. meta = {},
  178. set_node = function(self, node, meta)
  179. -- If this is a snow node and snow is supposed to be melted, then just remove the falling entity so we don't create gfx artifacts.
  180. if node.name == "default:snow" then
  181. if not snow.is_visible() then
  182. self.object:remove()
  183. return
  184. end
  185. end
  186. self.node = node
  187. self.meta = meta or {}
  188. self.object:set_properties({
  189. is_visible = true,
  190. textures = {node.name},
  191. })
  192. self.pharm, self.mharm = node_harm(node.name)
  193. self.sound = node_sound(node.name)
  194. end,
  195. get_staticdata = function(self)
  196. local ds = {
  197. node = self.node,
  198. meta = self.meta,
  199. pharm = self.pharm,
  200. mharm = self.mharm,
  201. sound = self.sound,
  202. }
  203. return core.serialize(ds)
  204. end,
  205. on_activate = function(self, staticdata)
  206. self.object:set_armor_groups({immortal = 1})
  207. local pos = self.object:get_pos()
  208. if outof_bounds(pos) then
  209. self.object:remove()
  210. return
  211. end
  212. local ds = core.deserialize(staticdata)
  213. if ds and ds.node then
  214. self:set_node(ds.node, ds.meta)
  215. elseif ds then
  216. self:set_node(ds)
  217. elseif staticdata ~= "" then
  218. self:set_node({name = staticdata})
  219. end
  220. end,
  221. on_step = function(self, dtime)
  222. -- Set gravity
  223. local acceleration = self.object:getacceleration()
  224. if not vector_equals(acceleration, {x = 0, y = -8, z = 0}) then
  225. self.object:setacceleration({x = 0, y = -8, z = 0})
  226. end
  227. -- Turn to actual node when colliding with ground, or continue to move
  228. local pos = self.object:get_pos()
  229. -- Position of bottom center point
  230. local bcp = vector_round({x = pos.x, y = pos.y - 0.7, z = pos.z})
  231. -- Avoid bugs caused by an unloaded node below
  232. local bcn = get_node_or_nil(bcp)
  233. local bcd = bcn and all_nodes[bcn.name]
  234. if bcn and (not bcd or bcd.walkable or (get_item_group(self.node.name, "float") ~= 0 and bcd.liquidtype ~= "none")) then
  235. if bcd and bcd.leveled and bcn.name == self.node.name then
  236. local addlevel = self.node.level
  237. if not addlevel or addlevel <= 0 then
  238. addlevel = bcd.leveled
  239. end
  240. if add_node_level(bcp, addlevel) == 0 then
  241. self.object:remove()
  242. return
  243. end
  244. elseif bcd and bcd.buildable_to and (get_item_group(self.node.name, "float") == 0 or bcd.liquidtype == "none") then
  245. remove_node(bcp)
  246. return
  247. end
  248. -- We have hit the ground. Check for a possible slope which we can continue to fall down.
  249. local ss = find_slope(bcp)
  250. if ss ~= nil then
  251. self.object:set_pos(vector_add(ss, {x=0, y=1, z=0}))
  252. self.object:set_velocity({x=0, y=0, z=0})
  253. entity_physics(bcp, self.node, self.pharm, self.mharm)
  254. ambiance.sound_play("default_gravel_footstep", ss, 0.2, 20)
  255. return
  256. end
  257. local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
  258. local protected = nil
  259. -- Check what's here.
  260. local n2 = get_node(np)
  261. local nd = all_nodes[n2.name]
  262. local nodedef = all_nodes[self.node.name]
  263. if nodedef then
  264. -- If not merely replacing air, or the nodetype is `buildable_to', then check protection.
  265. if n2.name ~= "air" or nodedef.buildable_to then
  266. protected = minetest.test_protection(np, "")
  267. end
  268. -- If it's not air and not liquid (and not protected), remove node and replace it with it's drops.
  269. if not protected and n2.name ~= "air" and (not nd or nd.liquidtype == "none") then
  270. remove_node(np)
  271. if nd.buildable_to == false then
  272. -- Add dropped items.
  273. -- Pass node name, because passing a node table gives wrong results.
  274. local drops = get_node_drops(n2.name, "")
  275. for _, dropped_item in pairs(drops) do
  276. add_item(np, dropped_item)
  277. end
  278. end
  279. -- Run script hook
  280. for _, callback in pairs(core.registered_on_dignodes) do
  281. callback(np, n2)
  282. end
  283. end
  284. -- Create node and remove entity.
  285. if not protected or n2.name == "air" or n2.name == "default:snow" or n2.name == "snow:footprints" then
  286. if protected and nodedef.buildable_to then
  287. --minetest.chat_send_player("MustTest", "# Server: Testing 1!")
  288. -- If the position is protected and the node we're placing is `buildable_to',
  289. -- then we must drop an item instead in order to avoid creating a protection exploit,
  290. -- even though we'd normally be placing into air.
  291. local callback = nodedef.on_collapse_to_entity
  292. if callback then
  293. callback(np, self.node)
  294. else
  295. add_item(np, self.node)
  296. end
  297. else
  298. --minetest.chat_send_player("MustTest", "# Server: Testing 2!")
  299. -- We're either placing into air, or crushing something that isn't protected.
  300. add_node(np, self.node)
  301. if self.meta then
  302. local meta = get_meta(np)
  303. meta:from_table(self.meta)
  304. end
  305. entity_physics(np, self.node, self.pharm, self.mharm)
  306. if self.sound then
  307. ambiance.sound_play(self.sound, np, 1.3, 20)
  308. end
  309. -- Mark node as unprotectable.
  310. -- This has to come before executing the node callback because the callback might remove the node.
  311. -- If the callback changes the node placed, it should use `minetest.swap_node()'.
  312. local meta = get_meta(np)
  313. meta:set_int("protection_cancel", 1)
  314. meta:mark_as_private("protection_cancel")
  315. -- Execute node callback.
  316. local callback = nodedef.on_finish_collapse
  317. if callback then
  318. callback(np, self.node)
  319. end
  320. -- Dirtspread notification.
  321. dirtspread.on_environment(np)
  322. end
  323. else
  324. -- Not air and protected, so we drop as entity instead.
  325. --minetest.chat_send_player("MustTest", "# Server: Testing 3!")
  326. local callback = nodedef.on_collapse_to_entity
  327. if callback then
  328. callback(np, self.node)
  329. else
  330. add_item(np, self.node)
  331. end
  332. end
  333. end
  334. self.object:remove()
  335. after(1, function() core.check_for_falling(np) end)
  336. return
  337. end
  338. local vel = self.object:get_velocity()
  339. if vector_equals(vel, {x = 0, y = 0, z = 0}) then
  340. local npos = self.object:get_pos()
  341. self.object:set_pos(vector_round(npos))
  342. end
  343. end
  344. })