init.lua 16 KB

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