fishing.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. --[[
  2. This fishing routine is inspired by the great work Rootyjr did for MineClone2
  3. ]]--
  4. local S = ethereal.intllib
  5. local fish_items = {
  6. "ethereal:fish_bluefin",
  7. "ethereal:fish_blueram",
  8. "ethereal:fish_catfish",
  9. "ethereal:fish_plaice",
  10. "ethereal:fish_salmon",
  11. {"ethereal:fish_clownfish", "savanna"},
  12. {"ethereal:fish_pike", "grassy"},
  13. {"ethereal:fish_flathead", "jungle"},
  14. {"ethereal:fish_pufferfish", "desert_ocean"},
  15. {"ethereal:fish_cichlid", "junglee_ocean"},
  16. {"ethereal:fish_coy", "sakura"},
  17. {"ethereal:fish_angler", "ocean"},
  18. {"ethereal:fish_jellyfish", "ocean"},
  19. {"ethereal:fish_seahorse", "ocean"},
  20. {"ethereal:fish_piranha", "jungle"},
  21. {"ethereal:fish_trout", "ocean"}
  22. }
  23. local junk_items = {
  24. "ethereal:bowl",
  25. "default:stick",
  26. "farming:string",
  27. "default:papyrus",
  28. "dye:black",
  29. {"ethereal:bamboo", "bamboo"}
  30. }
  31. local bonus_items = {
  32. "mobs:nametag",
  33. "mobs:saddle",
  34. "flowers:waterlily",
  35. "default:book",
  36. {"ethereal:crystal_spike", "frost"},
  37. {"ethereal:banana_bunch", "grove"}
  38. }
  39. local default_item = "default:dirt"
  40. local random = math.random -- yup we use this a lot
  41. -- add item function
  42. ethereal.add_item = function(fish, junk, bonus)
  43. if fish and fish ~= "" then
  44. table.insert(fish_items, fish)
  45. end
  46. if junk and junk ~= "" then
  47. table.insert(junk_items, junk)
  48. end
  49. if bonus and bonus ~= "" then
  50. table.insert(bonus_items, bonus)
  51. end
  52. end
  53. local effect = function(pos)
  54. minetest.add_particle({
  55. pos = {
  56. x = pos.x + random() - 0.5,
  57. y = pos.y + 0.1,
  58. z = pos.z + random() - 0.5
  59. },
  60. velocity = {x = 0, y = 4, z = 0},
  61. acceleration = {x = 0, y = -5, z = 0},
  62. expirationtime = random() * 0.5,
  63. size = random(),
  64. collisiondetection = false,
  65. vertical = false,
  66. texture = "bubble.png"
  67. })
  68. end
  69. -- fishing bob entity
  70. minetest.register_entity("ethereal:bob_entity", {
  71. textures = {"ethereal_fishing_bob.png"},
  72. visual_size = {x = 0.5, y = 0.5},
  73. collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
  74. physical = false,
  75. pointable = false,
  76. static_save = false,
  77. timer = 0,
  78. on_step = function(self, dtime)
  79. local pos = self.object:get_pos()
  80. local node = minetest.get_node(pos)
  81. local def = minetest.registered_nodes[node.name]
  82. -- casting rod into water
  83. if not self.cast then
  84. -- remove if we hit something hard
  85. if (def and def.walkable) or node.name == "ignore" then
  86. self.object:remove() ; --print("-- hit block")
  87. return
  88. end
  89. -- while bob is in water
  90. if def and def.liquidtype == "source"
  91. and minetest.get_item_group(node.name, "water") > 0 then
  92. -- incase of lag find water level
  93. local free_fall, blocker = minetest.line_of_sight(
  94. {x = pos.x, y = pos.y + 2, z = pos.z},
  95. {x = pos.x, y = pos.y , z = pos.z})
  96. -- do we have worms for bait, if so take one
  97. local player = self.fisher and minetest.get_player_by_name(self.fisher)
  98. local inv = player and player:get_inventory()
  99. local bait = 0
  100. if inv and inv:contains_item("main", "ethereal:worm") then
  101. inv:remove_item("main", "ethereal:worm")
  102. bait = 20
  103. end
  104. -- re-position fishing bob and set to cast
  105. pos = {x = pos.x, y = blocker.y + 0.45, z = pos.z}
  106. self.object:set_acceleration({x = 0, y = 0, z = 0})
  107. self.object:set_velocity({x = 0, y = 0, z = 0})
  108. self.object:set_pos(pos)
  109. self.bait = bait
  110. self.cast = true
  111. -- splash
  112. effect(pos) ; effect(pos) ; effect(pos) ; effect(pos)
  113. minetest.sound_play("default_water_footstep", {
  114. pos = pos, gain = 0.1}, true)
  115. end
  116. else -- already cast and waiting for fish
  117. -- we need a name
  118. if self.fisher == nil or self.fisher == "" then
  119. self.object:remove() ; --print("-- no name")
  120. return
  121. end
  122. local player = minetest.get_player_by_name(self.fisher)
  123. -- we need an actual person
  124. if not player then
  125. self.object:remove() ; --print("-- no player")
  126. return
  127. end
  128. local wield = player:get_wielded_item()
  129. -- we also need a rod to fish with
  130. if not wield or wield:get_name() ~= "ethereal:fishing_rod" then
  131. self.object:remove() ; --print("-- no rod")
  132. return
  133. end
  134. -- remove bob if player is too far away
  135. local pla_pos = player:get_pos()
  136. if (pla_pos.y - pos.y) > 15
  137. or (pla_pos.y - pos.y) < -15
  138. or (pla_pos.x - pos.x) > 15
  139. or (pla_pos.x - pos.x) < -15
  140. or (pla_pos.z - pos.z) > 15
  141. or (pla_pos.z - pos.z) < -15 then
  142. self.object:remove() ; --print("-- out of range")
  143. return
  144. end
  145. -- when in water, bob.
  146. if def and def.liquidtype == "source"
  147. and minetest.get_item_group(def.name, "water") ~= 0 then
  148. self.old_y = self.old_y or pos.y
  149. -- choose random time to wait (minus bait time for worm)
  150. if not self.patience or self.patience <= 0 then
  151. self.patience = random(10, (45 - self.bait))
  152. self.bait = 0
  153. end
  154. -- add particles if bobber bobbing
  155. if self.bob then
  156. effect(pos)
  157. -- handle timer
  158. if self.timer < self.patience then
  159. self.timer = self.timer + dtime
  160. else
  161. self.patience = 0
  162. self.timer = 0
  163. self.bob = false
  164. end
  165. else
  166. -- handle timer
  167. if self.timer < self.patience then
  168. self.timer = self.timer + dtime
  169. else
  170. -- waiting over, bob that bobber and play splash sound
  171. self.bob = true
  172. self.patience = 1.5 -- timeframe to catch fish after bob
  173. self.timer = 0
  174. self.object:set_velocity({x = 0, y = -1, z = 0})
  175. self.object:set_acceleration({x = 0, y = 3, z = 0})
  176. minetest.sound_play("default_water_footstep", {
  177. pos = pos, gain = 0.1}, true)
  178. end
  179. end
  180. else
  181. -- reset to original position after dive.
  182. if self.old_y and pos.y > self.old_y then
  183. self.object:set_velocity({x = 0, y = 0, z = 0})
  184. self.object:set_acceleration({x = 0, y = 0, z = 0})
  185. self.object:set_pos({x = pos.x, y = self.old_y, z = pos.z})
  186. --print("-- reset bob y pos")
  187. end
  188. -- remove if not in water and not bobbing
  189. if not self.bob then
  190. self.object:remove() ; --print("-- not in water")
  191. end
  192. end
  193. end -- if not self.cast
  194. end -- on_step
  195. })
  196. -- narrow item list depending on biome if applicable
  197. local find_item = function(list, pos)
  198. local item
  199. local items = {}
  200. local data= minetest.get_biome_data(pos)
  201. local biome = data and minetest.get_biome_name(data.biome) or ""
  202. for n = 1, #list do
  203. item = list[n]
  204. if type(item) == "string" then
  205. table.insert(items, item)
  206. elseif type(item) == "table" then
  207. if item[2] == "" or biome:find(item[2]) then
  208. table.insert(items, item[1])
  209. end
  210. end
  211. end
  212. --print("==biome: " .. biome, dump(items))
  213. if #items > 0 then
  214. return items[random(#items)]
  215. end
  216. return ""
  217. end
  218. -- fishing rod function that throws pre bob, places bob and catches fish when it moves
  219. local use_rod = function(itemstack, player, pointed_thing)
  220. local pos = player:get_pos()
  221. local objs = minetest.get_objects_inside_radius(pos, 15)
  222. local found = true
  223. local ent
  224. -- loop through entities and look for bobs
  225. for n = 1, #objs do
  226. ent = objs[n]:get_luaentity()
  227. if ent
  228. and ent.fisher
  229. and ent.name == "ethereal:bob_entity"
  230. and player:get_player_name() == ent.fisher then
  231. found = false
  232. if ent.bob == true then
  233. local item
  234. local r = random(100)
  235. if r < 86 then
  236. item = find_item(fish_items, pos)
  237. elseif r > 85 and r < 96 then
  238. item = find_item(junk_items, pos)
  239. else
  240. item = find_item(bonus_items, pos)
  241. end
  242. -- make sure item exists, if not replace with default item
  243. if not minetest.registered_items[item] then
  244. item = default_item
  245. end
  246. --print ("---caught", item, r)
  247. item = ItemStack(item)
  248. local inv = player:get_inventory()
  249. if inv:room_for_item("main", item) then
  250. inv:add_item("main", item)
  251. else
  252. minetest.add_item(pos, item)
  253. end
  254. end
  255. ent.object:remove()
  256. return itemstack
  257. end
  258. end
  259. -- loop through entities and look for bobs
  260. for n = 1, #objs do
  261. ent = objs[n]:get_luaentity()
  262. if ent
  263. and ent.fisher
  264. and ent.name == "ethereal:bob_entity"
  265. and player:get_player_name() == ent.fisher then
  266. found = false
  267. break
  268. end
  269. end
  270. if found == true then
  271. local playerpos = player:get_pos()
  272. local dir = player:get_look_dir()
  273. local pos = {x = playerpos.x, y = playerpos.y + 1.5, z = playerpos.z}
  274. minetest.sound_play("ethereal_casting_rod",
  275. {pos = pos, gain = 0.4, max_hear_distance = 16}, true)
  276. -- place actual bob
  277. local obj = minetest.add_entity(pos, "ethereal:bob_entity")
  278. obj:set_velocity({x = dir.x * 8, y = dir.y * 8, z = dir.z * 8})
  279. obj:set_acceleration({x = dir.x * -3, y = -9.8, z = dir.z * -3})
  280. obj:get_luaentity().fisher = player and player:get_player_name()
  281. end
  282. end
  283. -- scan area for bobs that belong to player and remove
  284. local remove_bob = function(player)
  285. local objs = minetest.get_objects_inside_radius(player:get_pos(), 15)
  286. local name = player:get_player_name()
  287. local ent
  288. for n = 1, #objs do
  289. ent = objs[n]:get_luaentity()
  290. if ent and ent.name == "ethereal:bob_entity" then
  291. -- only remove players own bob
  292. if ent.fisher and ent.fisher == name then
  293. ent.object:remove()
  294. end
  295. end
  296. end
  297. end
  298. -- remove bob if player signs off
  299. minetest.register_on_leaveplayer(function(player)
  300. remove_bob(player)
  301. end)
  302. -- remove bob if player dies
  303. minetest.register_on_dieplayer(function(player)
  304. remove_bob(player)
  305. end)
  306. -- fishing rod
  307. minetest.register_tool("ethereal:fishing_rod", {
  308. description = S("Fishing Rod (USE to cast and again when the time is right)"),
  309. groups = {tool = 1},
  310. inventory_image = "ethereal_fishing_rod.png",
  311. wield_image = "ethereal_fishing_rod.png^[transformFX",
  312. wield_scale = {x = 1.5, y = 1.5, z = 1},
  313. stack_max = 1,
  314. on_use = use_rod,
  315. sound = {breaks = "default_tool_breaks"}
  316. })
  317. minetest.register_craft({
  318. output = "ethereal:fishing_rod",
  319. recipe = {
  320. {"","","group:stick"},
  321. {"","group:stick","farming:string"},
  322. {"group:stick","","farming:string"}
  323. }
  324. })
  325. minetest.register_craft({
  326. type = "fuel",
  327. recipe = "ethereal:fishing_rod",
  328. burntime = 15
  329. })
  330. local fish = {
  331. {"Blue Fin", "bluefin", 2},
  332. {"Blue Ram", "blueram", 2},
  333. {"Catfish", "catfish", 2},
  334. {"Clownfish", "clownfish", 2},
  335. {"Pike", "pike", 2},
  336. {"Flathead", "flathead", 2},
  337. {"Plaice", "plaice", 2},
  338. {"Pufferfish", "pufferfish", -2},
  339. {"Coy", "coy", 2},
  340. {"Salmon", "salmon", 2},
  341. {"Cichlid", "cichlid", 2},
  342. {"Angler", "angler", 2},
  343. {"Jellyfish", "jellyfish", 0},
  344. {"Seahorse", "seahorse", 0},
  345. {"Piranha", "piranha", 2},
  346. {"Trout", "trout", 2}
  347. }
  348. for n = 1, #fish do
  349. local usage
  350. local groups
  351. if fish[n][3] > 0 then
  352. usage = minetest.item_eat(fish[n][3])
  353. groups = {food_fish_raw = 1, ethereal_fish = 1}
  354. end
  355. minetest.register_craftitem("ethereal:fish_" .. fish[n][2], {
  356. description = S(fish[n][1]),
  357. inventory_image = "ethereal_fish_" .. fish[n][2] .. ".png",
  358. on_use = usage,
  359. groups = groups
  360. })
  361. end
  362. -- cooked fish
  363. minetest.register_craftitem(":ethereal:fish_cooked", {
  364. description = S("Cooked Fish"),
  365. inventory_image = "ethereal_fish_cooked.png",
  366. wield_image = "ethereal_fish_cooked.png",
  367. groups = {food_fish = 1, flammable = 3},
  368. on_use = minetest.item_eat(5)
  369. })
  370. minetest.register_craft({
  371. type = "cooking",
  372. output = "ethereal:fish_cooked",
  373. recipe = "group:ethereal_fish",
  374. cooktime = 8
  375. })
  376. -- Sashimi (Thanks to Natalia Grosner for letting me use the sashimi image)
  377. minetest.register_craftitem("ethereal:sashimi", {
  378. description = S("Sashimi"),
  379. inventory_image = "ethereal_sashimi.png",
  380. wield_image = "ethereal_sashimi.png",
  381. on_use = minetest.item_eat(4)
  382. })
  383. minetest.register_craft({
  384. output = "ethereal:sashimi 2",
  385. recipe = {
  386. {"group:food_seaweed", "group:food_fish_raw", "group:food_seaweed"},
  387. }
  388. })
  389. -- Worm
  390. minetest.register_craftitem("ethereal:worm", {
  391. description = S("Worm"),
  392. inventory_image = "ethereal_worm.png",
  393. wield_image = "ethereal_worm.png"
  394. })
  395. minetest.register_craft({
  396. output = "ethereal:worm",
  397. recipe = {
  398. {"default:dirt", "default:dirt"}
  399. }
  400. })
  401. -- compatibility
  402. minetest.register_alias("ethereal:fish_raw", "ethereal:fish_cichlid")
  403. minetest.register_alias("ethereal:fishing_rod_baited", "ethereal:fishing_rod")
  404. minetest.register_alias("ethereal:fish_chichlid", "ethereal:fish_cichlid")