init.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. if not minetest.global_exists("falling") then falling = {} end
  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 100, 100
  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*500, 20*500
  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 = 1},
  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. utility.damage_player(r, "crush", pharm)
  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. tool_capabilities.damage_groups.fleshy = mharm
  111. r:punch(r, 1, tool_capabilities, nil)
  112. elseif l.name == "__builtin:item" then
  113. droplift.invoke(r)
  114. end
  115. end
  116. end
  117. end
  118. end
  119. local function node_not_walkable(pos)
  120. local nn = get_node(pos).name
  121. if nn == "air" then return true end
  122. local def = all_nodes[nn]
  123. if def and not def.walkable then return true end
  124. end
  125. local adjacency = {
  126. {x=0, y=0, z=0},
  127. {x=0, y=0, z=0},
  128. {x=0, y=0, z=0},
  129. {x=0, y=0, z=0},
  130. }
  131. local function outof_bounds(pos)
  132. if pos.z < -30912 then
  133. return true
  134. end
  135. if pos.z > 30927 then
  136. return true
  137. end
  138. if pos.x > 30927 then
  139. return true
  140. end
  141. if pos.x < -30912 then
  142. return true
  143. end
  144. return false
  145. end
  146. local find_slope = function(pos)
  147. adjacency[1].x=pos.x-1 adjacency[1].y=pos.y adjacency[1].z=pos.z
  148. adjacency[2].x=pos.x+1 adjacency[2].y=pos.y adjacency[2].z=pos.z
  149. adjacency[3].x=pos.x adjacency[3].y=pos.y adjacency[3].z=pos.z+1
  150. adjacency[4].x=pos.x adjacency[4].y=pos.y adjacency[4].z=pos.z-1
  151. local targets = {}
  152. for i = 1, 4 do
  153. local p = adjacency[i]
  154. if node_not_walkable(p) then
  155. p.y = p.y + 1
  156. if node_not_walkable(p) and not outof_bounds(p) then
  157. targets[#targets+1] = {x=p.x, y=p.y-1, z=p.z}
  158. end
  159. p.y = p.y - 1
  160. end
  161. end
  162. if #targets == 0 then
  163. return nil
  164. end
  165. return targets[random(1, #targets)]
  166. end
  167. -- Shall return true if a hypothetical falling node spawned at this position
  168. -- would find a slope to fall down (or would fall straight down). In other words,
  169. -- return false if that falling node, if spawned, would immediately turn back to
  170. -- a solid node without moving.
  171. function falling.could_fall_here(pos)
  172. local d = vector.add(pos, {x=0, y=-1, z=0})
  173. if outof_bounds(d) then
  174. return false
  175. end
  176. if node_not_walkable(d) then
  177. return true
  178. end
  179. if find_slope(d) then
  180. return true
  181. end
  182. return false
  183. end
  184. minetest.register_entity(":__builtin:falling_node", {
  185. initial_properties = {
  186. visual = "wielditem",
  187. visual_size = {x = 0.667, y = 0.667},
  188. textures = {},
  189. physical = true,
  190. is_visible = false,
  191. collide_with_objects = false,
  192. collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
  193. },
  194. node = {},
  195. meta = {},
  196. -- Warning: 'meta' sometimes contains userdata from the engine, or builtin.
  197. set_node = function(self, node, meta)
  198. -- 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.
  199. if node.name == "default:snow" then
  200. if not snow.is_visible() then
  201. self.object:remove()
  202. return
  203. end
  204. end
  205. self.node = node
  206. self.meta = meta or {}
  207. -- If we got userdata meta, convert to table form.
  208. if type(meta.to_table) == "function" then
  209. meta = meta:to_table()
  210. end
  211. for _, list in pairs(meta.inventory or {}) do
  212. for i, stack in pairs(list) do
  213. if type(stack) == "userdata" then
  214. list[i] = stack:to_string()
  215. end
  216. end
  217. end
  218. self.object:set_properties({
  219. is_visible = true,
  220. textures = {node.name},
  221. })
  222. self.pharm, self.mharm = node_harm(node.name)
  223. self.sound = node_sound(node.name)
  224. --minetest.log("TEST1: " .. dump(self.meta))
  225. end,
  226. get_staticdata = function(self)
  227. local ds = {
  228. node = self.node,
  229. meta = self.meta,
  230. pharm = self.pharm,
  231. mharm = self.mharm,
  232. sound = self.sound,
  233. }
  234. --minetest.log("TEST2: " .. dump(ds))
  235. return minetest.serialize(ds)
  236. end,
  237. on_activate = function(self, staticdata)
  238. self.object:set_armor_groups({immortal = 1})
  239. local pos = self.object:get_pos()
  240. if outof_bounds(pos) then
  241. self.object:remove()
  242. return
  243. end
  244. local ds = minetest.deserialize(staticdata)
  245. if ds and ds.node then
  246. self:set_node(ds.node, ds.meta)
  247. elseif ds then
  248. self:set_node(ds)
  249. elseif staticdata ~= "" then
  250. self:set_node({name = staticdata})
  251. end
  252. end,
  253. on_step = function(self, dtime)
  254. -- Set gravity
  255. local acceleration = self.object:get_acceleration()
  256. if not vector_equals(acceleration, {x = 0, y = -8, z = 0}) then
  257. self.object:set_acceleration({x = 0, y = -8, z = 0})
  258. end
  259. -- Turn to actual node when colliding with ground, or continue to move
  260. local pos = self.object:get_pos()
  261. -- Position of bottom center point
  262. local bcp = vector_round({x = pos.x, y = pos.y - 0.7, z = pos.z})
  263. -- Avoid bugs caused by an unloaded node below
  264. local bcn = get_node_or_nil(bcp)
  265. local bcd = bcn and all_nodes[bcn.name]
  266. if bcn and (not bcd or bcd.walkable or (get_item_group(self.node.name, "float") ~= 0 and bcd.liquidtype ~= "none")) then
  267. if bcd and bcd.leveled and bcn.name == self.node.name then
  268. local addlevel = self.node.level
  269. if not addlevel or addlevel <= 0 then
  270. addlevel = bcd.leveled
  271. end
  272. if add_node_level(bcp, addlevel) == 0 then
  273. self.object:remove()
  274. return
  275. end
  276. elseif bcd and bcd.buildable_to and (get_item_group(self.node.name, "float") == 0 or bcd.liquidtype == "none") then
  277. remove_node(bcp)
  278. return
  279. end
  280. -- We have hit the ground. Check for a possible slope which we can continue to fall down.
  281. local ss = find_slope(bcp)
  282. if ss ~= nil then
  283. self.object:set_pos(vector_add(ss, {x=0, y=1, z=0}))
  284. self.object:set_velocity({x=0, y=0, z=0})
  285. entity_physics(bcp, self.node, self.pharm, self.mharm)
  286. ambiance.sound_play("default_gravel_footstep", ss, 0.2, 20)
  287. return
  288. end
  289. local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
  290. local protected = nil
  291. -- Check what's here.
  292. local n2 = get_node(np)
  293. local nd = all_nodes[n2.name]
  294. local nodedef = all_nodes[self.node.name]
  295. if nodedef then
  296. -- If not merely replacing air, or the nodetype is `buildable_to', then check protection.
  297. if n2.name ~= "air" or nodedef.buildable_to then
  298. protected = minetest.test_protection(np, "")
  299. end
  300. -- If it's not air and not liquid (and not protected), remove node and replace it with it's drops.
  301. if not protected and n2.name ~= "air" and (not nd or nd.liquidtype == "none") then
  302. remove_node(np)
  303. if nd.buildable_to == false then
  304. -- Add dropped items.
  305. -- Pass node name, because passing a node table gives wrong results.
  306. local drops = get_node_drops(n2.name, "")
  307. for _, dropped_item in pairs(drops) do
  308. add_item(np, dropped_item)
  309. end
  310. end
  311. -- Run script hook
  312. for _, callback in pairs(core.registered_on_dignodes) do
  313. callback(np, n2)
  314. end
  315. end
  316. -- Create node and remove entity.
  317. if not protected or n2.name == "air" or n2.name == "default:snow" or n2.name == "snow:footprints" then
  318. if protected and nodedef.buildable_to then
  319. --minetest.chat_send_player("MustTest", "# Server: Testing 1!")
  320. -- If the position is protected and the node we're placing is `buildable_to',
  321. -- then we must drop an item instead in order to avoid creating a protection exploit,
  322. -- even though we'd normally be placing into air.
  323. local callback = nodedef.on_collapse_to_entity
  324. if callback then
  325. local drops = callback(np, self.node)
  326. if drops then
  327. for k, v in ipairs(drops) do
  328. minetest.add_item(np, v)
  329. end
  330. end
  331. else
  332. add_item(np, self.node)
  333. end
  334. else
  335. --minetest.chat_send_player("MustTest", "# Server: Testing 2!")
  336. -- We're either placing into air, or crushing something that isn't protected.
  337. add_node(np, self.node)
  338. if self.meta then
  339. local meta = get_meta(np)
  340. meta:from_table(self.meta)
  341. end
  342. entity_physics(np, self.node, self.pharm, self.mharm)
  343. if self.sound then
  344. ambiance.sound_play(self.sound, np, 1.3, 20)
  345. end
  346. -- Mark node as unprotectable.
  347. -- This has to come before executing the node callback because the callback might remove the node.
  348. -- If the callback changes the node placed, it should use `minetest.swap_node()'.
  349. local meta = get_meta(np)
  350. meta:set_int("protection_cancel", 1)
  351. meta:mark_as_private("protection_cancel")
  352. -- Execute node callback.
  353. local callback = nodedef.on_finish_collapse
  354. if callback then
  355. callback(np, self.node)
  356. end
  357. -- Dirtspread notification.
  358. dirtspread.on_environment(np)
  359. end
  360. else
  361. -- Not air and protected, so we drop as entity instead.
  362. --minetest.chat_send_player("MustTest", "# Server: Testing 3!")
  363. local callback = nodedef.on_collapse_to_entity
  364. if callback then
  365. callback(np, self.node)
  366. else
  367. add_item(np, self.node)
  368. end
  369. end
  370. end
  371. self.object:remove()
  372. after(1, function() core.check_for_falling(np) end)
  373. return
  374. end
  375. local vel = self.object:get_velocity()
  376. if vector_equals(vel, {x = 0, y = 0, z = 0}) then
  377. local npos = self.object:get_pos()
  378. self.object:set_pos(vector_round(npos))
  379. end
  380. end
  381. })
  382. -- Copied from builtin so I can fix the behavior.
  383. local function convert_to_falling_node(pos, node)
  384. local obj = core.add_entity(pos, "__builtin:falling_node")
  385. if not obj then
  386. return false
  387. end
  388. ambiance.particles_on_dig(pos, node)
  389. local def = core.registered_nodes[node.name]
  390. if def and def.sounds and def.sounds.fall then
  391. core.sound_play(def.sounds.fall, {pos = pos}, true)
  392. end
  393. -- Execute node callback PRIOR to dropping the node, incase it needs to clean stuff up.
  394. -- Also make sure to call this BEFORE we get the node's meta, because the callback can change it.
  395. if def and def._on_pre_fall then
  396. def._on_pre_fall(pos)
  397. end
  398. -- remember node level, the entities' set_node() uses this
  399. node.level = core.get_node_level(pos)
  400. local meta = core.get_meta(pos)
  401. local metatable = meta and meta:to_table() or {}
  402. -- 'metatable' must be in table form, WITHOUT userdata.
  403. obj:get_luaentity():set_node(node, metatable)
  404. core.remove_node(pos)
  405. return true, obj
  406. end
  407. -- Copied from builtin so I can fix the behavior.
  408. function core.spawn_falling_node(pos)
  409. local node = core.get_node(pos)
  410. if node.name == "air" or node.name == "ignore" then
  411. return false
  412. end
  413. if string.find(node.name, "flowing") then
  414. -- Do not treat flowing liquid as a falling node. Looks ugly.
  415. return false
  416. end
  417. if minetest.get_item_group(node.name, "immovable") ~= 0 then
  418. return false
  419. end
  420. return convert_to_falling_node(pos, node)
  421. end
  422. local function highlight_position(pos)
  423. utility.original_add_particle({
  424. pos = pos,
  425. velocity = {x=0, y=0, z=0},
  426. acceleration = {x=0, y=0, z=0},
  427. expirationtime = 1.5,
  428. size = 4,
  429. collisiondetection = false,
  430. vertical = false,
  431. texture = "heart.png",
  432. })
  433. end
  434. -- Copied from builtin so I can fix the behavior.
  435. function core.check_single_for_falling(p)
  436. local n = core.get_node(p)
  437. if core.get_item_group(n.name, "falling_node") ~= 0 then
  438. local p_bottom = vector.offset(p, 0, -1, 0)
  439. -- Only spawn falling node if node below is loaded
  440. local n_bottom = core.get_node_or_nil(p_bottom)
  441. local d_bottom = n_bottom and core.registered_nodes[n_bottom.name]
  442. if d_bottom then
  443. local same = n.name == n_bottom.name
  444. -- Let leveled nodes fall if it can merge with the bottom node
  445. if same and d_bottom.paramtype2 == "leveled" and
  446. core.get_node_level(p_bottom) <
  447. core.get_node_max_level(p_bottom) then
  448. local success, _ = convert_to_falling_node(p, n)
  449. return success
  450. end
  451. -- Otherwise only if the bottom node is considered "fall through"
  452. if not same and
  453. (not d_bottom.walkable or d_bottom.buildable_to) and
  454. (core.get_item_group(n.name, "float") == 0 or
  455. d_bottom.liquidtype == "none") then
  456. local success, _ = convert_to_falling_node(p, n)
  457. return success
  458. end
  459. end
  460. end
  461. local ndef = minetest.registered_nodes[n.name]
  462. if not ndef or not ndef.groups then
  463. return false
  464. end
  465. local groups = ndef.groups
  466. -- These special groups are mutually exclusive and should not be used together.
  467. local an = groups.attached_node or 0
  468. if an ~= 0 then
  469. if not utility.check_attached_node(p, n, an) then
  470. utility.drop_attached_node(p)
  471. return true
  472. end
  473. end
  474. local hn = groups.hanging_node or 0
  475. if hn ~= 0 then
  476. if not utility.check_hanging_node(p, n, hn) then
  477. utility.drop_attached_node(p)
  478. return true
  479. end
  480. end
  481. local sn = groups.standing_node or 0
  482. if sn ~= 0 then
  483. if not utility.check_standing_node(p, n, sn) then
  484. utility.drop_attached_node(p)
  485. return true
  486. end
  487. end
  488. return false
  489. end