tide.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. tide = tide or {}
  2. tide.modpath = minetest.get_modpath("machines")
  3. local BUFFER_SIZE = tech.tidal.buffer
  4. local ENERGY_AMOUNT = tech.tidal.power
  5. local function is_water(nn)
  6. if minetest.get_item_group(nn, "water") ~= 0 then
  7. return true
  8. end
  9. end
  10. -- Queued algorithm.
  11. local function count_nearby_ocean(startpos)
  12. local traversal = {}
  13. local queue = {}
  14. local curpos, hash, exists, name, found, depth
  15. local first = true
  16. local get_node_hash = minetest.hash_node_position
  17. local get_node = minetest.get_node
  18. local count = 0
  19. startpos.d = 1
  20. queue[#queue+1] = startpos
  21. ::continue::
  22. curpos = queue[#queue]
  23. queue[#queue] = nil
  24. depth = curpos.d
  25. curpos.d = nil
  26. hash = get_node_hash(curpos)
  27. exists = false
  28. if traversal[hash] then
  29. exists = true
  30. if depth >= traversal[hash] then
  31. goto next
  32. end
  33. end
  34. if depth >= 20 then
  35. goto next
  36. end
  37. name = get_node(curpos).name
  38. found = false
  39. if is_water(name) then
  40. found = true
  41. end
  42. if not found then
  43. goto next
  44. end
  45. traversal[hash] = depth
  46. if not exists then
  47. count = count + 1
  48. end
  49. -- Queue up adjacent locations.
  50. queue[#queue+1] = {x=curpos.x+1, y=curpos.y, z=curpos.z, d=depth+1}
  51. queue[#queue+1] = {x=curpos.x-1, y=curpos.y, z=curpos.z, d=depth+1}
  52. queue[#queue+1] = {x=curpos.x, y=curpos.y+1, z=curpos.z, d=depth+1}
  53. queue[#queue+1] = {x=curpos.x, y=curpos.y-1, z=curpos.z, d=depth+1}
  54. queue[#queue+1] = {x=curpos.x, y=curpos.y, z=curpos.z+1, d=depth+1}
  55. queue[#queue+1] = {x=curpos.x, y=curpos.y, z=curpos.z-1, d=depth+1}
  56. ::next::
  57. first = false
  58. if #queue > 0 then
  59. goto continue
  60. end
  61. return count
  62. end
  63. tide.on_energy_get =
  64. function(pos, energy)
  65. local meta = minetest.get_meta(pos)
  66. local inv = meta:get_inventory()
  67. local have = inv:get_stack("buffer", 1):get_count()
  68. if have < energy then
  69. inv:set_stack("buffer", 1, ItemStack(""))
  70. tide.trigger_update(pos)
  71. return have
  72. end
  73. have = have - energy
  74. inv:set_stack("buffer", 1, ItemStack("atomic:energy " .. have))
  75. tide.trigger_update(pos)
  76. return energy
  77. end
  78. tide.compose_formspec =
  79. function(pos)
  80. local formspec =
  81. "size[2,2.5]" ..
  82. default.gui_bg ..
  83. default.gui_bg_img ..
  84. default.gui_slots ..
  85. "label[0,0.5;Energy Buffer]" ..
  86. "list[context;buffer;0,1;1,1]"
  87. return formspec
  88. end
  89. tide.compose_infotext =
  90. function(pos)
  91. local meta = minetest.get_meta(pos)
  92. local eups = meta:get_int("eups")
  93. local state = "Standby"
  94. if meta:get_int("active") == 1 then
  95. state = "Active"
  96. end
  97. local infotext = "MV Tidal Generator (" .. state .. ")\n" ..
  98. "Output: " .. eups .. " EU Per/Sec"
  99. local err = meta:get_string("error")
  100. if err ~= "" and err ~= "DUMMY" then
  101. infotext = infotext .. "\n" .. err
  102. end
  103. return infotext
  104. end
  105. tide.trigger_update =
  106. function(pos)
  107. local timer = minetest.get_node_timer(pos)
  108. -- Restart timer even if already running.
  109. timer:start(1.0)
  110. end
  111. tide.on_punch =
  112. function(pos, node, puncher, pointed_thing)
  113. tide.trigger_update(pos)
  114. --minetest.get_meta(pos):set_int("chktmr", 0)
  115. --minetest.chat_send_player("MustTest", "TESTING!")
  116. tide.privatize(minetest.get_meta(pos))
  117. end
  118. tide.can_dig =
  119. function(pos, player)
  120. return true
  121. end
  122. tide.check_environment =
  123. function(pos, meta)
  124. local timer = meta:get_int("chktmr")
  125. local active = meta:get_int("active")
  126. if timer <= 0 then
  127. local result = false
  128. local good = false
  129. local eups = ENERGY_AMOUNT
  130. local sides = {
  131. {x=pos.x+1, y=pos.y, z=pos.z},
  132. {x=pos.x-1, y=pos.y, z=pos.z},
  133. {x=pos.x, y=pos.y, z=pos.z+1},
  134. {x=pos.x, y=pos.y, z=pos.z-1},
  135. }
  136. local sidewater = 0
  137. for k, v in ipairs(sides) do
  138. if is_water(minetest.get_node(v).name) then
  139. sidewater = sidewater + 1
  140. end
  141. end
  142. local ocean = count_nearby_ocean({x=pos.x, y=pos.y-1, z=pos.z})
  143. --minetest.chat_send_player("MustTest", "# Server: Ocean: " .. ocean)
  144. if ocean >= 500 and sidewater >= 4 then
  145. good = true
  146. end
  147. if good then
  148. --minetest.chat_send_all("# Server: Good!")
  149. -- Randomize time to next nodecheck.
  150. meta:set_int("chktmr", math.random(1, 60*3))
  151. meta:set_int("active", 1)
  152. meta:set_int("eups", eups)
  153. meta:set_string("error", "DUMMY")
  154. result = true
  155. else
  156. ---minetest.chat_send_all("# Server: Bad!")
  157. -- Don't set timer if generator is offline.
  158. -- The next check needs to happen the next time the machine is punched.
  159. meta:set_int("chktmr", 0)
  160. meta:set_int("active", 0)
  161. meta:set_int("eups", 0)
  162. if sidewater < 4 then
  163. meta:set_string("error", "Machine not properly submerged!")
  164. elseif ocean == 0 then
  165. meta:set_string("error", "Turbine has insufficient contact with water!")
  166. elseif ocean < 500 then
  167. meta:set_string("error", "Insufficient current strength (" .. ocean .. ")!")
  168. else
  169. meta:set_string("error", "Unknown issue, please contact admin.")
  170. end
  171. result = false
  172. end
  173. meta:set_string("infotext", tide.compose_infotext(pos))
  174. return result
  175. end
  176. -- Decrement check timer.
  177. timer = timer - 1
  178. meta:set_int("chktmr", timer)
  179. -- No check performed; just return whatever the result of the last check was.
  180. return (active == 1)
  181. end
  182. tide.on_timer =
  183. function(pos, elapsed)
  184. local meta = minetest.get_meta(pos)
  185. local owner = meta:get_string("owner")
  186. local inv = meta:get_inventory()
  187. local keeprunning = false
  188. -- Check if we can produce energy from environment.
  189. -- Note that this uses a caching algorithm.
  190. local canrun = tide.check_environment(pos, meta)
  191. -- If environment is no longer producing energy,
  192. -- unload the buffered energy.
  193. if not canrun then
  194. local energy = inv:get_stack("buffer", 1)
  195. energy:set_count(net2.put_energy(pos, owner, energy:get_count(), "mv"))
  196. inv:set_stack("buffer", 1, energy)
  197. end
  198. -- Produce energy.
  199. local needdischarge = false
  200. if canrun then
  201. local eups = meta:get_int("eups")
  202. local energy = "atomic:energy " .. eups
  203. local stack = inv:get_stack("buffer", 1)
  204. if stack:get_count() >= BUFFER_SIZE then
  205. needdischarge = true
  206. end
  207. if not needdischarge then
  208. if inv:room_for_item("buffer", energy) then
  209. inv:add_item("buffer", energy)
  210. end
  211. end
  212. keeprunning = true
  213. end
  214. -- Discharge energy.
  215. if needdischarge then
  216. --minetest.chat_send_player("MustTest", "DISCHARGING!")
  217. local energy = inv:get_stack("buffer", 1)
  218. -- Unload energy onto the network.
  219. local old = energy:get_count()
  220. energy:set_count(net2.put_energy(pos, owner, old, "mv"))
  221. inv:set_stack("buffer", 1, energy)
  222. if energy:get_count() < old then
  223. keeprunning = true
  224. else
  225. -- Batteries full? Go to sleep.
  226. keeprunning = false
  227. end
  228. end
  229. -- Determine mode (active or sleep) and set timer accordingly.
  230. if keeprunning then
  231. minetest.get_node_timer(pos):start(1.0)
  232. else
  233. -- Slow down timer during sleep periods to reduce load.
  234. minetest.get_node_timer(pos):start(math.random(1, 3*60))
  235. meta:set_int("chktmr", 0)
  236. meta:set_int("active", 0)
  237. meta:set_int("eups", 0)
  238. meta:set_string("infotext", tide.compose_infotext(pos))
  239. end
  240. end
  241. tide.on_construct =
  242. function(pos)
  243. local meta = minetest.get_meta(pos)
  244. meta:set_int("eups", 0)
  245. meta:set_int("active", 0)
  246. meta:set_int("chktmr", 0)
  247. meta:set_string("error", "DUMMY")
  248. meta:set_string("owner", "DUMMY")
  249. meta:set_string("nodename", "DUMMY")
  250. tide.privatize(meta)
  251. end
  252. tide.privatize =
  253. function(meta)
  254. meta:mark_as_private({
  255. "nodename",
  256. "owner",
  257. "chktmr",
  258. "error",
  259. "eups",
  260. "active",
  261. })
  262. end
  263. tide.after_place_node =
  264. function(pos, placer, itemstack, pointed_thing)
  265. local meta = minetest.get_meta(pos)
  266. local node = minetest.get_node(pos)
  267. local owner = placer:get_player_name()
  268. local inv = meta:get_inventory()
  269. meta:set_string("owner", owner)
  270. meta:set_string("nodename", node.name)
  271. inv:set_size("buffer", 1)
  272. net2.clear_caches(pos, owner, "mv")
  273. meta:set_string("formspec", tide.compose_formspec(pos))
  274. meta:set_string("infotext", tide.compose_infotext(pos))
  275. nodestore.add_node(pos)
  276. local timer = minetest.get_node_timer(pos)
  277. timer:start(1.0)
  278. end
  279. tide.on_blast =
  280. function(pos)
  281. local drops = {}
  282. drops[#drops+1] = "tide:tide"
  283. minetest.remove_node(pos)
  284. return drops
  285. end
  286. tide.allow_metadata_inventory_put =
  287. function(pos, listname, index, stack, player)
  288. return 0
  289. end
  290. tide.allow_metadata_inventory_move =
  291. function(pos, from_list, from_index, to_list, to_index, count, player)
  292. return 0
  293. end
  294. tide.allow_metadata_inventory_take =
  295. function(pos, listname, index, stack, player)
  296. return 0
  297. end
  298. tide.on_metadata_inventory_move =
  299. function(pos)
  300. tide.trigger_update(pos)
  301. end
  302. tide.on_metadata_inventory_put =
  303. function(pos)
  304. tide.trigger_update(pos)
  305. end
  306. tide.on_metadata_inventory_take =
  307. function(pos, listname, index, stack, player)
  308. tide.trigger_update(pos)
  309. end
  310. tide.on_destruct =
  311. function(pos)
  312. local meta = minetest.get_meta(pos)
  313. net2.clear_caches(pos, meta:get_string("owner"), "mv")
  314. nodestore.del_node(pos)
  315. end
  316. if not tide.run_once then
  317. local nodebox = {
  318. {0, 16, 0, 16, 15, 16}, -- Base.
  319. {2, 15, 2, 14, 10, 14}, -- Box shaft.
  320. {7, 15, 7, 9, -1, 9}, -- Shaft.
  321. {-6, 3, 6, 22, 4, 10}, -- Upper blade.
  322. {6, 0, -6, 10, 1, 22}, -- Lower blade.
  323. }
  324. for k, v in ipairs(nodebox) do
  325. for m, n in ipairs(v) do
  326. local p = nodebox[k][m]
  327. p = p / 16
  328. p = p - 0.5
  329. nodebox[k][m] = p
  330. end
  331. end
  332. minetest.register_node(":tide:tide", {
  333. drawtype = "nodebox",
  334. description = "MV Tidal Generator",
  335. tiles = {"technic_carbon_steel_block.png"},
  336. groups = utility.dig_groups("machine"),
  337. node_box = {
  338. type = "fixed",
  339. fixed = nodebox,
  340. },
  341. paramtype = "light",
  342. paramtype2 = "facedir",
  343. is_ground_content = false,
  344. sounds = default.node_sound_metal_defaults(),
  345. drop = "tide:tide",
  346. on_energy_get = function(...)
  347. return tide.on_energy_get(...) end,
  348. on_rotate = function(...)
  349. return screwdriver.rotate_simple(...) end,
  350. allow_metadata_inventory_put = function(...)
  351. return tide.allow_metadata_inventory_put(...) end,
  352. allow_metadata_inventory_move = function(...)
  353. return tide.allow_metadata_inventory_move(...) end,
  354. allow_metadata_inventory_take = function(...)
  355. return tide.allow_metadata_inventory_take(...) end,
  356. on_metadata_inventory_move = function(...)
  357. return tide.on_metadata_inventory_move(...) end,
  358. on_metadata_inventory_put = function(...)
  359. return tide.on_metadata_inventory_put(...) end,
  360. on_metadata_inventory_take = function(...)
  361. return tide.on_metadata_inventory_take(...) end,
  362. on_punch = function(...)
  363. return tide.on_punch(...) end,
  364. can_dig = function(...)
  365. return tide.can_dig(...) end,
  366. on_timer = function(...)
  367. return tide.on_timer(...) end,
  368. on_construct = function(...)
  369. return tide.on_construct(...) end,
  370. on_destruct = function(...)
  371. return tide.on_destruct(...) end,
  372. on_blast = function(...)
  373. return tide.on_blast(...) end,
  374. after_place_node = function(...)
  375. return tide.after_place_node(...) end,
  376. })
  377. minetest.register_craft({
  378. output = 'tide:tide',
  379. recipe = {
  380. {'', 'cb2:mv', ''},
  381. {'carbon_steel:ingot', 'carbon_steel:block', 'carbon_steel:ingot'},
  382. {'', 'techcrafts:electric_motor', ''},
  383. }
  384. })
  385. local c = "tide:core"
  386. local f = tide.modpath .. "/tide.lua"
  387. reload.register_file(c, f, false)
  388. tide.run_once = true
  389. end