diff --git a/data/plugins/skill/fishing/fish.rb b/data/plugins/skill/fishing/fish.rb new file mode 100644 index 00000000..bdc2c871 --- /dev/null +++ b/data/plugins/skill/fishing/fish.rb @@ -0,0 +1,38 @@ + +# The hash of names to fish. +CATCHABLE_FISH = {} + +# A fish that can be caught. +class Fish + attr_reader :id, :level, :experience, :name + + # Creates the Fish. + def initialize(id, level, experience) + @id = id + @level = level + @experience = experience + + @name = name_of(:item, id) + end + +end + +# Appends a Fish to the hash. +def append_fish(name, fish) + CATCHABLE_FISH[name] = fish +end + +append_fish(:shrimp, Fish.new(317, 1, 10)) +append_fish(:sardine, Fish.new(327, 5, 20)) +append_fish(:herring, Fish.new(345, 10, 30)) +append_fish(:anchovy, Fish.new(321, 15, 40)) +append_fish(:mackerel, Fish.new(353, 16, 20)) +append_fish(:trout, Fish.new(335, 20, 50)) +append_fish(:cod, Fish.new(341, 23, 45)) +append_fish(:pike, Fish.new(349, 25, 60)) +append_fish(:salmon, Fish.new(331, 30, 70)) +append_fish(:tuna, Fish.new(359, 35, 80)) +append_fish(:lobster, Fish.new(377, 40, 90)) +append_fish(:bass, Fish.new(363, 46, 100)) +append_fish(:swordfish, Fish.new(371, 50, 100)) +append_fish(:shark, Fish.new(383, 76, 110)) \ No newline at end of file diff --git a/data/plugins/skill/fishing/fishing.rb b/data/plugins/skill/fishing/fishing.rb new file mode 100644 index 00000000..daa47e73 --- /dev/null +++ b/data/plugins/skill/fishing/fishing.rb @@ -0,0 +1,111 @@ +require 'java' + +java_import 'org.apollo.game.action.DistancedAction' +java_import 'org.apollo.game.model.Animation' +java_import 'org.apollo.game.model.entity.Skill' + +# An action that causes a mob to fish at a spot. +class FishingAction < DistancedAction + attr_reader :spot, :tool, :position, :started + + # Creates the FishingAction. + def initialize(mob, position, spot, option) + super(4, true, mob, position, 1) + @position = position + @spot = spot + @tool = spot.tools[action - 1] + + @options = (option == 1) ? spot.first_option : spot.second_option + @minimum_level = options.map { |fish| fish.level }.min + end + + # Returns whether or not a catch is successful. + def successful_catch(level, requirement) + return [level - requirement, 30].min > rand(40) + end + + # Starts the fishing process. + def start_fishing() + @started = true + mob.send_message(tool.message, true) + end + + # Executes the action. + def executeAction() + skills = mob.skill_set + fishing_level = skills.get_skill(Skill::FISHING).current_level + mob.turn_to(position) + + if (@minimum_level > fishing_level) + mob.send_message("You need a fishing level of #{@minimum_level} to fish at this spot.") + stop + return + end + + inventory = mob.inventory + if inventory.free_slots.zero? + inventory.force_capacity_exceeded + stop + return + end + + unless inventory.contains(@tool.id) + mob.send_message("You need a #{@tool.name} to fish at this spot.") + stop + return + end + + bait = @tool.bait + if (bait.empty? && !mob.inventory.contains(bait)) + mob.send_message("You need #{name_of(:item, bait)}s to fish at this spot.") + stop + return + end + + unless @started + start_fishing + else + options = @options.reject { |fish| fish.level > fishing_level } # Player may level up mid-action so reject here, not at initialisation. + fish = options.sample # TODO it's a ~70/30 chance, not 50/50 + + if successful_catch(fishing_level, fish.level) + inventory.remove(bait) unless bait.empty? + inventory.add(type.id) + + name = fish.name + mob.send_message("You catch #{name.end_with?('s') ? 'some' : 'a'} #{name}.", true) + skills.add_experience(Skill::FISHING, fish.experience) + + unless (bait != -1 && !mob.inventory.contains(bait)) + mob.send_message("You need more #{name_of(:item, bait)}s to fish at this spot.") + stop + return + end + end + end + + mob.play_animation(@tool.animation) + end + + # Stops this action. + def stop() + super + mob.stop_animation + end + + def equals(other) + return (get_class == other.get_class and @spot == other.spot and @position == other.position && @options == @other.options) + end + +end + +# Intercepts the NpcAction message to determine whether or not a clicked npc was a fishing spot. +on :message, :npc_action do |ctx, player, message| + npc = $world.npc_repository.get(message.index) + spot = FISHING_SPOTS[npc.id] + + unless spot.nil? + player.start_action(FishingAction.new(player, npc.position, spot, message.option)) + ctx.break_handler_chain + end +end \ No newline at end of file diff --git a/data/plugins/skill/fishing/plugin.xml b/data/plugins/skill/fishing/plugin.xml new file mode 100644 index 00000000..64f84d78 --- /dev/null +++ b/data/plugins/skill/fishing/plugin.xml @@ -0,0 +1,20 @@ + + + skill-fishing + 1 + Fishing + Adds the fishing skill. + + Linux + Major + + + + + + + + + util + + \ No newline at end of file diff --git a/data/plugins/skill/fishing/spot.rb b/data/plugins/skill/fishing/spot.rb new file mode 100644 index 00000000..9150c066 --- /dev/null +++ b/data/plugins/skill/fishing/spot.rb @@ -0,0 +1,26 @@ + +# The hash of fishing spots. +FISHING_SPOTS = {} + +# A Fishing spot. +class Spot + attr_reader :tools, :first_fish, :second_fish + + # Creates the fishing spot. + def initialize(tools, first_fish, second_fish) + @tools = tools.map { |id| FISHING_TOOLS[id] } + @first_fish = first_fish.map { |fish| CATCHABLE_FISH[fish] } + @second_fish = second_fish.map { |fish| CATCHABLE_FISH[fish] } + end + +end + +# Appends a fishing spot to the hash. +def append_spot(id, spot) + FISHING_SPOTS[id] = spot +end + +append_spot(309, Spot.new([:fly_fishing_rod, :fishing_rod], [:trout, :salmon], [:pike])) +append_spot(312, Spot.new([:lobster_cage, :harpoon], [:lobster], [:tuna, :swordfish])) +append_spot(313, Spot.new([:big_net, :harpoon], [:mackerel, :cod], [:bass, :shark])) +append_spot(316, Spot.new([:small_net, :fishing_rod], [:shrimp, :anchovy], [:sardine, :herring])) \ No newline at end of file diff --git a/data/plugins/skill/fishing/tool.rb b/data/plugins/skill/fishing/tool.rb new file mode 100644 index 00000000..e2ce56a7 --- /dev/null +++ b/data/plugins/skill/fishing/tool.rb @@ -0,0 +1,47 @@ +require 'java' + +java_import 'org.apollo.game.model.Animation' + +# The hash of fishing tool names to Tools. +FISHING_TOOLS = {} + +# A fishing tool. +class Tool + attr_reader :id, :bait, :animation, :message, :name + + # Creates the tool. + def initialize(id, bait=[], animation, message) + @id = id + @bait = bait + @animation = Animation.new(animation) + @message = message + + @name = name_of(:item, id) + end + +end + + +private + +# Appends a tool with the specified name to the hash. +def append_tool(name, tool) + FISHING_TOOLS[name] = tool +end + +HARPOON_ANIMATION_ID = 618 +CAGE_ANIMATION_ID = 619 +NET_ANIMATION_ID = 620 +ROD_ANIMATION_ID = 622 + +# TODO The other feathers that can be used +FISHING_ROD_BAIT = [ 313 ] +FLY_FISHING_ROD_BAIT = [ 314 ] + +append_tool(:lobster_cage, Tool.new(301, CAGE_ANIMATION_ID, 'You attempt to catch a lobster...')) +append_tool(:small_net, Tool.new(303, NET_ANIMATION_ID, 'You cast out your net...')) +append_tool(:big_net, Tool.new(305, NET_ANIMATION_ID, 'You cast out your net...')) +append_tool(:harpoon, Tool.new(311, HARPOON_ANIMATION_ID, 'You start harpooning fish...')) + +append_tool(:fishing_rod, Tool.new(307, FISHING_ROD_BAIT, ROD_ANIMATION_ID, 'You attempt to catch a fish...')) +append_tool(:fly_fishing_rod, Tool.new(309, FLY_FISHING_ROD_BAIT, ROD_ANIMATION_ID, 'You attempt to catch a fish...'))