lantern.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. -- lantern.lua
  2. assert(type(lantern) == "table")
  3. -- NOTE: Lantern overfill up to 124% is a feature, not a bug. ;-)
  4. -- but be carefull overfilling the lantern, may cause a fire.
  5. -- ==== SETTINGS ====
  6. -- fuel material
  7. lantern.fuel = "basic_materials:oil_extract"
  8. -- fuel max in bottles
  9. lantern.fuel_max = 4
  10. -- fuel time in seconds (per fuel bottle)
  11. lantern.fuel_time = 15*60
  12. -- fuel multiplier on dim light (x times longer burn)
  13. -- low fuel is fuel_time/fuel_dim, on this level lamp only turns dim
  14. lantern.fuel_dim = 4
  15. -- fuel multiplier on med light (x times longer burn)
  16. lantern.fuel_med = 2
  17. -- mode light duration
  18. -- ==== ==-==== =====
  19. -- full 13 100% 1.00
  20. -- med 9 69% 1.50
  21. -- dim 6 46% 2.20
  22. -- off 1 7% 14.30 N/A
  23. lantern.bright_full = 13
  24. lantern.bright_med = 9
  25. lantern.bright_dim = 6
  26. lantern.bright_off = 1
  27. -- == GENERIC ==
  28. lantern.nodelist = {
  29. "lantern:lantern_floor_on",
  30. "lantern:lantern_floor_med",
  31. "lantern:lantern_floor_dim",
  32. "lantern:lantern_floor_off",
  33. "lantern:lantern_ceiling_on",
  34. "lantern:lantern_ceiling_med",
  35. "lantern:lantern_ceiling_dim",
  36. "lantern:lantern_ceiling_off",
  37. "lantern:lantern_wall_on",
  38. "lantern:lantern_wall_med",
  39. "lantern:lantern_wall_dim",
  40. "lantern:lantern_wall_off"
  41. }
  42. local function listcontains(list, obj)
  43. for i, v in ipairs(list) do
  44. if obj == v then
  45. return true
  46. end
  47. end
  48. return false
  49. end
  50. function lantern.turnoff(pos)
  51. -- stop dim timer
  52. -- stop on timer
  53. -- update burntime
  54. local sside = lantern.get_side(pos)
  55. local sname = string.format("lantern:lantern_%s_", sside)
  56. local node = minetest.get_node(pos)
  57. local timer = minetest.get_node_timer(pos)
  58. --if not timer:is_started() then
  59. -- return false
  60. --end
  61. local meta = minetest.get_meta(pos)
  62. local burntime = meta:get_int("burntime") or 0
  63. local timeout = timer:get_timeout()
  64. local elapsed = timer:get_elapsed()
  65. timer:stop()
  66. if node.name == sname .. "on" then
  67. -- TURN OFF
  68. if elapsed < burntime then
  69. burntime = burntime - elapsed
  70. else
  71. burntime = 0
  72. end
  73. elseif node.name == sname .. "med" then
  74. -- TURN OFF (med)
  75. burntime = burntime * lantern.fuel_med
  76. if elapsed < burntime then
  77. burntime = burntime - elapsed
  78. burntime = math.floor(burntime / lantern.fuel_med)
  79. else
  80. burntime = 0
  81. end
  82. elseif node.name == sname .. "dim" then
  83. -- TURN OFF (dim)
  84. burntime = burntime * lantern.fuel_dim
  85. if elapsed < burntime then
  86. burntime = burntime - elapsed
  87. burntime = math.floor(burntime / lantern.fuel_dim)
  88. else
  89. burntime = 0
  90. end
  91. end
  92. node.name = sname .. "off"
  93. minetest.swap_node(pos, node)
  94. meta:set_int("burntime", burntime)
  95. -- meta:set_string("infotext", string.format("Lantern [%d]", burntime))
  96. lantern.update_info(pos)
  97. --return true
  98. end
  99. function lantern.turndim(pos)
  100. -- timer must be stopped
  101. -- start dim timer
  102. local timer = minetest.get_node_timer(pos)
  103. if timer:is_started() then
  104. return false
  105. end
  106. local fuel_min = math.floor(lantern.fuel_time / lantern.fuel_dim)
  107. local meta = minetest.get_meta(pos)
  108. local burntime = meta:get_int("burntime") or 0
  109. if burntime <= 0 then
  110. -- no fuel
  111. return false
  112. end
  113. -- burntime multiplier on low consumption (dim)
  114. burntime = burntime * lantern.fuel_dim
  115. timer:start(burntime)
  116. local node = minetest.get_node(pos)
  117. local sside = lantern.get_side(pos)
  118. local sname = string.format("lantern:lantern_%s_dim", sside)
  119. node.name = sname
  120. minetest.swap_node(pos, node)
  121. return true
  122. end
  123. function lantern.turnmed(pos)
  124. -- timer must be stopped
  125. -- start med timer
  126. local timer = minetest.get_node_timer(pos)
  127. if timer:is_started() then
  128. return false
  129. end
  130. local fuel_min = math.floor(lantern.fuel_time / lantern.fuel_dim)
  131. local meta = minetest.get_meta(pos)
  132. local burntime = meta:get_int("burntime") or 0
  133. if burntime <= 0 then
  134. -- no fuel
  135. return false
  136. end
  137. -- burntime multiplier on med consumption (med)
  138. burntime = burntime * lantern.fuel_med
  139. timer:start(burntime)
  140. local node = minetest.get_node(pos)
  141. local sside = lantern.get_side(pos)
  142. local sname = string.format("lantern:lantern_%s_med", sside)
  143. node.name = sname
  144. minetest.swap_node(pos, node)
  145. return true
  146. end
  147. function lantern.turnon(pos)
  148. -- timer must be stopped
  149. -- start on timer
  150. local timer = minetest.get_node_timer(pos)
  151. if timer:is_started() then
  152. return false
  153. end
  154. local fuel_min = math.floor(lantern.fuel_time / lantern.fuel_dim)
  155. local meta = minetest.get_meta(pos)
  156. local burntime = meta:get_int("burntime") or 0
  157. if burntime < fuel_min then
  158. -- low fuel, turn dim instead
  159. return false
  160. end
  161. -- at low fuel only turn dim
  162. timer:start(burntime - fuel_min)
  163. local sside = lantern.get_side(pos)
  164. local sname = string.format("lantern:lantern_%s_on", sside)
  165. local node = minetest.get_node(pos)
  166. node.name = sname
  167. minetest.swap_node(pos, node)
  168. return true
  169. end
  170. function lantern.get_side(pos)
  171. local node = minetest.get_node(pos)
  172. if not node then
  173. return nil
  174. end
  175. local sside
  176. if node.param2 == 0 then
  177. -- ceiling
  178. sside = "ceiling"
  179. elseif node.param2 == 1 then
  180. -- floor
  181. sside = "floor"
  182. else
  183. -- wall
  184. sside = "wall"
  185. end
  186. return sside
  187. end
  188. -- switch lamp on/dim/off
  189. function lantern.switch(pos)
  190. local node = minetest.get_node(pos)
  191. if not node then
  192. return false
  193. end
  194. local sside = lantern.get_side(pos)
  195. local sname = string.format("lantern:lantern_%s_", sside)
  196. local timer = minetest.get_node_timer(pos)
  197. local meta = minetest.get_meta(pos)
  198. if node.name == sname .. "off" then
  199. -- TURN DIM
  200. -- lantern.chat_debug("boxface", "try turning dim")
  201. if lantern.turndim(pos) then
  202. --lantern.chat_debug("boxface", "success")
  203. else
  204. --lantern.chat_debug("boxface", "failed")
  205. end
  206. elseif node.name == sname .. "dim" then
  207. lantern.turnoff(pos)
  208. -- TURN MED
  209. if not lantern.turnmed(pos) then
  210. lantern.turnoff(pos)
  211. end
  212. elseif node.name == sname .. "med" then
  213. lantern.turnoff(pos)
  214. -- TURN ON
  215. --lantern.chat_debug("boxface", "try turning on full")
  216. -- if low fuel turn off
  217. if not lantern.turnon(pos) then
  218. --lantern.chat_debug("boxface", "failed! wtf?")
  219. lantern.turnoff(pos)
  220. end
  221. elseif node.name == sname .. "on" then
  222. -- TURN OFF
  223. -- lantern.chat_debug("boxface", "try turning off")
  224. lantern.turnoff(pos)
  225. end
  226. return true
  227. end
  228. function lantern.refuel(pos)
  229. -- DONE: check refueling when lamp is on, may have a bug
  230. local meta = minetest.get_meta(pos)
  231. local node = minetest.get_node(pos)
  232. if not node then
  233. return false
  234. end
  235. local oldstate = lantern.get_state(pos)
  236. local timer = minetest.get_node_timer(pos)
  237. if timer:is_started() then
  238. -- refueling when lamp is on
  239. -- Do you want to make a fire?
  240. -- DONE: stop timer before refueling, then turn it back on.
  241. lantern.turnoff(pos)
  242. end
  243. if listcontains(lantern.nodelist, node.name) then
  244. local burntime = meta:get_int("burntime") or 0
  245. if burntime < (lantern.fuel_max * lantern.fuel_time) then
  246. burntime = burntime + lantern.fuel_time
  247. meta:set_int("burntime", burntime)
  248. -- meta:set_string("infotext", "Lantern [" .. burntime .. "]")
  249. lantern.update_info(pos)
  250. -- set old state
  251. --lantern.chat_debug("boxface", "Old state was: [" .. oldstate .. "]")
  252. if oldstate ~= "off" then
  253. --lantern.chat_debug("boxface", "set old state")
  254. lantern.set_state(pos, oldstate)
  255. end
  256. return true
  257. end
  258. end
  259. -- if lamp was turned off, if not refueled turn it back on
  260. if oldstate ~= "off" then
  261. lantern.set_state(pos, oldstate)
  262. end
  263. return false
  264. end
  265. function lantern.take_fuel(pos, itemstack)
  266. -- DONE: turn off timer when taking fuel (bug)
  267. local meta = minetest.get_meta(pos)
  268. local oldstate = lantern.get_state(pos)
  269. if oldstate ~= "off" then
  270. lantern.turnoff(pos)
  271. end
  272. local burntime = meta:get_int("burntime") or 0
  273. if burntime >= lantern.fuel_time then
  274. if itemstack:item_fits(lantern.fuel) then
  275. itemstack:add_item(lantern.fuel)
  276. burntime = burntime - lantern.fuel_time
  277. meta:set_int("burntime", burntime)
  278. -- meta:set_string("infotext", "Lantern [" .. burntime .. "]")
  279. lantern.update_info(pos)
  280. end
  281. end
  282. if oldstate ~= "off" then
  283. lantern.set_state(pos, oldstate)
  284. end
  285. return itemstack
  286. end
  287. function lantern.get_state(pos)
  288. local sside = lantern.get_side(pos)
  289. local sname = string.format("lantern:lantern_%s_", sside)
  290. local node = minetest.get_node(pos)
  291. local is_off = (node.name == sname .. "off")
  292. local is_on = (node.name == sname .. "on")
  293. local is_med = (node.name == sname .. "med")
  294. local is_dim = (node.name == sname .. "dim")
  295. if is_off then
  296. return "off"
  297. elseif is_on then
  298. return "on"
  299. elseif is_med then
  300. return "med"
  301. elseif is_dim then
  302. return "dim"
  303. else
  304. return "error"
  305. end
  306. end
  307. function lantern.set_state(pos, state)
  308. if state == "off" then
  309. return lantern.turnoff(pos)
  310. elseif state == "on" then
  311. return lantern.turnon(pos)
  312. elseif state == "med" then
  313. return lantern.turnmed(pos)
  314. elseif state == "dim" then
  315. return lantern.turndim(pos)
  316. end
  317. end
  318. function lantern.update_info(pos)
  319. -- update info text
  320. -- DONE: get fuel in percent and display on infotext.
  321. local meta = minetest.get_meta(pos)
  322. local burntime = meta:get_int("burntime") or 0
  323. local sstate = lantern.get_state(pos)
  324. local is_off = (sstate == "off")
  325. local is_on = (sstate == "on")
  326. local is_med = (sstate == "med")
  327. local is_dim = (sstate == "dim")
  328. if is_off then
  329. local burn_max = lantern.fuel_max * lantern.fuel_time
  330. local perc = math.floor(burntime / burn_max * 100)
  331. local s = string.format("Lantern: %d%%", perc)
  332. meta:set_string("infotext", s)
  333. return
  334. elseif is_on then
  335. local timer = minetest.get_node_timer(pos)
  336. local elapsed = timer:get_elapsed()
  337. local burn = burntime - elapsed
  338. local burn_max = lantern.fuel_max * lantern.fuel_time
  339. local perc = math.floor(burn / burn_max * 100)
  340. local s = string.format("Lantern: %d%%", perc)
  341. meta:set_string("infotext", s)
  342. return
  343. elseif is_med then
  344. local timer = minetest.get_node_timer(pos)
  345. local elapsed = timer:get_elapsed()
  346. local burn = burntime * lantern.fuel_med - elapsed
  347. local burn_max = lantern.fuel_max * lantern.fuel_time * lantern.fuel_med
  348. local perc = math.floor(burn / burn_max * 100)
  349. local s = string.format("Lantern: %d%%", perc)
  350. meta:set_string("infotext", s)
  351. return
  352. elseif is_dim then
  353. local timer = minetest.get_node_timer(pos)
  354. local elapsed = timer:get_elapsed()
  355. local burn = burntime * lantern.fuel_dim - elapsed
  356. local burn_max = lantern.fuel_max * lantern.fuel_time * lantern.fuel_dim
  357. local perc = math.floor(burn / burn_max * 100)
  358. local s = string.format("Lantern: %d%%", perc)
  359. meta:set_string("infotext", s)
  360. return
  361. end
  362. end
  363. -- == CALLBACKS ==
  364. function lantern.do_after_dig_node(pos, oldnode, oldmetadata, digger)
  365. -- TODO: Find a way to keep unused fuel when is less than a bottle.
  366. -- TODO: use lantern.turnoff() instead of calculating fuel again. (review)
  367. local timer = minetest.get_node_timer(pos)
  368. if timer:is_started() then
  369. -- lantern.chat_debug("boxface", "WARNING: timer was on after dig")
  370. timer:stop()
  371. end
  372. --lantern.chat_debug("boxface", "After Dig!")
  373. --lantern.chat_debug("boxface", dump(oldmetadata))
  374. local burntime = oldmetadata.fields["burntime"] or 0
  375. local fuelcount = math.floor(burntime / lantern.fuel_time)
  376. local inv = digger:get_inventory()
  377. -- take back or drop unused fuel
  378. if fuelcount > 0 then
  379. local str = string.format("%s %d", lantern.fuel, fuelcount)
  380. local stackfuel = ItemStack(str)
  381. local leftover = inv:add_item("main", stackfuel)
  382. if leftover:get_count() > 0 then
  383. minetest.item_drop(leftover, digger, pos)
  384. end
  385. end
  386. end
  387. function lantern.do_construct(pos)
  388. end
  389. function lantern.do_place(itemstack, placer, pointed_thing)
  390. local under = pointed_thing.under
  391. local above = pointed_thing.above
  392. local node = minetest.get_node(under)
  393. local wdir = minetest.dir_to_wallmounted(vector.subtract(under, above))
  394. -- possible bug?
  395. local fakestack = itemstack
  396. local stackname = itemstack:get_name()
  397. if stackname == "lantern:lantern_floor_off" then
  398. if wdir == 0 then
  399. -- ceiling
  400. fakestack:set_name("lantern:lantern_ceiling_off")
  401. elseif wdir == 1 then
  402. -- floor
  403. fakestack:set_name("lantern:lantern_floor_off")
  404. else
  405. -- wall
  406. fakestack:set_name("lantern:lantern_wall_off")
  407. end
  408. itemstack = minetest.item_place(fakestack, placer, pointed_thing, wdir)
  409. local meta = minetest.get_meta(above)
  410. meta:set_string("infotext", "Lantern (off)")
  411. itemstack:set_name("lantern:lantern_floor_off")
  412. end
  413. return itemstack
  414. end
  415. function lantern.do_punch(pos, node, puncher, pointed_thing)
  416. -- TODO: if shift key pressed switch down light (bright to dim)
  417. lantern.switch(pos)
  418. lantern.update_info(pos)
  419. end
  420. function lantern.do_timer(pos, elapsed)
  421. local sside = lantern.get_side(pos)
  422. local sname = string.format("lantern:lantern_%s_", sside)
  423. local node = minetest.get_node(pos)
  424. local is_on = (node.name == sname .. "on")
  425. local is_med = (node.name == sname .. "med")
  426. if is_on or is_med then
  427. -- lantern.chat_debug("boxface", "lamp runs low on fuel")
  428. -- patch set fuel to low level
  429. local meta = minetest.get_meta(pos)
  430. local fuel_low = math.floor(lantern.fuel_time / lantern.fuel_dim)
  431. meta:set_int("burntime", fuel_low)
  432. lantern.turnoff(pos)
  433. lantern.turndim(pos)
  434. elseif node.name == sname .. "dim" then
  435. -- lantern.chat_debug("boxface", "lamp runs out of fuel")
  436. -- patch set fuel to zero
  437. local meta = minetest.get_meta(pos)
  438. meta:set_int("burntime", 0)
  439. lantern.turnoff(pos)
  440. end
  441. end
  442. function lantern.do_rightclick(pos, node, clicker, itemstack, pointed_thing)
  443. local pname = clicker:get_player_name()
  444. local control = clicker:get_player_control()
  445. -- ==== extract fuel ====
  446. if control.aux1 then
  447. --lantern.chat_debug("boxface", "rightclick aux1")
  448. if itemstack:is_empty() or itemstack:get_name() == lantern.fuel then
  449. itemstack = lantern.take_fuel(pos, itemstack)
  450. end
  451. return
  452. end
  453. -- ==== refuel lamps ====
  454. if itemstack:get_name() == lantern.fuel then
  455. local isRefueled = lantern.refuel(pos)
  456. if isRefueled then
  457. itemstack:take_item(1)
  458. -- lantern.chat_debug(pname, "Lantern refueled")
  459. else
  460. -- lantern.chat_debug(pname, "Lantern is full")
  461. end
  462. else
  463. -- swith lantern on/dim/off
  464. -- TODO: move to on_punch
  465. --lantern.switch(pos)
  466. end
  467. end