init.lua 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. -- Minetest: builtin/item.lua (override falling entity with new features)
  2. -- override falling nodes to add damage
  3. local function add_fall_damage(node, damage)
  4. if core.registered_nodes[node] then
  5. local group = core.registered_nodes[node].groups
  6. group.falling_node_damage = damage
  7. core.override_item(node, {groups = group})
  8. else
  9. print (node .. " not found to add falling_node_damage to")
  10. end
  11. end
  12. add_fall_damage("default:sand", 2)
  13. add_fall_damage("default:desert_sand", 2)
  14. add_fall_damage("default:silver_sand", 2)
  15. add_fall_damage("default:gravel", 3)
  16. add_fall_damage("caverealms:coal_dust", 3)
  17. add_fall_damage("tnt:tnt_burning", 4)
  18. --
  19. -- Falling stuff
  20. --
  21. local node_fall_hurt = core.settings:get_bool("node_fall_hurt") ~= false
  22. local delay = 0.1 -- used to simulate lag
  23. local gravity = core.settings:get("movement_gravity") or 9.81
  24. local function fall_hurt_check(self, pos)
  25. if self.hurt_toggle then
  26. -- Get damage level from falling_node_damage group
  27. local damage = core.registered_nodes[self.node.name] and
  28. core.registered_nodes[self.node.name].groups.falling_node_damage
  29. if damage then
  30. local all_objects = minetest.get_objects_inside_radius(pos, 0.8)
  31. for _,obj in ipairs(all_objects) do
  32. local name = obj:get_luaentity() and
  33. obj:get_luaentity().name or ""
  34. if name ~= "__builtin:item"
  35. and name ~= "__builtin:falling_node" then
  36. obj:punch(self.object, 4.0, {
  37. damage_groups = {fleshy = damage}
  38. })
  39. self.hurt_toggle = false
  40. end
  41. end
  42. end
  43. else
  44. self.hurt_toggle = true
  45. end
  46. end
  47. core.register_entity(":__builtin:falling_node", {
  48. initial_properties = {
  49. visual = "wielditem",
  50. visual_size = {x = 0.667, y = 0.667},
  51. textures = {},
  52. physical = true,
  53. is_visible = false,
  54. collide_with_objects = false,
  55. collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
  56. },
  57. set_node = function(self, node, meta)
  58. self.node = node
  59. meta = meta or {}
  60. self.hurt_toggle = true
  61. if type(meta.to_table) == "function" then
  62. meta = meta:to_table()
  63. end
  64. for _, list in pairs(meta.inventory or {}) do
  65. for i, stack in pairs(list) do
  66. if type(stack) == "userdata" then
  67. list[i] = stack:to_string()
  68. end
  69. end
  70. end
  71. local def = core.registered_nodes[node.name]
  72. if not def then
  73. -- Don't allow unknown nodes to fall
  74. core.log("warning", "Unknown falling node removed at "..
  75. core.pos_to_string(self.object:get_pos()))
  76. self.object:remove()
  77. return
  78. end
  79. self.meta = meta
  80. if def.drawtype == "airlike" then
  81. self.object:set_properties({
  82. is_visible = false,
  83. })
  84. elseif def.drawtype == "torchlike" or def.drawtype == "signlike" then
  85. local textures
  86. if def.tiles and def.tiles[1] then
  87. if def.drawtype == "torchlike" then
  88. textures = { "("..def.tiles[1]..")^[transformFX", def.tiles[1] }
  89. else
  90. textures = { def.tiles[1] }
  91. end
  92. end
  93. self.object:set_properties({
  94. is_visible = true,
  95. visual = "upright_sprite",
  96. visual_size = {x = 1, y = 1},
  97. textures = textures,
  98. glow = def.light_source,
  99. })
  100. else
  101. local itemstring = node.name
  102. if core.is_colored_paramtype(def.paramtype2) then
  103. itemstring = core.itemstring_with_palette(itemstring, node.param2)
  104. end
  105. self.object:set_properties({
  106. is_visible = true,
  107. wield_item = itemstring,
  108. glow = def.light_source,
  109. })
  110. end
  111. -- Rotate entity
  112. if def.drawtype == "torchlike" then
  113. self.object:set_yaw(math.pi*0.25)
  114. elseif (node.param2 ~= 0 and (def.wield_image == ""
  115. or def.wield_image == nil))
  116. or def.drawtype == "signlike" then
  117. if (def.paramtype2 == "facedir" or def.paramtype2 == "colorfacedir") then
  118. local fdir = (node.param2 or 1) % 32
  119. local face = fdir % 4
  120. local axis = fdir - face
  121. local pitch, yaw, roll
  122. if axis == 4 then
  123. pitch = (4 - face) * (math.pi/2) - math.pi/2
  124. yaw = math.pi/2
  125. roll = math.pi/2
  126. elseif axis == 8 then
  127. pitch = (4 - face) * (math.pi/2) - math.pi*1.5
  128. yaw = math.pi*1.5
  129. roll = math.pi/2
  130. elseif axis == 12 then
  131. pitch = (4 - face) * (math.pi/2)
  132. yaw = 0
  133. roll = math.pi/2
  134. elseif axis == 16 then
  135. pitch = (4 - face) * (math.pi/2) + math.pi
  136. yaw = math.pi
  137. roll = math.pi/2
  138. elseif axis == 20 then
  139. pitch = math.pi
  140. yaw = face * (math.pi/2) + math.pi
  141. roll = 0
  142. else
  143. pitch = 0
  144. yaw = (4 - face) * (math.pi/2)
  145. roll = 0
  146. end
  147. self.object:set_rotation({x = pitch, y = yaw, z = roll})
  148. elseif (def.paramtype2 == "wallmounted"
  149. or def.paramtype2 == "colorwallmounted") then
  150. local rot = (node.param2 or 1) % 8
  151. local pitch, yaw, roll = 0, 0, 0
  152. if rot == 1 then
  153. pitch, yaw = -math.pi, -math.pi
  154. elseif rot == 2 then
  155. pitch, yaw = math.pi/2, math.pi/2
  156. elseif rot == 3 then
  157. pitch, yaw = math.pi/2, math.pi*1.5
  158. elseif rot == 4 then
  159. pitch, yaw = math.pi/2, math.pi
  160. elseif rot == 5 then
  161. pitch, yaw = math.pi/2, 0
  162. end
  163. if def.drawtype == "signlike" then
  164. pitch = pitch - math.pi/2
  165. if rot >= 0 and rot <= 1 then
  166. roll = roll - math.pi/2
  167. end
  168. end
  169. self.object:set_rotation({x = pitch, y = yaw, z = roll})
  170. end
  171. end
  172. end,
  173. get_staticdata = function(self)
  174. return core.serialize({
  175. node = self.node,
  176. meta = self.meta
  177. })
  178. end,
  179. on_activate = function(self, staticdata)
  180. self.object:set_armor_groups({immortal = 1})
  181. self.object:set_acceleration({x = 0, y = -gravity, z = 0})
  182. local ds = core.deserialize(staticdata)
  183. if ds and ds.node then
  184. self:set_node(ds.node, ds.meta)
  185. elseif ds then
  186. self:set_node(ds)
  187. elseif staticdata ~= "" then
  188. self:set_node({name = staticdata})
  189. end
  190. end,
  191. on_step = function(self, dtime)
  192. -- used to simulate a little lag
  193. self.timer = (self.timer or 0) + dtime
  194. if self.timer < delay then
  195. return
  196. end
  197. self.timer = 0
  198. -- Set gravity and horizontal slowing
  199. self.object:set_acceleration({x = 0, y = -gravity, z = 0})
  200. local vel = self.object:get_velocity()
  201. vel.x = vel.x * 0.95
  202. vel.z = vel.z * 0.95
  203. if vel.x < 0.1 and vel.z < 0.1 then
  204. vel.x = 0
  205. vel.z = 0
  206. end
  207. self.object:set_velocity(vel)
  208. local pos = self.object:get_pos()
  209. -- Position of bottom center point
  210. local below_pos = {x = pos.x, y = pos.y - 0.7, z = pos.z}
  211. -- Check for player/mobs below falling node and hurt them >:D
  212. if node_fall_hurt then
  213. fall_hurt_check(self, below_pos)
  214. end
  215. -- check if falling node has custom function set
  216. local custom = core.registered_items[self.node.name]
  217. and core.registered_items[self.node.name].falling_step
  218. if custom and custom(self, pos, dtime + delay) == false then
  219. return -- skip further checks if false
  220. end
  221. -- Avoid bugs caused by an unloaded node below
  222. local below_node = core.get_node_or_nil(below_pos)
  223. -- Delete on contact with ignore at world edges or return if unloaded
  224. if not below_node then
  225. return
  226. elseif below_node.name == "ignore" then
  227. self.object:remove()
  228. return
  229. end
  230. local below_nodef = core.registered_nodes[below_node.name]
  231. -- Is it a level node we can add to?
  232. if below_nodef and below_nodef.leveled and
  233. below_node.name == self.node.name then
  234. local addlevel = self.node.level
  235. if not addlevel or addlevel <= 0 then
  236. addlevel = below_nodef.leveled
  237. end
  238. if core.add_node_level(below_pos, addlevel) == 0 then
  239. self.object:remove()
  240. return
  241. end
  242. end
  243. -- Stop node if it falls on walkable surface, or floats on water
  244. if (below_nodef and below_nodef.walkable == true)
  245. or (below_nodef
  246. and core.get_item_group(self.node.name, "float") ~= 0
  247. and below_nodef.liquidtype ~= "none") then
  248. self.object:set_velocity({x = 0, y = 0, z = 0})
  249. end
  250. -- Has the fallen node stopped moving ?
  251. if vector.equals(vel, {x = 0, y = 0, z = 0}) then
  252. local npos = self.object:get_pos()
  253. -- Get node we've landed inside
  254. local cnode = minetest.get_node(npos)
  255. local cdef = core.registered_nodes[cnode.name]
  256. -- If air_equivalent or buildable_to or an attached_node then place
  257. -- node, otherwise drop falling node as an item instead.
  258. if (cdef and cdef.air_equivalent == true)
  259. or (cdef and cdef.buildable_to == true)
  260. or (cdef and cdef.liquidtype ~= "none")
  261. or core.get_item_group(cnode.name, "attached_node") ~= 0 then
  262. -- Are we an attached node ? (grass, flowers, torch)
  263. if core.get_item_group(cnode.name, "attached_node") ~= 0 then
  264. -- Add drops from attached node
  265. local drops = core.get_node_drops(cnode.name, "")
  266. for _, dropped_item in pairs(drops) do
  267. core.add_item(npos, dropped_item)
  268. end
  269. -- Run script hook
  270. for _, callback in pairs(core.registered_on_dignodes) do
  271. callback(npos, cnode)
  272. end
  273. end
  274. -- Round position
  275. npos = vector.round(npos)
  276. -- Place falling entity as node and write any metadata
  277. core.add_node(npos, self.node)
  278. if self.meta then
  279. local meta = core.get_meta(npos)
  280. meta:from_table(self.meta)
  281. end
  282. -- Play placed sound
  283. local def = core.registered_nodes[self.node.name]
  284. if def.sounds and def.sounds.place and def.sounds.place.name then
  285. core.sound_play(def.sounds.place, {pos = npos})
  286. end
  287. -- Just incase we landed on other falling nodes
  288. core.check_for_falling(npos)
  289. else
  290. -- Add drops from falling node
  291. local drops = core.get_node_drops(self.node, "")
  292. for _, dropped_item in pairs(drops) do
  293. core.add_item(npos, dropped_item)
  294. end
  295. end
  296. -- Remove falling entity if it cannot be placed
  297. self.object:remove()
  298. end
  299. end
  300. })
  301. --[[
  302. core.override_item("default:gravel", {
  303. falling_step = function(self, pos, dtime)
  304. print ("Gravel falling!", dtime)
  305. end
  306. })
  307. ]]