fishing.lua 12 KB

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