windy.lua 12 KB

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