api.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. -- Item shelf for generic objects
  2. -- Inventory overlay and blast code taken from vessels mod in MTG
  3. -- All other code by Zorman2000
  4. local function get_shelf_formspec(inv_size)
  5. return "size[8,7]"..
  6. default.gui_bg..
  7. default.gui_bg_img..
  8. default.gui_slots..
  9. "list[context;main;"..(math.abs((inv_size / 2) - 8) / 2)..",0.25;"..(inv_size / 2)..",2;]"..
  10. "list[current_player;main;0,2.75;8,1;]"..
  11. "list[current_player;main;0,4;8,3;8]"..
  12. "listring[context;main]"..
  13. "listring[current_player;main]"
  14. end
  15. local temp_texture
  16. local temp_size
  17. local function get_obj_dir(param2)
  18. return ((param2 + 1) % 4)
  19. end
  20. local function update_shelf(pos)
  21. -- Remove all objects
  22. local objs = minetest.get_objects_inside_radius(pos, 0.75)
  23. for _,obj in pairs(objs) do
  24. obj:remove()
  25. end
  26. local node = minetest.get_node(pos)
  27. local meta = minetest.get_meta(pos)
  28. -- Calculate directions
  29. local node_dir = minetest.facedir_to_dir(((node.param2 + 2) % 4))
  30. local obj_dir = minetest.facedir_to_dir(get_obj_dir(node.param2))
  31. -- Get maximum number of shown items (4 or 6)
  32. local max_shown_items = minetest.get_item_group(node.name, "itemshelf_shown_items")
  33. -- Get custom displacement properties
  34. local depth_displacement = meta:get_float("itemshelf:depth_displacement")
  35. local vertical_displacement = meta:get_float("itemshelf:vertical_displacement")
  36. if depth_displacement == 0 then
  37. depth_displacement = 0.25
  38. end
  39. if vertical_displacement == 0 then
  40. vertical_displacement = 0.2375
  41. end
  42. -- minetest.log("displacements: "..dump(depth_displacement)..", "..dump(vertical_displacement))
  43. -- Calculate the horizontal displacement. This one is hardcoded so that either 4 or 6
  44. -- items are properly displayed.
  45. local horizontal_displacement = 0.715
  46. if max_shown_items == 6 then
  47. horizontal_displacement = 0.555
  48. end
  49. -- Calculate initial position for entities
  50. -- local start_pos = {
  51. -- x=pos.x - (0.25 * obj_dir.x) - (node_dir.x * 0.25),
  52. -- y=pos.y + 0.25,
  53. -- z=pos.z - (0.25 * obj_dir.z) - (node_dir.z * 0.25)
  54. -- }
  55. -- How the below works: Following is a top view of a node
  56. -- | +z (N) 0
  57. -- |
  58. -- ------------------------
  59. -- | | |
  60. -- | | |
  61. -- | | |
  62. -- -x (W) 3 | | (0,0) | +x (E) 1
  63. -- -------------|-----------+----------|--------------
  64. -- | | |
  65. -- | | |
  66. -- | | |
  67. -- | | |
  68. -- ------------------------
  69. -- |
  70. -- | -z (S) 2
  71. -- From the picture above, your front could be at either -z, -z, x or z.
  72. -- To get the entity closer to the front, you need to add a certain amount
  73. -- (e.g. 0.25) to the x and z coordinates, and then multiply these by the
  74. -- the node direction (which is a vector pointing outwards of the node face).
  75. -- Therefore, start_pos is:
  76. local start_pos = {
  77. x=pos.x - (obj_dir.x * horizontal_displacement) + (node_dir.x * depth_displacement),
  78. y=pos.y + vertical_displacement,
  79. z=pos.z - (obj_dir.z * horizontal_displacement) + (node_dir.z * depth_displacement)
  80. }
  81. -- Calculate amount of objects in the inventory
  82. local inv = minetest.get_meta(pos):get_inventory()
  83. local list = inv:get_list("main")
  84. local obj_count = 0
  85. for key, itemstack in pairs(list) do
  86. if not itemstack:is_empty() then
  87. obj_count = obj_count + 1
  88. end
  89. end
  90. -- minetest.log("Found "..dump(obj_count).." items on shelf inventory")
  91. if obj_count > 0 then
  92. local shown_items = math.min(#list, max_shown_items)
  93. for i = 1, shown_items do
  94. local offset = i
  95. if i > (shown_items / 2) then
  96. offset = i - (shown_items / 2)
  97. end
  98. if i == ((shown_items / 2) + 1) then
  99. start_pos.y = start_pos.y - 0.5125
  100. end
  101. local item_displacement = 0.475
  102. if shown_items == 6 then
  103. item_displacement = 0.2775
  104. end
  105. local obj_pos = {
  106. x=start_pos.x + (item_displacement * offset * obj_dir.x), --- (node_dir.z * overhead * 0.25),
  107. y=start_pos.y,
  108. z=start_pos.z + (item_displacement * offset * obj_dir.z) --- (node_dir.x * overhead * 0.25)
  109. }
  110. if not list[i]:is_empty() then
  111. -- minetest.log("Adding item entity at "..minetest.pos_to_string(obj_pos))
  112. temp_texture = list[i]:get_name()
  113. temp_size = 0.8/max_shown_items
  114. --minetest.log("Size: "..dump(temp_size))
  115. local ent = minetest.add_entity(obj_pos, "itemshelf:item")
  116. ent:set_properties({
  117. wield_item = temp_texture,
  118. visual_size = {x = 0.8/max_shown_items, y = 0.8/max_shown_items}
  119. })
  120. ent:set_yaw(minetest.dir_to_yaw(minetest.facedir_to_dir(node.param2)))
  121. end
  122. end
  123. end
  124. end
  125. itemshelf = {}
  126. -- Definable properties:
  127. -- - description
  128. -- - textures (if drawtype is nodebox)
  129. -- - nodebox (like default minetest.register_node def)
  130. -- - mesh (like default minetest.register_node def)
  131. -- - item capacity (how many items will fit into the shelf, use even numbers, max 16)
  132. -- - shown_items (how many items to show, will always show first (shown_items/2) items of each row, max 6)
  133. -- - `half-depth`: if set to true, will use different nodebox. Do not use with `depth_offset`
  134. -- - `vertical_offset`: starting position vertical displacement from the center of the node
  135. -- - `depth_offset`: starting position depth displacement from the center of the node
  136. function itemshelf.register_shelf(name, def)
  137. -- Determine drawtype
  138. local drawtype = "nodebox"
  139. if def.mesh then
  140. drawtype = "mesh"
  141. end
  142. minetest.register_node("itemshelf:"..name, {
  143. description = def.description,
  144. tiles = def.textures,
  145. paramtype = "light",
  146. paramtype2 = "facedir",
  147. drawtype = drawtype,
  148. node_box = def.nodebox,
  149. mesh = def.mesh,
  150. groups = def.groups or {choppy = 2, itemshelf = 1, itemshelf_shown_items = def.shown_items or 4},
  151. on_construct = function(pos)
  152. -- Initialize inventory
  153. local meta = minetest.get_meta(pos)
  154. local inv = meta:get_inventory()
  155. inv:set_size("main", def.capacity or 4)
  156. -- Initialize formspec
  157. meta:set_string("formspec", get_shelf_formspec(def.capacity or 4))
  158. -- If given half_depth, initialize the displacement
  159. if def.half_depth == true then
  160. meta:set_float("itemshelf:depth_displacement", -0.1475)
  161. end
  162. -- Initialize custom displacements if defined
  163. if def.vertical_offset then
  164. meta:set_float("itemshelf:vertical_displacement", def.vertical_offset)
  165. end
  166. if def.depth_offset then
  167. meta:set_float("itemshelf:depth_displacement", def.depth_offset)
  168. end
  169. end,
  170. -- allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  171. -- if minetest.get_item_group(stack:get_name(), "music_disc") ~= 0 then
  172. -- return stack:get_count()
  173. -- end
  174. -- return 0
  175. -- end,
  176. allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  177. if minetest.is_protected(pos, player:get_player_name()) then
  178. return 0
  179. else
  180. return count
  181. end
  182. end,
  183. allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  184. if minetest.is_protected(pos, player:get_player_name()) then
  185. return 0
  186. else
  187. return stack:get_count()
  188. end
  189. end,
  190. allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  191. if minetest.is_protected(pos, player:get_player_name()) then
  192. return 0
  193. else
  194. return stack:get_count()
  195. end
  196. end,
  197. on_metadata_inventory_put = update_shelf,
  198. on_metadata_inventory_take = update_shelf,
  199. on_dig = function(pos, node, digger)
  200. -- Clear any object disc
  201. local objs = minetest.get_objects_inside_radius(pos, 0.7)
  202. for _,obj in pairs(objs) do
  203. obj:remove()
  204. end
  205. -- Pop-up items
  206. minetest.add_item(pos, node.name)
  207. local meta = minetest.get_meta(pos)
  208. local list = meta:get_inventory():get_list("main")
  209. for _,item in pairs(list) do
  210. local drop_pos = {
  211. x=math.random(pos.x - 0.5, pos.x + 0.5),
  212. y=pos.y,
  213. z=math.random(pos.z - 0.5, pos.z + 0.5)}
  214. minetest.add_item(pos, item:to_string())
  215. end
  216. -- Remove node
  217. minetest.remove_node(pos)
  218. end,
  219. on_blast = function(pos)
  220. minetest.add_item(pos, minetest.get_node(pos).name)
  221. local meta = minetest.get_meta(pos)
  222. local list = meta:get_inventory():get_list("main")
  223. for _,item in pairs(list) do
  224. local drop_pos = {
  225. x=math.random(pos.x - 0.5, pos.x + 0.5),
  226. y=pos.y,
  227. z=math.random(pos.z - 0.5, pos.z + 0.5)}
  228. minetest.add_item(pos, item:to_string())
  229. end
  230. -- Remove node
  231. minetest.remove_node(pos)
  232. return nil
  233. end,
  234. -- Screwdriver support
  235. on_rotate = function(pos, node, user, mode, new_param2) --{name = node.name, param1 = node.param1, param2 = node.param2}, user, mode, new_param2)
  236. -- Rotate
  237. node.param2 = new_param2
  238. minetest.swap_node(pos, node)
  239. update_shelf(pos)
  240. -- Disable rotation by screwdriver
  241. return false
  242. end,
  243. })
  244. end
  245. -- Entity for shelf
  246. minetest.register_entity("itemshelf:item", {
  247. hp_max = 1,
  248. visual = "wielditem",
  249. visual_size = {x = 0.20, y = 0.20},
  250. collisionbox = {0,0,0, 0,0,0},
  251. physical = false,
  252. on_activate = function(self, staticdata)
  253. -- Staticdata
  254. local data = {}
  255. if staticdata ~= nil and staticdata ~= "" then
  256. local cols = string.split(staticdata, "|")
  257. data["itemstring"] = cols[1]
  258. data["visualsize"] = tonumber(cols[2])
  259. end
  260. -- Texture
  261. if temp_texture ~= nil then
  262. -- Set texture from temp
  263. self.itemstring = temp_texture
  264. temp_texture = nil
  265. elseif staticdata ~= nil and staticdata ~= "" then
  266. -- Set texture from static data
  267. self.itemstring = data.itemstring
  268. end
  269. -- Set texture if available
  270. if self.itemstring ~= nil then
  271. self.wield_item = self.itemstring
  272. end
  273. -- Visual size
  274. if temp_size ~= nil then
  275. self.visualsize = temp_size
  276. temp_size = nil
  277. elseif staticdata ~= nil and staticdata ~= "" then
  278. self.visualsize = data.visualsize
  279. end
  280. -- Set visual size if available
  281. if self.visualsize ~= nil then
  282. self.visual_size = {x=self.visualsize, y=self.visualsize}
  283. end
  284. -- Set object properties
  285. self.object:set_properties(self)
  286. end,
  287. get_staticdata = function(self)
  288. local result = ""
  289. if self.itemstring ~= nil then
  290. result = self.itemstring.."|"
  291. end
  292. if self.visualsize ~= nil then
  293. result = result..self.visualsize
  294. end
  295. return result
  296. end,
  297. })