bat2.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. bat2 = bat2 or {}
  2. bat2_lv = bat2_lv or {}
  3. bat2_mv = bat2_mv or {}
  4. bat2_hv = bat2_hv or {}
  5. -- Localize for performance.
  6. local math_floor = math.floor
  7. for k, v in ipairs({
  8. {tier="lv", up="LV", name="LV", buffer=tech.battery_lv.buffer},
  9. {tier="mv", up="MV", name="MV", buffer=tech.battery_mv.buffer},
  10. {tier="hv", up="HV", name="HV", buffer=tech.battery_hv.buffer},
  11. }) do
  12. -- Which function table are we operating on?
  13. local functable = _G["bat2_" .. v.tier]
  14. functable.do_battery_decay =
  15. function(meta)
  16. local timeout = false
  17. local time0 = meta:get_int("decay")
  18. local time1 = os.time()
  19. if time1 > (time0 + 60*60*24*3) then
  20. -- Note: we do not add 72 hours to the decay timeout unless a battery cell
  21. -- actually decays. This means that if we don't decay a battery after 72
  22. -- hours, we'll try again every hour after that until we do.
  23. meta:set_int("decay", (time0 + 60*60*24*3+60*60))
  24. timeout = true
  25. end
  26. -- The battery is considered "full" (and 'stressed') when over 90% usage.
  27. local maxstorage = false
  28. local chg, max = functable.get_energy_status(meta)
  29. if (chg / max) > 0.9 then
  30. maxstorage = true
  31. end
  32. -- Decay happens slowly while the battery bank is holding the max amount
  33. -- of energy. This sort of simulates batteries becoming weaker over time
  34. -- and needing replacing.
  35. if timeout and maxstorage and math.random(1, 20) == 1 then
  36. local inv = meta:get_inventory()
  37. local size = inv:get_size("batteries")
  38. local idx = math.random(1, size)
  39. local stack = inv:get_stack("batteries", idx)
  40. if stack:get_name() == "battery:battery" then
  41. inv:set_stack("batteries", idx, ItemStack("battery:battery_broken"))
  42. -- Update decay timeout value.
  43. meta:set_int("decay", time1)
  44. end
  45. end
  46. end
  47. functable.on_energy_put =
  48. function(pos, energy)
  49. --minetest.chat_send_all("# Server: Got " .. energy .. " energy!")
  50. local meta = minetest.get_meta(pos)
  51. local chg, max = functable.get_energy_status(meta)
  52. local canfit = max - chg
  53. local toput = energy
  54. if toput > canfit then
  55. toput = canfit
  56. end
  57. local total = chg + toput
  58. meta:set_int("energy", total)
  59. energy = energy - toput
  60. if toput > 0 then
  61. functable.trigger_update(pos)
  62. end
  63. return energy
  64. end
  65. functable.on_energy_get =
  66. function(pos, energy)
  67. local meta = minetest.get_meta(pos)
  68. local have = meta:get_int("energy")
  69. if have < energy then
  70. meta:set_int("energy", 0)
  71. if have > 0 then
  72. functable.trigger_update(pos)
  73. end
  74. return have
  75. end
  76. have = have - energy
  77. meta:set_int("energy", have)
  78. if energy > 0 then
  79. functable.trigger_update(pos)
  80. end
  81. return energy
  82. end
  83. functable.compose_infotext =
  84. function(pos)
  85. local meta = minetest.get_meta(pos)
  86. local inv = meta:get_inventory()
  87. local chg, max = functable.get_energy_status(meta)
  88. local size = inv:get_size("batteries")
  89. local cnt = functable.get_battery_count(inv)
  90. local infotext = v.up .. " Battery Array\n" ..
  91. "Internal Battery Units: " .. cnt .. "/" .. size .. "\n" ..
  92. "Energy: " .. chg .. "/" .. max .. " EUs\n"
  93. if max > 0 then
  94. local percent = math_floor(chg / max * 100)
  95. infotext = infotext .. "Charge: " .. percent .. "%"
  96. else
  97. infotext = infotext .. "Charge: 0%"
  98. end
  99. meta:set_string("infotext", infotext)
  100. end
  101. functable.compose_formspec =
  102. function(pos)
  103. local meta = minetest.get_meta(pos)
  104. local chg, max = functable.get_energy_status(meta)
  105. local charge_desc = v.name .. " Charge Status: " ..
  106. chg .. "/" .. max .. " EUs"
  107. local formspec =
  108. "size[8,8.5]" ..
  109. default.formspec.get_form_colors() ..
  110. default.formspec.get_form_image() ..
  111. default.formspec.get_slot_colors() ..
  112. "label[0,0;" .. minetest.formspec_escape(charge_desc) .. "]" ..
  113. "label[0,0.5;NRAIB (Non-Redundant Array of Independant Batteries)]" ..
  114. "item_image[7,0;1,1;battery:battery]" ..
  115. "list[context;batteries;0,1;8,2;]" ..
  116. "list[current_player;main;0,4.25;8,1;]" ..
  117. "list[current_player;main;0,5.5;8,3;8]" ..
  118. "listring[context;batteries]"..
  119. "listring[current_player;main]"..
  120. default.get_hotbar_bg(0, 4.25)
  121. meta:set_string("formspec", formspec)
  122. end
  123. functable.update_charge_visual =
  124. function(pos)
  125. local meta = minetest.get_meta(pos)
  126. local chg, max = functable.get_energy_status(meta)
  127. local name = "bat2:bt0_" .. v.tier
  128. if max > 0 then -- Avoid divide-by-zero.
  129. local percent = math_floor((chg / max) * 100)
  130. local sz = math.ceil(100 / 12)
  131. for i = 0, 12, 1 do
  132. if percent <= sz*i then
  133. name = "bat2:bt" .. i .. "_" .. v.tier
  134. break
  135. end
  136. end
  137. end
  138. machines.swap_node(pos, name)
  139. end
  140. functable.on_punch =
  141. function(pos, node, puncher, pointed_thing)
  142. functable.trigger_update(pos)
  143. functable.privatize(minetest.get_meta(pos))
  144. end
  145. functable.can_dig =
  146. function(pos, player)
  147. local meta = minetest.get_meta(pos)
  148. local inv = meta:get_inventory()
  149. return inv:is_empty("batteries")
  150. end
  151. functable.allow_metadata_inventory_put =
  152. function(pos, listname, index, stack, player)
  153. local NBATT = "battery:battery"
  154. local pname = player:get_player_name()
  155. if minetest.test_protection(pos, pname) then
  156. return 0
  157. end
  158. if stack:get_name() == NBATT then
  159. return stack:get_count()
  160. end
  161. return 0
  162. end
  163. functable.allow_metadata_inventory_move =
  164. function(pos, from_list, from_index, to_list, to_index, count, player)
  165. local meta = minetest.get_meta(pos)
  166. local inv = meta:get_inventory()
  167. local stack = inv:get_stack(from_list, from_index)
  168. return functable.allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
  169. end
  170. functable.allow_metadata_inventory_take =
  171. function(pos, listname, index, stack, player)
  172. local pname = player:get_player_name()
  173. if minetest.test_protection(pos, pname) then
  174. return 0
  175. end
  176. return stack:get_count()
  177. end
  178. functable.get_battery_count =
  179. function(inv)
  180. local batteries = inv:get_list("batteries")
  181. local count = 0
  182. for k, v in ipairs(batteries) do
  183. if v:get_name() == "battery:battery" then
  184. -- Only 1 battery allowed per stack.
  185. count = count + 1
  186. end
  187. end
  188. return count
  189. end
  190. functable.update_maximum_charge =
  191. function(meta)
  192. local count = functable.get_battery_count(meta:get_inventory())
  193. local max
  194. if v.tier == "lv" then
  195. max = count * v.buffer
  196. elseif v.tier == "mv" then
  197. max = count * v.buffer
  198. elseif v.tier == "hv" then
  199. max = count * v.buffer
  200. end
  201. local chg = meta:get_int("energy")
  202. -- Ensure charge isn't over max. This can happen if user removed a battery.
  203. if chg > max then
  204. meta:set_int("energy", max)
  205. end
  206. meta:set_int("max", max)
  207. end
  208. functable.on_timer =
  209. function(pos, elapsed)
  210. local meta = minetest.get_meta(pos)
  211. local current_amount = meta:get_int("energy")
  212. local old_eu_amount = meta:get_int("old_eu")
  213. -- Todo: here, we can respond to changes in EU amount since last update.
  214. meta:set_int("old_eu", current_amount)
  215. -- Needed in case the operator removes or adds a battery.
  216. -- Also, EUs can be added/drained from batteries without going through a distributer.
  217. functable.do_battery_decay(meta)
  218. functable.update_maximum_charge(meta)
  219. functable.update_charge_visual(pos)
  220. functable.compose_infotext(pos)
  221. functable.compose_formspec(pos)
  222. -- Run timer again, to expire in 1 hour.
  223. -- If the machine needs an update sooner, the timer will be restarted with
  224. -- a quicker timeout.
  225. local timer = minetest.get_node_timer(pos)
  226. timer:start(60*60)
  227. end
  228. functable.on_blast =
  229. function(pos)
  230. local drops = {}
  231. default.get_inventory_drops(pos, "batteries", drops)
  232. drops[#drops+1] = "bat2:bt0_" .. v.tier
  233. minetest.remove_node(pos)
  234. return drops
  235. end
  236. functable.on_construct =
  237. function(pos)
  238. local meta = minetest.get_meta(pos)
  239. local inv = meta:get_inventory()
  240. inv:set_size("batteries", 8*2)
  241. functable.update_maximum_charge(meta)
  242. functable.compose_infotext(pos)
  243. functable.compose_formspec(pos)
  244. meta:set_string("nodename", "DUMMY")
  245. meta:set_string("owner", "DUMMY")
  246. meta:set_int("energy", 0)
  247. meta:set_int("old_eu", 0)
  248. meta:set_int("max", 0)
  249. functable.privatize(meta)
  250. end
  251. functable.privatize =
  252. function(meta)
  253. meta:mark_as_private({
  254. "nodename",
  255. "owner",
  256. "energy",
  257. "old_eu",
  258. "max",
  259. "decay",
  260. })
  261. end
  262. functable.on_destruct =
  263. function(pos)
  264. local meta = minetest.get_meta(pos)
  265. net2.clear_caches(pos, meta:get_string("owner"), v.tier)
  266. nodestore.del_node(pos)
  267. end
  268. functable.after_place_node =
  269. function(pos, placer, itemstack, pointed_thing)
  270. local meta = minetest.get_meta(pos)
  271. local owner = placer:get_player_name()
  272. meta:set_string("nodename", minetest.get_node(pos).name)
  273. meta:set_string("owner", owner)
  274. net2.clear_caches(pos, owner, v.tier)
  275. nodestore.add_node(pos)
  276. end
  277. functable.on_metadata_inventory_move =
  278. function(pos)
  279. functable.trigger_update(pos)
  280. end
  281. functable.on_metadata_inventory_put =
  282. function(pos)
  283. functable.trigger_update(pos)
  284. end
  285. functable.on_metadata_inventory_take =
  286. function(pos)
  287. functable.trigger_update(pos)
  288. end
  289. functable.trigger_update =
  290. function(pos)
  291. -- Restart timer without new timeout even if already running.
  292. local timer = minetest.get_node_timer(pos)
  293. timer:start(1.0)
  294. end
  295. -- Read the current & max charge of the battery, but do not trigger any update.
  296. -- Function shall be used internally ONLY.
  297. functable.get_energy_status =
  298. function(meta)
  299. local chg = meta:get_int("energy")
  300. local max = meta:get_int("max")
  301. return chg, max
  302. end
  303. end
  304. if not bat2.run_once then
  305. local nodebox = {
  306. {0, 0, 0, 5, 16, 5},
  307. {11, 0, 0, 16, 16, 5},
  308. {0, 0, 11, 5, 16, 16},
  309. {11, 0, 11, 16, 16, 16},
  310. {1, 1, 1, 15, 15, 15},
  311. {0, 0, 0, 16, 1, 16},
  312. {0, 15, 0, 16, 16, 16},
  313. }
  314. local selectbox = {
  315. {0, 0, 0, 16, 16, 16},
  316. }
  317. utility.transform_nodebox(nodebox)
  318. utility.transform_nodebox(selectbox)
  319. for k, v in ipairs({
  320. {tier="lv", title="LV"},
  321. {tier="mv", title="MV"},
  322. {tier="hv", title="HV"},
  323. }) do
  324. -- Register 13 nodes for each tier; each node has a different texture set to show the charge level.
  325. for i = 0, 12, 1 do
  326. -- Which function table are we operating on?
  327. local functable = _G["bat2_" .. v.tier]
  328. minetest.register_node(":bat2:bt" .. i .. "_" .. v.tier, {
  329. drawtype = "nodebox",
  330. description = v.title .. " Battery Box",
  331. tiles = {
  332. "technic_" .. v.tier .. "_battery_box_top.png",
  333. "technic_" .. v.tier .. "_battery_box_bottom.png",
  334. "technic_" .. v.tier .. "_battery_box_side.png^battery_meter" .. i .. ".png",
  335. "technic_" .. v.tier .. "_battery_box_side.png^battery_meter" .. i .. ".png",
  336. "technic_" .. v.tier .. "_battery_box_side.png^battery_meter" .. i .. ".png",
  337. "technic_" .. v.tier .. "_battery_box_side.png^battery_meter" .. i .. ".png",
  338. },
  339. groups = utility.dig_groups("machine"),
  340. paramtype = "light",
  341. paramtype2 = "facedir",
  342. is_ground_content = false,
  343. sounds = default.node_sound_metal_defaults(),
  344. drop = "bat2:bt0_" .. v.tier,
  345. node_box = {
  346. type = "fixed",
  347. fixed = nodebox,
  348. },
  349. selection_box = {
  350. type = "fixed",
  351. fixed = selectbox,
  352. },
  353. on_energy_put = function(...)
  354. return functable.on_energy_put(...) end,
  355. on_energy_get = function(...)
  356. return functable.on_energy_get(...) end,
  357. on_punch = function(...)
  358. return functable.on_punch(...) end,
  359. can_dig = function(...)
  360. return functable.can_dig(...) end,
  361. on_timer = function(...)
  362. return functable.on_timer(...) end,
  363. on_construct = function(...)
  364. return functable.on_construct(...) end,
  365. on_destruct = function(...)
  366. return functable.on_destruct(...) end,
  367. after_place_node = function(...)
  368. return functable.after_place_node(...) end,
  369. on_metadata_inventory_move = function(...)
  370. return functable.on_metadata_inventory_move(...) end,
  371. on_metadata_inventory_put = function(...)
  372. return functable.on_metadata_inventory_put(...) end,
  373. on_metadata_inventory_take = function(...)
  374. return functable.on_metadata_inventory_take(...) end,
  375. on_blast = function(...)
  376. return functable.on_blast(...) end,
  377. on_rotate = function(...)
  378. return screwdriver.rotate_simple(...) end,
  379. allow_metadata_inventory_put = function(...)
  380. return functable.allow_metadata_inventory_put(...) end,
  381. allow_metadata_inventory_move = function(...)
  382. return functable.allow_metadata_inventory_move(...) end,
  383. allow_metadata_inventory_take = function(...)
  384. return functable.allow_metadata_inventory_take(...) end,
  385. })
  386. end
  387. end
  388. local c = "bat2:core"
  389. local f = battery.modpath .. "/bat2.lua"
  390. reload.register_file(c, f, false)
  391. bat2.run_once = true
  392. end