Housekeeping

This commit is contained in:
KeepBotting
2019-03-26 14:05:40 -04:00
parent a0c78ced90
commit 739c331860
135 changed files with 4 additions and 4 deletions
+41
View File
@@ -0,0 +1,41 @@
# 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, hash)
unless hash.has_keys?(:id, :level, :experience)
fail 'Hash must contain an id, level, and experience.'
end
CATCHABLE_FISH[name] = Fish.new(hash[:id], hash[:level], hash[:experience])
end
append_fish :shrimp, id: 317, level: 1, experience: 10
append_fish :sardine, id: 327, level: 5, experience: 20
append_fish :herring, id: 345, level: 10, experience: 30
append_fish :anchovy, id: 321, level: 15, experience: 40
append_fish :mackerel, id: 353, level: 16, experience: 20
append_fish :trout, id: 335, level: 20, experience: 50
append_fish :cod, id: 341, level: 23, experience: 45
append_fish :pike, id: 349, level: 25, experience: 60
append_fish :salmon, id: 331, level: 30, experience: 70
append_fish :tuna, id: 359, level: 35, experience: 80
append_fish :lobster, id: 377, level: 40, experience: 90
append_fish :bass, id: 363, level: 46, experience: 100
append_fish :swordfish, id: 371, level: 50, experience: 100
append_fish :shark, id: 383, level: 76, experience: 110
+120
View File
@@ -0,0 +1,120 @@
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 :position, :options, :spot, :started, :tool
# Creates the FishingAction.
def initialize(mob, position, spot, option)
super(4, true, mob, position, 1)
@position = position
@spot = spot
@tool = spot.tools[option - 1]
@options = (option == 1) ? spot.first_fish : spot.second_fish
@minimum_level = @options.map(&:level).min
end
# Returns whether or not a catch is successful.
def successful_catch(level, requirement)
[level - requirement + 5, 30].min > rand(40)
end
# Starts the fishing process.
def start_fishing
@started = true
mob.send_message(tool.message)
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.downcase} to fish at this spot.")
stop
return
end
bait = find_bait
if bait == -1
mob.send_message("You need #{name_of(:item, bait).downcase}s to fish at this spot.")
stop
return
end
if @started
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.nil?
inventory.add(fish.id)
name = fish.name
mob.send_message("You catch #{name.end_with?('s') ? 'some' : 'a'} #{name.downcase}.")
skills.add_experience(Skill::FISHING, fish.experience)
if find_bait == -1
mob.send_message("You need more #{name_of(:item, bait).downcase}s to fish at this spot.")
stop
return
end
end
else
start_fishing
end
mob.play_animation(@tool.animation)
end
# Finds the id of the first piece of bait in the player's inventory, or nil if no bait is
# required, or -1 if the player's inventory does not contain any valid bait.
def find_bait
baits = @tool.bait
baits.empty? ? nil : baits.find(-1) { |bait| mob.inventory.contains(bait) }
end
# Stops this action.
def stop
super
mob.stop_animation
end
def equals(other)
get_class == other.get_class && @spot == other.spot && @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 |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))
message.terminate
end
end
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<plugin>
<id>skill-fishing</id>
<version>1</version>
<name>Fishing</name>
<description>Adds the fishing skill.</description>
<authors>
<author>Linux</author>
<author>Major</author>
</authors>
<scripts>
<script>fish.rb</script>
<script>tool.rb</script>
<script>spot.rb</script>
<script>fishing.rb</script>
</scripts>
<dependencies>
<dependency>util</dependency>
</dependencies>
</plugin>
+26
View File
@@ -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]))
+60
View File
@@ -0,0 +1,60 @@
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 :animation, :bait, :id, :message, :name
# Creates the tool.
def initialize(id, animation, message, bait)
@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 tool(name, hash)
unless hash.has_keys?(:id, :animation, :message)
fail 'Hash must contain an id, animation, and message.'
end
bait = hash[:bait] || []
FISHING_TOOLS[name] = Tool.new(hash[:id], hash[:animation], hash[:message], bait)
end
# The harpoon fishing animation id.
HARPOON_ANIMATION = 618
# The cage fishing animation id.
CAGE_ANIMATION = 619
# The net fishing animation id.
NET_ANIMATION = 620
# The rod fishing animation id.
ROD_ANIMATION = 622
# TODO: The other feathers that can be used
FISHING_ROD_BAIT = [313]
FLY_FISHING_ROD_BAIT = [314]
tool :lobster_cage, id: 301, animation: CAGE_ANIMATION, message: 'You attempt to catch a lobster...'
tool :small_net, id: 303, animation: NET_ANIMATION, message: 'You cast out your net...'
tool :big_net, id: 305, animation: NET_ANIMATION, message: 'You cast out your net...'
tool :harpoon, id: 311, animation: HARPOON_ANIMATION, message: 'You start harpooning fish...'
tool :fishing_rod, id: 307, animation: ROD_ANIMATION, message: 'You attempt to catch a fish...',
bait: FISHING_ROD_BAIT
tool :fly_fishing_rod, id: 309, animation: ROD_ANIMATION, message: 'You attempt to catch a fish...',
bait: FLY_FISHING_ROD_BAIT
+95
View File
@@ -0,0 +1,95 @@
require 'java'
java_import 'org.apollo.game.action.Action'
java_import 'org.apollo.util.LanguageUtil'
# A herb is an ingredient that requires identification before being used.
class Herb < Ingredient
include HerbloreMethod
attr_reader :unidentified, :level, :experience
def initialize(item_id, unidentified, level, experience)
super item_id
@unidentified = unidentified
@level = level
@experience = experience
end
def invoke(player, _id, slot)
item = player.inventory.get(slot)
player.start_action(HerbIdentificationAction.new(player, self, slot, item))
end
end
# An action that makes a player identify a herb.
class HerbIdentificationAction < Action
attr_reader :herb, :slot, :item, :pulses
def initialize(player, herb, slot, item)
super(0, true, player)
@herb = herb
@slot = slot
@item = item
@pulses = 0
end
def execute
if @pulses == 0
unless check_skill(mob, @herb.level, 'identify this herb')
stop
return
end
end
execute_action
@pulses += 1
end
def execute_action
player = mob
inventory = player.inventory
if inventory.remove_slot(@slot, 1) == 1
identified = @herb.item
inventory.add(identified)
article = LanguageUtil.getIndefiniteArticle(identified.definition.name)
player.skill_set.add_experience(Skill::HERBLORE, @herb.experience)
player.send_message("This herb is #{article} #{identified.definition.name}.")
end
stop
end
def equals(other)
get_class == other.get_class && slot == other.slot && herb == other.herb
end
end
# Appends a herb to the InventoryItemMessage interception.
def append_herb(item_id, unidentified, level, experience)
herb = Herb.new(item_id, unidentified, level, experience)
append_herblore_item(herb, unidentified)
herb
end
# Herbs
GUAM_LEAF = append_herb(249, 199, 1, 2.5)
MARRENTILL = append_herb(251, 201, 5, 3.8)
TARROMIN = append_herb(253, 203, 11, 5)
HARRALANDER = append_herb(255, 205, 20, 6.3)
RANARR = append_herb(257, 207, 25, 7.5)
TOADFLAX = append_herb(2998, 3049, 30, 8)
IRIT_LEAF = append_herb(259, 209, 40, 8.8)
AVANTOE = append_herb(261, 211, 48, 10)
KWUARM = append_herb(263, 213, 54, 11.3)
SNAPDRAGON = append_herb(3000, 3051, 59, 11.8)
CADANTINE = append_herb(265, 215, 65, 12.5)
LANTADYME = append_herb(2481, 2485, 67, 13.1)
DWARF_WEED = append_herb(267, 217, 70, 13.8)
TORSTOL = append_herb(269, 219, 75, 15)
@@ -0,0 +1,102 @@
# Thanks to Sillhouette <http://www.rune-server.org/members/silhouette> for posting
# a large amount of Herblore skill data which has been thankfully used in this plugin.
require 'java'
java_import 'org.apollo.game.message.impl.SetWidgetItemModelMessage'
java_import 'org.apollo.game.model.entity.Skill'
HERBLORE_DIALOGUE = 4429
HERBLORE_ITEM = {}
HERBLORE_ITEM_ON_ITEM = {}
DRINK_ITEM = {}
# A module which describes an invocable method of the Herblore skill.
module HerbloreMethod
def self.new
fail 'You cannot instantiate this module!'
end
def invoke(_player, _primary, _secondary)
fail 'You must implement the invocation of HerbloreMethod!'
end
end
# The ItemOnItemMessage listener for all Herblore-related functions.
on :message, :item_on_item do |player, message|
primary = message.id
secondary = message.target_id
hash = HERBLORE_ITEM_ON_ITEM[primary]
if hash.nil?
secondary = message.id
primary = message.target_id
hash = HERBLORE_ITEM_ON_ITEM[primary]
end
unless hash.nil?
method = hash[secondary]
unless method.nil?
method.invoke(player, primary, secondary)
message.terminate
end
end
end
# The ItemOptionMessage listener for all Herblore-related functions.
on :message, :first_item_option do |player, message|
id = message.id
method = HERBLORE_ITEM[id]
unless method.nil?
method.invoke(player, id, message.slot)
message.terminate
end
method = DRINK_ITEM[id]
unless method.nil?
method.invoke(player, id, message.slot)
message.terminate
end
end
# Utility for adding the various Herblore methods to the handled constant arrays.
def append_herblore_item(method, key, secondary = -1)
if secondary == -1
HERBLORE_ITEM[key] = method
else
hash = HERBLORE_ITEM_ON_ITEM[key]
hash = {} if hash.nil?
hash[secondary] = method
HERBLORE_ITEM_ON_ITEM[key] = hash
end
end
# Utility method for checking if a player's inventory has a of the specified id, with optionally
# the specified amount (1 by default), at the specified slot.
def check_slot(player, slot, id, amount = 1)
item = player.inventory.get(slot)
!item.nil? && item.id == id && item.amount >= amount
end
# Utility method for checking if a player's Herblore (maximum) level is at the required level. Also
# informs the player if this is not the case with use of the action variable, like so:
# "You need a Herblore level of at least #{required.to_s} to #{action}."
def check_skill(player, required, action)
if required > player.skill_set.get_skill(Skill::HERBLORE).current_level
player.send_message("You need a Herblore level of at least #{required} to #{action}.")
return false
end
true
end
# Opens a 'make' dialogue for the specified player, displaying the specified item. Optionally, a
# listener can be used for the dialogue.
def open_dialogue(player, item, listener = nil)
player.send(SetWidgetItemModelMessage.new(1746, item, 170))
player.interface_set.open_dialogue(listener, HERBLORE_DIALOGUE)
end
@@ -0,0 +1,251 @@
require 'java'
java_import 'org.apollo.game.action.Action'
java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.Item'
java_import 'org.apollo.game.model.inter.EnterAmountListener'
java_import 'org.apollo.game.model.inter.dialogue.DialogueAdapter'
GRINDING_ANIM = Animation.new(364)
PESTLE_MORTAR = 233
# An ingredient which can be used for making (unfinished) potions.
class Ingredient
attr_reader :item_id, :item
def initialize(item)
@item_id = item
@item = Item.new(item) # Share item instances.
end
# Checks if the specified player has the specified amount of this ingredient. Optionally, they
# can immediately be removed if that amount was indeed found.
def check_remove(player, amount, remove)
inventory = player.inventory
counter = 0
inventory.items.each do |inv_item|
break unless counter < amount
next if inv_item.nil?
id = inv_item.id
inventory_amount = inv_item.amount
if id == @item_id
if inventory_amount >= amount
inventory.remove(@item_id, amount) if remove
return true
else
counter += inventory_amount
end
end
end
if counter >= amount
inventory.remove(@item_id, amount) if remove
return true
end
false
end
end
# An ingredient which needs to be grinded before being usable for Herblore.
class GroundIngredient < Ingredient
include HerbloreMethod
attr_reader :raw
def initialize(item_id, raw)
super(item_id)
@raw = raw
end
def invoke(player, _pestle_mortar, _ingredient)
action = GrindingAction.new(player, self)
listener = GrindingDialogueListener.new(player, action)
open_dialogue(player, @item_id, listener)
end
end
# A DialogueAdapter used for grinding ingredients. It is also used as an EnterAmountListener for
# the amount of grinding actions.
class GrindingDialogueListener < DialogueAdapter
include EnterAmountListener
attr_reader :player, :action
def initialize(player, action)
super()
@player = player
@action = action
end
# Called when a button has been clicked whilst the dialogue was opened.
def buttonClicked(button)
amount = get_amount(button)
return false if amount == 0
interfaces = @player.interface_set
interfaces.close
if amount == -1
interfaces.open_enter_amount_dialogue(self)
return true
end
amount = player.inventory.get_amount(@action.ingredient.raw) if amount == -2
execute(amount)
end
# Called when an amount of grinding actions has been entered.
def amountEntered(amount)
execute(amount) if amount > 0
end
# Called to set the action(s) in motion.
def execute(amount)
@action.set_amount(amount)
@player.start_action(@action)
end
# Gets the amount of actions based on the specified button id.
def get_amount(button)
case button
when 2799 then return 1
when 2798 then return 5
when 1748 then return -1
when 1747 then return -2
else return 0
end
end
end
# An action which makes the player grind one or more GrindedIngredients from their 'raw' form.
class GrindingAction < Action
attr_reader :ingredient, :amount, :pulses, :slot, :listener
def initialize(player, ingredient)
super(0, true, player)
@ingredient = ingredient
@pulses = 0
end
def execute
grind
@pulses += 1
end
# Performs the grinding action once the materials have been checked.
def grind
if @pulses == 0
mob.play_animation GRINDING_ANIM
elsif @pulses == 1
unless gather_materials
stop
return
end
player = mob
inventory = player.inventory
item = inventory.get(@slot)
name = item.definition.name.downcase
player.send_message("You grind the #{name} to dust.")
inventory.reset(@slot)
inventory.add(@ingredient.item)
set_delay(1)
elsif @pulses == 2
mob.stop_animation
continue
end
end
# Checks if the player has the required materials to perform the (next) action.
def gather_materials
items = mob.inventory.items
pst_mrt = false
ingr = false
raw = @ingredient.raw
(0...items.length).each do |slot|
item = items[slot]
next if item.nil?
id = item.id
if id == PESTLE_MORTAR && !pst_mrt
pst_mrt = true
elsif id == raw && !ingr
ingr = true
@slot = slot
end
return true if pst_mrt && ingr
end
mob.send_message("You do not have any more #{name_of(raw).downcase}s.")
false
end
# Either invokes the stop() method in Action to shut it down
# or continues to the next ingredient.
def continue
@amount -= 1
if @amount > 0
set_delay(0)
@pulses = -1
else
stop
end
end
# Sets the amount of actions.
def set_amount(amount)
@amount = amount
end
def stop
super
mob.inventory.remove_listener(@listener) unless listener.nil?
end
def equals(other)
get_class == other.get_class && @ingredient == other.ingredient
end
end
# Appends a ground ingredient to the ItemOnItemMessage listener interception.
def append_ground(id, raw)
ground = GroundIngredient.new(id, raw)
append_herblore_item(ground, PESTLE_MORTAR, raw)
ground
end
# Normal ingredients
EYE_NEWT = Ingredient.new(221)
RED_SPIDERS_EGGS = Ingredient.new(223)
LIMPWURT_ROOT = Ingredient.new(225)
SNAPE_GRASS = Ingredient.new(231)
WHITE_BERRIES = Ingredient.new(239)
WINE_ZAMORAK = Ingredient.new(245)
JANGERBERRIES = Ingredient.new(247)
TOADS_LEGS = Ingredient.new(2152)
MORT_MYRE_FUNGI = Ingredient.new(2970)
POTATO_CACTUS = Ingredient.new(3138)
PHOENIX_FEATHER = Ingredient.new(4621)
FROG_SPAWN = Ingredient.new(5004)
PAPAYA_FRUIT = Ingredient.new(5972)
POISON_IVY_BERRIES = Ingredient.new(6018)
YEW_ROOTS = Ingredient.new(6049)
MAGIC_ROOTS = Ingredient.new(6051)
# Ground ingredients
UNICORN_HORN_DUST = append_ground(235, 237)
DRAGON_SCALE_DUST = append_ground(241, 243)
CHOCOLATE_DUST = append_ground(1975, 1973)
CRUSHED_NEST = append_ground(6693, 5075)
@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<plugin>
<id>skill-herblore</id>
<version>0.9</version>
<name>Herblore</name>
<description>Adds the Herblore skill.</description>
<authors>
<author>Chris Fletcher</author>
<author>Major</author>
</authors>
<scripts>
<script>herblore.rb</script>
<script>ingredient.rb</script>
<script>herb.rb</script>
<script>potion.rb</script>
</scripts>
<dependencies />
</plugin>
+372
View File
@@ -0,0 +1,372 @@
require 'java'
java_import 'org.apollo.game.action.Action'
java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.Item'
java_import 'org.apollo.game.model.entity.Skill'
java_import 'org.apollo.game.model.inter.EnterAmountListener'
java_import 'org.apollo.game.model.inter.dialogue.DialogueAdapter'
private
WATER_VIAL_ID = 227
EMPTY_VIAL_ID = 229
MIXING_ANIM = Animation.new(363)
# Represents an unfinished potion which can be invoked as a HerbloreMethod and used as an
# ingredient.
class UnfinishedPotion < Ingredient
include HerbloreMethod
attr_reader :herb, :level
def initialize(item_id, herb, level)
super(item_id)
@herb = herb
@level = level
end
def invoke(player, _primary, _secondary)
action = UnfinishedMixingAction.new(player, self)
listener = UnfinishedMixingDialogueListener.new(player, action)
open_dialogue(player, @item_id, listener)
end
end
# Represents a finished potion which can be invoked as a HerbloreMethod.
class FinishedPotion
include HerbloreMethod
attr_reader :item, :ingredients, :level, :experience
def initialize(item, ingredients, level, experience)
@item = Item.new(item)
@ingredients = ingredients
@level = level
@experience = experience
end
def invoke(player, primary, secondary)
action = FinishedMixingAction.new(player, primary, secondary, self)
listener = FinishedMixingDialogueListener.new(player, action)
open_dialogue(player, @item.id, listener)
end
end
# A DialogueAdapter used for mixing potions. It is also used as an EnterAmountListener for the
# amount of mixing actions.
class MixingDialogueListener < DialogueAdapter
include EnterAmountListener
attr_reader :player, :action
def initialize(player, action)
super()
@player = player
@action = action
end
# Called when a button has been clicked whilst the dialogue was opened.
def buttonClicked(button)
amount = get_amount(button)
return false if amount == 0
interfaces = @player.interface_set
interfaces.close
if amount == -1
interfaces.open_enter_amount_dialogue(self)
return true
end
amount = calculate_maximum if amount == -2
execute(amount)
true
end
# Called when an amount of mixing actions has been entered.
def amountEntered(amount)
execute(amount) if amount > 0
end
# Called to set the action(s) in motion.
def execute(amount)
@action.set_amount(amount)
@player.start_action(@action)
end
def calculate_maximum(_code)
# Override for potion-specific amount calculation.
end
# Gets the amount of actions based on the specified button id.
def get_amount(button)
case button
when 2799 then return 1
when 2798 then return 5
when 1748 then return -1
when 1747 then return -2
else return 0
end
end
end
# A MixingDialogueListener used for mixing unfinished potions.
class UnfinishedMixingDialogueListener < MixingDialogueListener
def calculate_maximum
inventory = @player.inventory
amount = inventory.get_amount(WATER_VIAL_ID)
return 0 if amount <= 0
herbs = inventory.get_amount(@action.potion.herb.item.id)
[herbs, amount].min
end
end
# A MixingDialogueListener used for mixing finished potions.
class FinishedMixingDialogueListener < MixingDialogueListener
def calculate_maximum
inventory = @player.inventory
amount = inventory.capacity
@action.potion.ingredients.each do |ingredient|
item_amount = inventory.get_amount(ingredient.item.id)
amount = item_amount if amount > item_amount
end
amount
end
end
# An Action which handles the none-finished-dependent mixing.
class MixingAction < Action
attr_reader :potion, :amount, :started, :pulses, :action, :listener
def initialize(player, potion, action)
super(1, true, player)
@potion = potion
@started = false
@pulses = 0
@action = action
@action.freeze
end
def execute
if @pulses == 0
unless @started
unless check_skill(mob, @potion.level, @action)
stop
return
end
@started = true
end
unless gather_materials
stop
return
end
end
mob.play_animation(MIXING_ANIM)
execute_action
@amount -= 1
@amount > 0 ? @pulses = 0 : stop
end
def stop
super()
mob.inventory.remove_listener(@listener) unless @listener.nil?
end
def execute_action
# Override for action execution.
end
def gather_materials
# Override for ingredient checking and gathering
false
end
# Sets the amount of actions.
def set_amount(amount)
@amount = amount
end
def equals(other)
get_class == other.get_class && @potion == other.potion
end
end
# A MixingAction which handles the execution of making UnfinishedPotions.
class UnfinishedMixingAction < MixingAction
attr_reader :slots
def initialize(player, potion)
super(player, potion, 'use this herb.')
end
def execute_action
name = @potion.herb.item.definition.name
player = mob
inventory = player.inventory
created = name.sub(/ leaf$/, '')
message = "You put the #{name} in the water to make an unfinished #{created} potion."
player.send_message(message)
@slots.each do |slot, amount|
unless inventory.remove_slot(slot, amount)
stop
return
end
end
inventory.add(@potion.item)
end
def gather_materials
@slots = {}
inventory = mob.inventory
vial_slot = inventory.slot_of(WATER_VIAL_ID)
if vial_slot == -1
mob.send_message('You do not have any more vials of water.')
return false
end
item = @potion.herb.item
herb_slot = inventory.slot_of(item.id)
if herb_slot == -1
mob.send_message("You do not have any more #{item.definition.name}.")
return false
end
@slots[vial_slot] = 1
@slots[herb_slot] = 1
true
end
end
# A MixingAction which handles the execution of making FinishedPotions.
class FinishedMixingAction < MixingAction
attr_reader :unfinished, :ingredient, :slots
def initialize(player, unfinished, ingredient, potion)
super(player, potion, 'mix this potion')
@unfinished = unfinished
@ingredient = ingredient
end
def execute_action
ingredient = name_of(:item, @ingredient).downcase
name = @potion.item.definition.name.sub('(3)', '')
name = "#{LanguageUtil.get_indefinite_article(name)} #{name}"
mob.send_message("You add the #{ingredient} to the mixture to make #{name}.")
mob.skill_set.add_experience(Skill::HERBLORE, @potion.experience)
inventory = mob.inventory
@slots.each do |slot, amount|
unless inventory.remove_slot(slot, amount) # TODO: will this remove stuff incorrectly?
stop
return
end
end
inventory.add(@potion.item)
end
def gather_materials
@slots = {}
inventory = mob.inventory
vial_slot = inventory.slot_of(@unfinished)
if vial_slot == -1
mob.send_message('You do not have enough unfinished potions.')
return false
end
ingredient_slot = inventory.slot_of(@ingredient)
if ingredient_slot == -1
mob.send_message('You do not have enough ingredients.')
return false
end
@slots[vial_slot] = 1
@slots[ingredient_slot] = 1
true
end
end
# Appends a finished potion to the ItemOnItemMessage handling interception.
def finished_potion(item, unfinished, ingredient, level, experience)
potion = FinishedPotion.new(item, [unfinished, ingredient], level, experience)
append_herblore_item(potion, unfinished.item_id, ingredient.item_id)
potion
end
# Appends an unfinished potion to the ItemOnItemMessage handling interception.
def unfinished_potion(item, herb, level)
potion = UnfinishedPotion.new(item, herb, level)
append_herblore_item(potion, herb.item_id, WATER_VIAL_ID)
potion
end
# Unfinished potions
UNF_GUAM = unfinished_potion(91, GUAM_LEAF, 1) # 3
UNF_MARRENTILL = unfinished_potion(93, MARRENTILL, 5)
UNF_TARROMIN = unfinished_potion(95, TARROMIN, 12)
UNF_HARRALANDER = unfinished_potion(97, HARRALANDER, 22)
UNF_RANARR = unfinished_potion(99, RANARR, 30)
UNF_TOADFLAX = unfinished_potion(3002, TOADFLAX, 34)
UNF_IRIT = unfinished_potion(101, IRIT_LEAF, 45)
UNF_AVANTOE = unfinished_potion(103, AVANTOE, 50)
UNF_KWUARM = unfinished_potion(105, KWUARM, 55)
UNF_SNAPDRAGON = unfinished_potion(3004, SNAPDRAGON, 63)
UNF_CADANTINE = unfinished_potion(107, CADANTINE, 66)
UNF_LANTADYME = unfinished_potion(2483, LANTADYME, 69)
UNF_DWARF_WEED = unfinished_potion(109, DWARF_WEED, 72)
UNF_TORSTOL = unfinished_potion(111, TORSTOL, 78)
# Finished potions
ATTACK_POT = finished_potion(121, UNF_GUAM, EYE_NEWT, 1, 25) # 3
ANTIPOISON_POT = finished_potion(175, UNF_MARRENTILL, UNICORN_HORN_DUST, 5, 37.5)
STRENGTH_POT = finished_potion(115, UNF_TARROMIN, LIMPWURT_ROOT, 12, 50)
RESTORE_POT = finished_potion(127, UNF_HARRALANDER, RED_SPIDERS_EGGS, 18, 62.5)
ENERGY_POT = finished_potion(3010, UNF_HARRALANDER, CHOCOLATE_DUST, 26, 67.5)
DEFENCE_POT = finished_potion(133, UNF_RANARR, WHITE_BERRIES, 30, 75)
AGILITY_POT = finished_potion(3034, UNF_TOADFLAX, TOADS_LEGS, 34, 80)
PRAYER_POT = finished_potion(139, UNF_RANARR, SNAPE_GRASS, 38, 87.5)
SUPER_ATTACK_POT = finished_potion(145, UNF_IRIT, EYE_NEWT, 45, 100)
SUPER_ANTIPOISON_POT = finished_potion(181, UNF_IRIT, UNICORN_HORN_DUST, 48, 106.3)
FISHING_POT = finished_potion(151, UNF_AVANTOE, SNAPE_GRASS, 50, 112.5)
SUPER_ENERGY_POT = finished_potion(3018, UNF_AVANTOE, MORT_MYRE_FUNGI, 52, 117.5)
SUPER_STRENGTH_POT = finished_potion(157, UNF_KWUARM, LIMPWURT_ROOT, 55, 125)
WEAPON_POISON = finished_potion(187, UNF_KWUARM, DRAGON_SCALE_DUST, 60, 137.5)
SUPER_RESTORE_POT = finished_potion(3026, UNF_SNAPDRAGON, RED_SPIDERS_EGGS, 63, 142.5)
SUPER_DEFENCE_POT = finished_potion(163, UNF_CADANTINE, WHITE_BERRIES, 66, 150)
ANTIFIRE_POT = finished_potion(2428, UNF_LANTADYME, DRAGON_SCALE_DUST, 69, 157.5)
RANGING_POT = finished_potion(169, UNF_DWARF_WEED, WINE_ZAMORAK, 72, 162.5)
MAGIC_POT = finished_potion(3042, UNF_LANTADYME, POTATO_CACTUS, 76, 172.5)
ZAMORAK_BREW = finished_potion(189, UNF_TORSTOL, JANGERBERRIES, 78, 175)
+81
View File
@@ -0,0 +1,81 @@
require 'java'
java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.Graphic'
java_import 'org.apollo.game.model.entity.Skill'
ALCHEMY_SPELLS = {}
ILLEGAL_ALCH_ITEMS = [995]
# A spell that alchemises an item.
class AlchemySpell < Spell
attr_reader :animation, :graphic, :multiplier, :experience, :delay
def initialize(level, elements, experience, animation, graphic, multiplier, delay)
super(level, elements, experience)
@animation = animation
@graphic = graphic
@multiplier = multiplier
@delay = delay
end
end
# An Action that performs an AlchemySpell.
class AlchemyAction < ItemSpellAction
def initialize(player, alchemy, slot, item)
super(player, alchemy, slot, item)
end
def illegal_item?
ILLEGAL_ALCH_ITEMS.include?(@item.id)
end
def execute_action
if @pulses == 0
mob.play_animation(@spell.animation)
mob.play_graphic(@spell.graphic)
inventory = mob.inventory
gold = (item.definition.value * @spell.multiplier)
inventory.remove(inventory.get(@slot).id, 1)
inventory.add(995, gold)
mob.skill_set.add_experience(Skill::MAGIC, @spell.experience)
set_delay(@spell.delay)
elsif @pulses == 1
mob.stop_animation
mob.stop_graphic
stop
end
end
end
private
# The height of the graphic.
GRAPHIC_HEIGHT = 100
# Inserts an `AlchemySpell` into the hash of available alchemy spells.
def alchemy(_name, hash)
unless hash.has_keys?(:button, :level, :fires, :animation, :graphic, :multiplier, :experience, :delay)
fail 'Hash must have button, level, fires, animation, graphic, multiplier, experience, delay keys.'
end
id, multiplier = hash[:button], hash[:multiplier]
level, experience = hash[:level], hash[:experience]
runes = { NATURE => 1, FIRE => hash[:fires] }
animation = Animation.new(hash[:animation])
graphic = Graphic.new(hash[:graphic], 0, GRAPHIC_HEIGHT)
delay = hash[:delay]
ALCHEMY_SPELLS[id] = AlchemySpell.new(level, runes, experience, animation, graphic, multiplier, delay)
end
alchemy :low_level, button: 1_162, level: 21, fires: 3, animation: 712, graphic: 112, multiplier: 0.4, experience: 31, delay: 2
alchemy :high_level, button: 1_178, level: 55, fires: 5, animation: 713, graphic: 113, multiplier: 0.6, experience: 65, delay: 3
+89
View File
@@ -0,0 +1,89 @@
require 'java'
java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.Graphic'
java_import 'org.apollo.game.model.Item'
java_import 'org.apollo.game.model.entity.Skill'
CONVERT_SPELLS = {}
BONES_ID = 526
CONVERT_ANIM = Animation.new(722)
CONVERT_GRAPHIC = Graphic.new(141, 0, 100)
# A `Spell` for converting items.
class ConvertSpell < Spell
attr_reader :reward
def initialize(level, elements, experience, reward)
super(level, elements, experience)
@reward = Item.new(reward)
end
end
# A `SpellAction` for a `ConvertSpell`.
class ConvertingAction < SpellAction
attr_reader :slots
def initialize(player, spell, slots)
super(player, spell)
@slots = slots
end
def execute_action
if @pulses == 0
mob.play_animation(CONVERT_ANIM)
mob.play_graphic(CONVERT_GRAPHIC)
inventory = mob.inventory
firing = (@slots.length * 2) < inventory.capacity
inventory.stop_firing_events unless firing # In case of many changes, wait with firing events
reward = @spell.reward
@slots.each { |slot| inventory.set(slot, reward) }
unless firing
inventory.start_firing_events
inventory.force_refresh
end
mob.skill_set.add_experience(Skill::MAGIC, @spell.experience)
set_delay(2)
elsif @pulses == 1
mob.stop_animation
mob.stop_graphic
stop
end
end
end
def bone_slots(player)
inventory = player.inventory
items = inventory.items
size = inventory.size
counter = 0
slots = []
(0...inventory.capacity).each do |slot|
break unless counter <= size
item = items[slot]
slots << slot if !item.nil? && item.id == BONES_ID
counter += 1
end
slots
end
def convert(_name, button, level, elements, experience, reward)
CONVERT_SPELLS[button] = ConvertSpell.new(level, elements, experience, reward)
end
convert :bones_to_bananas, 1159, 15, { NATURE => 1, WATER => 2, EARTH => 2 }, 25, 1963
convert :bones_to_peaches, 15877, 60, { NATURE => 2, WATER => 4, EARTH => 4 }, 35.5, 6883
+107
View File
@@ -0,0 +1,107 @@
require 'java'
java_import 'org.apollo.game.model.entity.EquipmentConstants'
AIR_ELEMENTS = {}
WATER_ELEMENTS = {}
EARTH_ELEMENTS = {}
FIRE_ELEMENTS = {}
AIR_RUNE = 556
WATER_RUNE = 555
EARTH_RUNE = 557
FIRE_RUNE = 554
MIND_RUNE = 558
CHAOS_RUNE = 562
DEATH_RUNE = 560
BLOOD_RUNE = 565
COSMIC_RUNE = 564
LAW_RUNE = 563
NATURE_RUNE = 561
SOUL_RUNE = 566
MIST_RUNE = 4695
DUST_RUNE = 4696
SMOKE_RUNE = 4697
MUD_RUNE = 4698
STEAM_RUNE = 4694
LAVA_RUNE = 4699
# An element of a spell.
class Element
attr_reader :runes, :staffs, :name
def initialize(runes, staffs, name = 'Null')
@runes = runes
@staffs = staffs
@name = name
end
def check_remove(player, amount, remove)
weapon = player.equipment.get(EquipmentConstants::WEAPON)
unless @staffs.nil? || weapon.nil?
@staffs.each { |staff| return true if weapon.id == staff }
end
inventory = player.inventory
found = {}
counter = 0
inventory.items.each do |item|
break unless counter < amount
next if item.nil?
amt = item.amount
@runes.each do |rune|
break unless counter < amount
id = item.id
if id == rune
if amt >= amount
inventory.remove(id, amount) if remove
return true
else
found[id] = amt
counter += amt
end
end
end
end
if counter >= amount
found.each { |id, amt| inventory.remove(id, amt) } if remove
return true
end
false
end
end
AIR_RUNES = [556, 4695, 4696, 4697]
WATER_RUNES = [555, 4695, 4698, 4694]
EARTH_RUNES = [557, 4696, 4697, 4698]
FIRE_RUNES = [554, 4697, 4694, 4699]
AIR_STAFFS = [1381, 1397, 1405]
WATER_STAFFS = [1383, 1395, 1403]
EARTH_STAFFS = [1385, 1399, 1407, 3053, 3054]
FIRE_STAFFS = [1387, 1393, 1401, 3053, 3054]
AIR = Element.new(AIR_RUNES, AIR_STAFFS, 'Air rune')
WATER = Element.new(WATER_RUNES, WATER_STAFFS, 'Water rune')
EARTH = Element.new(EARTH_RUNES, EARTH_STAFFS, 'Earth rune')
FIRE = Element.new(FIRE_RUNES, FIRE_STAFFS, 'Fire rune')
MIND = Element.new([MIND_RUNE], nil, 'Mind rune')
CHAOS = Element.new([CHAOS_RUNE], nil, 'Chaos rune')
DEATH = Element.new([DEATH_RUNE], nil, 'Death rune')
BLOOD = Element.new([BLOOD_RUNE], nil, 'Blood rune')
COSMIC = Element.new([COSMIC_RUNE], nil, 'Cosmic rune')
LAW = Element.new([LAW_RUNE], nil, 'Law rune')
NATURE = Element.new([NATURE_RUNE], nil, 'Nature rune')
SOUL = Element.new([SOUL_RUNE], nil, 'Soul rune')
+106
View File
@@ -0,0 +1,106 @@
require 'java'
java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.Graphic'
java_import 'org.apollo.game.model.Item'
ENCHANT_SPELLS = {}
ENCHANT_ITEMS = {}
RING_GFX = Graphic.new(238, 0, 100)
RING_ANIM = Animation.new(931)
LOW_NECK_GFX = Graphic.new(114, 0, 100)
LOW_NECK_ANIM = Animation.new(719)
MED_NECK_GFX = Graphic.new(115, 0, 100)
MED_NECK_ANIM = Animation.new(720)
HIGH_NECK_GFX = Graphic.new(116, 0, 100)
HIGH_NECK_ANIM = Animation.new(721)
ONYX_NECK_GFX = Graphic.new(452, 0, 100)
# A `Spell` for enchanting an item.
class EnchantSpell < Spell
attr_reader :button, :animation, :graphic, :delay
def initialize(button, level, elements, animation, graphic, delay, experience)
super(level, elements, experience)
@button = button
@animation = animation
@graphic = graphic
@delay = delay
end
end
# A `SpellAction` for an `EnchantSpell`.
class EnchantAction < ItemSpellAction
attr_reader :reward
def initialize(player, enchant, slot, item, reward)
super(player, enchant, slot, item)
@reward = Item.new(reward)
end
def illegal_item?
ENCHANT_ITEMS[@item.id].nil?
end
def execute_action
if @pulses == 0
mob.play_animation(@spell.animation)
mob.play_graphic(@spell.graphic)
mob.inventory.set(@slot, @reward)
mob.skill_set.add_experience(Skill::MAGIC, @spell.experience)
set_delay(@spell.delay)
elsif @pulses == 1
mob.stop_animation
mob.stop_graphic
stop
end
end
end
def enchant(button, level, elements, item, animation, graphic, delay, experience, reward)
enchant = EnchantSpell.new(button, level, elements, animation, graphic, delay, experience)
ENCHANT_SPELLS[item] = enchant
ENCHANT_ITEMS[item] = reward
end
SAPPHIRE_ELEMENTS = { COSMIC => 1, WATER => 1 }
EMERALD_ELEMENTS = { COSMIC => 1, AIR => 1 }
RUBY_ELEMENTS = { COSMIC => 1, FIRE => 5 }
DIAMOND_ELEMENTS = { COSMIC => 1, EARTH => 10 }
DSTONE_ELEMENTS = { COSMIC => 1, EARTH => 15, WATER => 15 }
ONYX_ELEMENTS = { COSMIC => 1, FIRE => 20, EARTH => 20 }
# Sapphire
enchant 1155, 7, SAPPHIRE_ELEMENTS, 1637, RING_ANIM, RING_GFX, 2, 17.5, 2550 # Ring
enchant 1155, 7, SAPPHIRE_ELEMENTS, 1656, LOW_NECK_ANIM, LOW_NECK_GFX, 1, 17.5, 3853 # Necklace
enchant 1155, 7, SAPPHIRE_ELEMENTS, 1692, LOW_NECK_ANIM, LOW_NECK_GFX, 1, 17.5, 1727 # Amulet
# Emerald
enchant 1165, 27, EMERALD_ELEMENTS, 1639, RING_ANIM, RING_GFX, 2, 37, 2552 # Ring
enchant 1165, 27, EMERALD_ELEMENTS, 1658, LOW_NECK_ANIM, LOW_NECK_GFX, 1, 37, 5521 # Necklace
enchant 1165, 27, EMERALD_ELEMENTS, 1696, LOW_NECK_ANIM, LOW_NECK_GFX, 1, 37, 1729 # Amulet
# Ruby
enchant 1176, 49, RUBY_ELEMENTS, 1641, RING_ANIM, RING_GFX, 2, 59, 2568 # Ring
enchant 1176, 49, RUBY_ELEMENTS, 1698, MED_NECK_ANIM, MED_NECK_GFX, 2, 59, 1725 # Amulet
# Diamond
enchant 1180, 57, DIAMOND_ELEMENTS, 1643, RING_ANIM, RING_GFX, 2, 67, 2570 # Ring
enchant 1180, 57, DIAMOND_ELEMENTS, 1700, MED_NECK_ANIM, MED_NECK_GFX, 2, 67, 1731 # Amulet
# Dragonstone
enchant 1187, 68, DSTONE_ELEMENTS, 1645, RING_ANIM, RING_GFX, 2, 78, 2572 # Ring
enchant 1187, 68, DSTONE_ELEMENTS, 1702, HIGH_NECK_ANIM, HIGH_NECK_GFX, 3, 78, 1712 # Amulet
# Onyx
enchant 6003, 87, ONYX_ELEMENTS, 6575, RING_ANIM, RING_GFX, 2, 97, 6583 # Ring
enchant 6003, 87, ONYX_ELEMENTS, 6581, HIGH_NECK_ANIM, ONYX_NECK_GFX, 2, 97, 6585 # Amulet
+172
View File
@@ -0,0 +1,172 @@
require 'java'
java_import 'org.apollo.game.action.Action'
java_import 'org.apollo.game.message.impl.DisplayTabInterfaceMessage'
java_import 'org.apollo.game.model.entity.EquipmentConstants'
java_import 'org.apollo.game.model.entity.Skill'
java_import 'org.apollo.cache.def.ItemDefinition'
# A `Message` to display the magic spellbook.
DISPLAY_SPELLBOOK = DisplayTabInterfaceMessage.new(6)
# A spell that can be cast.
class Spell
attr_reader :level, :elements, :experience
def initialize(level, elements, experience)
@level = level
@elements = elements
@experience = experience
end
end
# An `Action` for casting a `Spell`.
class SpellAction < Action
attr_reader :spell, :pulses
def initialize(mob, spell)
super(0, true, mob)
@spell = spell
@pulses = 0
end
def illegal_item?
false
end
def execute
if @pulses == 0
unless check_skill
stop
return
end
if illegal_item?
mob.send_message('You cannot use that spell on this item!')
stop
return false
end
unless process_elements
stop
return
end
end
execute_action
@pulses += 1
end
def execute_action
stop
end
def check_skill
required = @spell.level
if required > mob.skill_set.getSkill(Skill::MAGIC).current_level
mob.send_message("You need a Magic level of at least #{required} to cast this spell.")
return false
end
true
end
def process_elements
elements = @spell.elements
elements.each do |element, amount|
unless element.check_remove(mob, amount, false)
mob.send_message("You do not have enough #{element.name.split.map(&:capitalize) * ' '}s to cast this spell.")
return false
end
end
elements.each { |element, amount| element.check_remove(mob, amount, true) }
true
end
def equals(other)
get_class == other.get_class
end
end
# A `SpellAction` that verifies an input `Item` is legal.
class ItemSpellAction < SpellAction
attr_reader :slot, :item
def initialize(mob, spell, slot, item)
super(mob, spell)
@slot = slot
@item = item
end
# We override SpellAction#execute to implement an illegal item check (e.g. coins for alchemy)
def execute
if @pulses == 0
id = @item.id
mob.send(DISPLAY_SPELLBOOK)
# TODO: There has to be a better way to do this.
@spell.elements.each do |element, amount|
element.runes.each do |rune|
if id == rune && !element.check_remove(mob, amount + 1, false)
mob.send_message("You do not have enough #{element.name.split.map(&:capitalize) * ' '}s to cast this spell.")
stop
return false
end
end
end
end
super
end
end
# Intercepts the magic on item message.
on :message, :magic_on_item do |player, message|
spell = message.spell_id
alchemy = ALCHEMY_SPELLS[spell]
unless alchemy.nil?
slot = message.slot
item = player.inventory.get(slot)
player.start_action(AlchemyAction.new(player, alchemy, slot, item))
message.terminate
else
enchant = ENCHANT_SPELLS[message.id]
if !enchant.nil? && enchant.button == spell
slot = message.slot
item = player.inventory.get(slot)
player.start_action(EnchantAction.new(player, enchant, slot, item, ENCHANT_ITEMS[item.id]))
message.terminate
end
end
end
# Intercepts the button message
on :message, :button do |player, message|
button = message.widget_id
tele = TELEPORT_SPELLS[button]
unless tele.nil?
player.start_action(TeleportingAction.new(player, tele))
message.terminate
end
conv = CONVERT_SPELLS[button]
unless conv.nil?
slots = bone_slots(player)
if slots.length == 0
player.send_message("You can't convert these bones!")
else
player.start_action(ConvertingAction.new(player, conv, slots))
end
message.terminate
end
end
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<plugin>
<id>skill-magic</id>
<version>1</version>
<name>Magic</name>
<description>Adds the Magic skill.</description>
<authors>
<author>Chris Fletcher</author>
<author>Major</author>
</authors>
<scripts>
<script>magic.rb</script>
<script>element.rb</script>
<script>teleport.rb</script>
<script>alchemy.rb</script>
<script>enchant.rb</script>
<script>convert.rb</script>
</scripts>
<dependencies />
</plugin>
+101
View File
@@ -0,0 +1,101 @@
# Thanks to phl0w <http://www.rune-server.org/members/phl0w> for providing
# the correct destination coordinates of the ancient teleports.
require 'java'
java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.Graphic'
java_import 'org.apollo.game.model.Position'
java_import 'org.apollo.game.model.entity.Skill'
TELEPORT_SPELLS = {}
MODERN_TELE_ANIM = Animation.new(714)
MODERN_TELE_GRAPHIC = Graphic.new(111, 5, 100)
ANCIENT_TELE_END_GRAPHIC = Graphic.new(455)
ANCIENT_TELE_ANIM = Animation.new(1979)
ANCIENT_TELE_GRAPHIC = Graphic.new(392)
# A `Spell` that teleports a `Player` to another `Position`.
class TeleportSpell < Spell
attr_reader :ancient, :destination, :experience, :name
def initialize(ancient, level, elements, destination, experience, name)
super(level, elements, experience)
@ancient = ancient
@destination = destination
@name = name
end
end
# A `SpellAction` for a `TeleportSpell`.
class TeleportingAction < SpellAction
def initialize(mob, spell)
super(mob, spell)
end
def execute_action
@spell.ancient ? execute_ancient : execute_modern
end
def execute_modern
if @pulses == 0
mob.play_animation(MODERN_TELE_ANIM)
mob.play_graphic(MODERN_TELE_GRAPHIC)
elsif @pulses == 3
mob.stop_graphic
mob.stop_animation
mob.teleport(@spell.destination)
mob.skill_set.add_experience(Skill::MAGIC, @spell.experience)
stop
end
end
def execute_ancient
if @pulses == 0
mob.play_graphic(ANCIENT_TELE_GRAPHIC)
mob.play_animation(ANCIENT_TELE_ANIM)
set_delay(2)
elsif @pulses == 2
mob.stop_graphic
mob.stop_animation
mob.teleport(@spell.destination)
mob.skill_set.add_experience(Skill::MAGIC, @spell.experience)
stop
end
end
end
def tele(ancient = false, button, level, elements, x, y, experience, name)
position = Position.new(x, y)
TELEPORT_SPELLS[button] = TeleportSpell.new(ancient, level, elements, position, experience, name)
end
def ancient_tele(*args)
tele(true, *args)
end
# Modern teleports
tele 1_164, 25, { LAW => 1, AIR => 3, FIRE => 1 }, 3213, 3424, 35, 'Varrock'
tele 1_167, 31, { LAW => 1, AIR => 3, EARTH => 1 }, 3222, 3219, 41, 'Lumbridge'
tele 1_170, 37, { LAW => 1, AIR => 3, WATER => 1 }, 2965, 3379, 47, 'Falador'
tele 1_174, 45, { LAW => 1, AIR => 5 }, 2757, 3478, 55.5, 'Camelot'
tele 1_540, 51, { LAW => 2, WATER => 2 }, 2662, 3306, 61, 'Ardougne'
tele 1_541, 58, { LAW => 2, EARTH => 2 }, 2549, 3114, 68, 'the Watchtower'
tele 7_455, 61, { LAW => 2, FIRE => 2 }, 2871, 3590, 68, 'Trollheim'
tele 18_470, 64, { Element.new([1963], nil, 'Banana') => 1, LAW => 2, WATER => 2, FIRE => 2 },
2_754, 2_785, 76, 'Ape Atoll'
# Ancient teleports
ancient_tele 13_035, 54, { LAW => 2, FIRE => 1, AIR => 1 }, 3098, 9882, 64, 'Paddewwa'
ancient_tele 13_045, 60, { LAW => 2, SOUL => 2 }, 3320, 3338, 70, 'Senntisten'
ancient_tele 13_053, 66, { LAW => 2, BLOOD => 1 }, 3493, 3472, 76, 'Kharyll'
ancient_tele 13_061, 72, { LAW => 2, WATER => 4 }, 3003, 3470, 82, 'Lassar'
ancient_tele 13_069, 78, { LAW => 2, FIRE => 3, AIR => 2 }, 2966, 3_696, 88, 'Dareeyak'
ancient_tele 13_079, 84, { LAW => 2, SOUL => 2 }, 3163, 3664, 94, 'Carrallangar'
ancient_tele 13_087, 90, { LAW => 2, BLOOD => 2 }, 3287, 3883, 100, 'Annakarl'
ancient_tele 13_095, 96, { LAW => 2, WATER => 8 }, 2972, 3873, 106, 'Ghorrock'
+20
View File
@@ -0,0 +1,20 @@
GEMSTONES = {}
# A gemstone that can be received when mining.
class Gemstone
attr_reader :id, :chance
def initialize(id, chance)
@id = id
@chance = chance
end
end
def gem(gem)
GEMSTONES[gem.id] = gem
end
gem(Gemstone.new(1623, 0)) # uncut sapphire
gem(Gemstone.new(1605, 0)) # uncut emerald
gem(Gemstone.new(1619, 0)) # uncut ruby
gem(Gemstone.new(1617, 0)) # uncut diamond
+155
View File
@@ -0,0 +1,155 @@
require 'java'
java_import 'org.apollo.game.action.DistancedAction'
java_import 'org.apollo.game.model.entity.EquipmentConstants'
java_import 'org.apollo.game.model.entity.Skill'
PROSPECT_PULSES = 3
ORE_SIZE = 1
# TODO: finish implementing this
# A `DistancedAction` for mining ore.
class MiningAction < DistancedAction
attr_reader :position, :ore, :counter, :started
def initialize(mob, position, ore)
super(0, true, mob, position, ORE_SIZE)
@position = position
@ore = ore
@started = false
@counter = 0
end
def find_pickaxe
weapon = mob.equipment.get(EquipmentConstants::WEAPON)
PICKAXE_IDS.each do |id|
return PICKAXES[id] if (!weapon.nil? && weapon.id == id) || mob.inventory.contains(id)
end
nil
end
# starts the mining animation, sets counters/flags and turns the mob to
# the ore
def start_mine(pickaxe)
@started = true
mob.send_message('You swing your pick at the rock.')
mob.play_animation(pickaxe.animation)
@counter = pickaxe.pulses
end
def executeAction
skills = mob.skill_set
level = skills.get_skill(Skill::MINING).current_level
pickaxe = find_pickaxe
mob.turn_to(@position)
# verify the mob can mine with their pickaxe
if pickaxe.nil? || level < pickaxe.level
mob.send_message('You do not have a pickaxe for which you have the level to use.')
stop
return
end
# verify the mob can mine the ore
if ore.level > level
mob.send_message('You do not have the required level to mine this rock.')
stop
return
end
# check if we need to kick start things
if @started
# count down and check if we can have a chance at some ore now
if @counter == 0
# TODO: calculate the chance that the player can actually get the rock
if mob.inventory.add(ore.id)
name = name_of(@ore.id).sub(/ ore$/, '').downcase
mob.send_message("You manage to mine some #{name}.")
skills.add_experience(Skill::MINING, ore.exp)
# TODO: expire the rock
end
stop
end
@counter -= 1
else
start_mine(pickaxe)
end
end
def equals(other)
get_class == other.get_class && @position == other.position && @ore == other.ore
end
end
# A `DistancedAction` for a rock with no available ore.
class ExpiredProspectingAction < DistancedAction
attr_reader :position
def initialize(mob, position)
super(0, true, mob, position, ORE_SIZE)
end
def executeAction
mob.send_message('There is currently no ore available in this rock.')
stop
end
def equals(other)
get_class == other.get_class && @position == other.position
end
end
# A `DistancedAction` for prospecting a rock.
class ProspectingAction < DistancedAction
attr_reader :position, :ore
def initialize(mob, position, ore)
super(PROSPECT_PULSES, true, mob, position, ORE_SIZE)
@position = position
@ore = ore
@started = false
end
def executeAction
if @started
ore_def = ItemDefinition.lookup(@ore.id)
name = ore_def.name.sub(/ ore$/, '').downcase
mob.send_message("This rock contains #{name}.")
stop
else
@started = true
mob.send_message('You examine the rock for ores...')
mob.turn_to(@position)
end
end
def equals(other)
get_class == other.get_class && @position == other.position && @ore == other.ore
end
end
on :message, :first_object_action do |mob, message|
ore = ORES[message.id]
mob.start_action(MiningAction.new(mob, message.position, ore)) unless ore.nil?
end
on :message, :second_object_action do |mob, message|
ore = ORES[message.id]
if !ore.nil?
mob.start_action(ProspectingAction.new(mob, message.position, ore))
elsif !EXPIRED_ORES[message.id].nil?
mob.start_action(ExpiredProspectingAction.new(mob, message.position))
end
end
+95
View File
@@ -0,0 +1,95 @@
# Thanks to Mikey` <http://www.rune-server.org/members/mikey%60/> for helping
# to find some of the item/object IDs, minimum levels and experiences.
#
# Thanks to Clifton <http://www.rune-server.org/members/clifton/> for helping
# to find some of the expired object IDs.
ORES = {}
EXPIRED_ORES = {}
# An ore that can be mined.
class Ore
attr_reader :id, :objects, :level, :exp, :respawn
def initialize(id, objects, level, exp, respawn)
@id = id
@objects = objects
@level = level
@exp = exp
@respawn = respawn
end
end
def append_ore(ore)
ore.objects.each do |obj, expired_obj|
ORES[obj] = ore
EXPIRED_ORES[expired_obj] = true
end
end
CLAY_OBJECTS = {
2180 => 450, 2109 => 451, 14_904 => 14_896, 14_905 => 14_897
}
COPPER_OBJECTS = {
11_960 => 11_555, 11_961 => 11_556, 11_962 => 11_557, 11_936 => 11_552,
11_937 => 11_553, 11_938 => 11_554, 2090 => 450, 2091 => 451,
14_906 => 14_898, 14_907 => 14_899, 14_856 => 14_832, 14_857 => 14_833,
14_858 => 14_834
}
TIN_OBJECTS = {
11_597 => 11_555, 11_958 => 11_556, 11_959 => 11_557, 11_933 => 11_552,
11_934 => 11_553, 11_935 => 11_554, 2094 => 450, 2095 => 451,
14_092 => 14_894, 14_903 => 14_895
}
IRON_OBJECTS = {
11_954 => 11_555, 11_955 => 11_556, 11_956 => 11_557, 2092 => 450,
2093 => 451, 14_900 => 14_892, 14_901 => 14_893, 14_913 => 14_915,
14_914 => 14_916
}
COAL_OBJECTS = {
11_963 => 11_555, 11_964 => 11_556, 11_965 => 11_557, 11_930 => 11_552,
11_931 => 11_553, 11_932 => 11_554, 2096 => 450, 2097 => 451,
14_850 => 14_832, 14_851 => 14_833, 14_852 => 14_834
}
SILVER_OBJECTS = {
11_948 => 11_555, 11_949 => 11_556, 11_950 => 11_557, 2100 => 450, 2101 => 451
}
GOLD_OBJECTS = {
11_951 => 11_555, 11_952 => 11_556, 11_953 => 11_557, 2098 => 450, 2099 => 451
}
MITHRIL_OBJECTS = {
11_945 => 11_555, 11_946 => 11_556, 11_947 => 11_557, 11_942 => 11_552,
11_943 => 11_553, 11_944 => 11_554, 2102 => 450, 2103 => 451,
14_853 => 14_832, 14_854 => 14_833, 14_855 => 14_834
}
ADAMANT_OBJECTS = {
11_939 => 11_552, 11_940 => 11_553, 11_941 => 11_554, 2104 => 450,
2105 => 451, 14_862 => 14_832, 14_863 => 14_833, 14_864 => 14_834
}
RUNITE_OBJECTS = {
2106 => 450, 2107 => 451, 14_859 => 14_832, 14_860 => 14_833,
14_861 => 14_834
}
append_ore Ore.new 434, CLAY_OBJECTS, 1, 5, 3 # clay
append_ore Ore.new 436, COPPER_OBJECTS, 1, 17.5, 6 # copper ore
append_ore Ore.new 438, TIN_OBJECTS, 1, 17.5, 6 # tin ore
append_ore Ore.new 440, IRON_OBJECTS, 15, 35, 16 # iron ore
append_ore Ore.new 453, COAL_OBJECTS, 30, 50, 100 # coal
append_ore Ore.new 444, GOLD_OBJECTS, 40, 65, 200 # gold ore
append_ore Ore.new 442, SILVER_OBJECTS, 20, 40, 200 # silver ore
append_ore Ore.new 447, MITHRIL_OBJECTS, 55, 80, 400 # mithril ore
append_ore Ore.new 449, ADAMANT_OBJECTS, 70, 95, 800 # adamant ore
append_ore Ore.new 451, RUNITE_OBJECTS, 85, 125, 2500 # runite ore
# TODO: rune essence object id = 2491
# level 1, exp 5, rune ess = 1436, pure ess = 7936
+33
View File
@@ -0,0 +1,33 @@
require 'java'
java_import 'org.apollo.game.model.Animation'
PICKAXES = {}
PICKAXE_IDS = []
# A pickaxe that can be mined with.
class Pickaxe
attr_reader :id, :level, :animation, :pulses
def initialize(id, level, animation, pulses)
@id = id
@level = level
@animation = Animation.new(animation)
@pulses = pulses
end
end
def append_pickaxe(pickaxe)
PICKAXES[pickaxe.id] = pickaxe
PICKAXE_IDS << pickaxe.id # tacky way of keeping things in order
end
# NOTE: ADD LOWER LEVEL PICKAXES FIRST
append_pickaxe(Pickaxe.new(1265, 1, 625, 8)) # bronze pickaxe
append_pickaxe(Pickaxe.new(1267, 1, 626, 7)) # iron pickaxe
append_pickaxe(Pickaxe.new(1269, 1, 627, 6)) # steel pickaxe
append_pickaxe(Pickaxe.new(1273, 21, 629, 5)) # mithril pickaxe
append_pickaxe(Pickaxe.new(1271, 31, 628, 4)) # adamant pickaxe
append_pickaxe(Pickaxe.new(1275, 41, 624, 3)) # rune pickaxe
PICKAXE_IDS.reverse!
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0"?>
<plugin>
<id>skill-mining</id>
<version>1</version>
<name>Mining</name>
<description>Adds the mining skill.</description>
<authors>
<author>Graham</author>
<author>Mikey`</author>
<author>WH:II:DOW</author>
<author>Requa</author>
<author>Clifton</author>
</authors>
<scripts>
<script>pickaxe.rb</script>
<script>ore.rb</script>
<script>gem.rb</script>
<script>respawn.rb</script>
<script>mining.rb</script>
</scripts>
<dependencies>
<dependency>util</dependency>
</dependencies>
</plugin>
+16
View File
@@ -0,0 +1,16 @@
# Calculates the number of pulses it takes for an ore to respawn based on the
# number of players currently online.
#
# The 'base' argument is the number of pulses it takes with no players online.
# The 'players' argument is the number of players currently logged into the
# current world.
#
# The base times can be found on this website:
# http://runescape.salmoneus.net/mining.html#respawn
#
# These must be converted to pulses (seconds * 10 / 6) to work with this
# function. The rest of the mining plugin rounds the base respawn times in
# pulses down where appropriate.
def respawn_pulses(base, players)
base - players * base / ($world.player_repository.size * 2)
end
+89
View File
@@ -0,0 +1,89 @@
require 'java'
java_import 'org.apollo.game.action.Action'
java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.entity.Skill'
BURY_BONE_ANIMATION = Animation.new(827)
BONES = {}
# A bone with an id and experience value.
class Bone
attr_reader :id, :experience
def initialize(id, experience)
@id = id
@experience = experience
end
end
# An action where a bone in a player's inventory is buried.
class BuryBoneAction < Action
attr_reader :slot, :bone
def initialize(mob, slot, bone)
super(1, true, mob)
@slot = slot
@bone = bone
@executions = 0
end
def execute
if @executions == 0
mob.send_message('You dig a hole in the ground...')
mob.play_animation(BURY_BONE_ANIMATION)
@executions += 1
elsif @executions == 1
if mob.inventory.get(@slot).id == @bone.id
mob.send_message('You bury the bones.')
mob.inventory.reset(@slot)
mob.skill_set.add_experience(Skill::PRAYER, @bone.experience)
end
stop
end
end
def equals(other)
get_class == other.get_class
end
end
# Intercepts the first item option message.
on :message, :first_item_option do |player, message|
bone = BONES[message.id]
unless bone.nil?
player.start_action(BuryBoneAction.new(player, message.slot, bone))
message.terminate
end
end
# Appends a bone to the array
def append_bone(hash)
fail 'Hash must contain an id and an experience value.' unless hash.has_keys?(:id, :experience)
id = hash[:id]
BONES[id] = Bone.new(id, hash[:experience])
end
append_bone name: :regular_bones, id: 526, experience: 5
append_bone name: :burnt_bones, id: 528, experience: 5
append_bone name: :bat_bones, id: 530, experience: 4
append_bone name: :big_bones, id: 532, experience: 45
append_bone name: :babydragon_bones, id: 534, experience: 30
append_bone name: :dragon_bones, id: 536, experience: 72
append_bone name: :wolf_bones, id: 2859, experience: 14
append_bone name: :shaikahan_bones, id: 3123, experience: 25
append_bone name: :jogre_bones, id: 3125, experience: 15
append_bone name: :burnt_zogre_bones, id: 3127, experience: 25
append_bone name: :monkey_bones, id: 3179, experience: 14 # smallish
append_bone name: :monkey_bones, id: 3180, experience: 14 # medium
append_bone name: :monkey_bones, id: 3181, experience: 14 # quite large
append_bone name: :monkey_bones, id: 3182, experience: 14 # quite large
append_bone name: :monkey_bones, id: 3183, experience: 14 # small
append_bone name: :shaking_bones, id: 3187, experience: 14
append_bone name: :zogre_bones, id: 4812, experience: 23
append_bone name: :fayrg_bones, id: 4830, experience: 84
append_bone name: :raurg_bones, id: 4832, experience: 96
append_bone name: :ourg_bones, id: 4834, experience: 140
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<plugin>
<id>skill-prayer</id>
<version>0.9</version>
<name>Prayer</name>
<description>Adds the Prayer skill.</description>
<authors>
<author>Major</author>
<author>010253</author>
</authors>
<scripts>
<script>bury.rb</script>
<script>prayers.rb</script>
</scripts>
<dependencies>
<dependency>attributes</dependency>
</dependencies>
</plugin>
+88
View File
@@ -0,0 +1,88 @@
java_import 'org.apollo.game.message.impl.ConfigMessage'
# Declares the active prayer attribute.
declare_attribute(:active_prayer, -1, :persistent)
# The hash of button ids to prayers.
PRAYERS = {}
# Intercept the ButtonMessage to toggle a prayer.
on :message, :button do |player, message|
button = message.widget_id
prayer = PRAYERS[button]
unless prayer.nil?
if prayer.level > player.skill_set.get_maximum_level(Skill::PRAYER)
update_setting(player, prayer, :off)
next
end
player.send_message('after level check')
previous = player.active_prayer
update_setting(player, PRAYERS[previous], :off) unless previous == -1
if previous != button
player.send_message("Previous: #{previous}, new: #{button}.")
update_setting(player, prayer, :on)
player.active_prayer = button
end
end
end
private
# A Prayer that can be activated by a player.
class Prayer
attr_reader :name, :level, :button, :setting, :drain
def initialize(name, level, button, setting, drain)
@name = name
@level = level
@button = button
@setting = setting
@drain = drain
end
end
def update_setting(player, prayer, state)
value = (state == :on) ? 1 : 0
player.send_message("Toggling prayer #{prayer.name}, state: #{state}.")
player.send(ConfigMessage.new(prayer.setting, value))
end
# Appends a Prayer to the hash.
def append_prayer(name, hash)
unless hash.has_keys?(:level, :button, :setting, :drain)
fail 'Error: prayer hash hash must contain a level, button, setting, and drain.'
end
button = hash[:button]
PRAYERS[button] = Prayer.new(name, hash[:level], button, hash[:setting], hash[:drain])
end
# Don't deal with the actual effect here to avoid mess (TODO do it, but with attributes?).
append_prayer :thick_skin, level: 1, button: 5609, setting: 83, drain: 0.01
append_prayer :burst_of_strength, level: 4, button: 5610, setting: 84, drain: 0.01
append_prayer :clarity_of_thought, level: 7, button: 5611, setting: 85, drain: 0.01
append_prayer :rock_skin, level: 10, button: 5612, setting: 86, drain: 0.04
append_prayer :superhuman_strength, level: 13, button: 5613, setting: 87, drain: 0.04
append_prayer :improved_reflexes, level: 16, button: 5614, setting: 88, drain: 0.04
append_prayer :rapid_restore, level: 19, button: 5615, setting: 89, drain: 0.01
append_prayer :rapid_heal, level: 22, button: 5615, setting: 90, drain: 0.01
append_prayer :protect_item, level: 25, button: 5617, setting: 91, drain: 0.01
append_prayer :steel_skin, level: 28, button: 5618, setting: 92, drain: 0.1
append_prayer :ultimate_strength, level: 31, button: 5619, setting: 93, drain: 0.1
append_prayer :incredible_reflexes, level: 34, button: 5620, setting: 94, drain: 0.1
append_prayer :protect_from_magic, level: 37, button: 5621, setting: 95, drain: 0.15
append_prayer :protect_from_missiles, level: 40, button: 5622, setting: 96, drain: 0.15
append_prayer :protect_from_melee, level: 43, button: 5623, setting: 97, drain: 0.15
append_prayer :retribution, level: 46, button: 683, setting: 98, drain: 0.15
append_prayer :redemption, level: 49, button: 684, setting: 99, drain: 0.15
append_prayer :smite, level: 52, button: 685, setting: 100, drain: 0.2
+129
View File
@@ -0,0 +1,129 @@
require 'java'
java_import 'org.apollo.game.action.DistancedAction'
java_import 'org.apollo.game.model.Position'
PORTALS = {}
ENTRANCE_ALTARS = {}
CRAFTING_ALTARS = {}
# Represents a runecrafting altar.
class Altar
attr_reader :entrance_altar, :crafting, :portal_id, :entrance, :exit, :crafting_centre
def initialize(entrance_altar, crafting, portal_id, entrance_position, exit_position,
crafting_centre)
@entrance_altar = entrance_altar
@altar = crafting
@portal_id = portal_id
@entrance_position = entrance_position
@exit_position = exit_position
@crafting_centre = crafting_centre
end
end
# Intercepts the item on object message.
on :message, :item_on_object do |player, message|
talisman = TALISMANS[message.id]
altar = ENTRANCE_ALTARS[message.object_id]
unless talisman.nil? || altar.nil?
player.start_action(TeleportAction.new(player, message.position, 2, altar.entrance_position))
message.terminate
end
end
# Intercepts the first object action message.
on :message, :object_action do |player, message|
if message.option == 1
object_id = message.id
if PORTALS.key?(object_id)
altar = PORTALS[object_id]
entrance = altar.entrance_position
player.start_action(TeleportAction.new(player, entrance, 1, altar.exit_position))
message.terminate
elsif RUNES.key?(object_id)
rune = RUNES[object_id]
altar = CRAFTING_ALTARS[object_id]
player.start_action(RunecraftingAction.new(player, rune, altar.crafting_centre))
message.terminate
end
end
end
# An action that causes a mob to teleport when it comes within the specified distance of a
# specified position.
class TeleportAction < DistancedAction
attr_reader :teleport_position
def initialize(mob, position, distance, teleport_position)
super(0, true, mob, position, distance)
@teleport_position = teleport_position
end
def executeAction
mob.teleport(@teleport_position)
stop
end
def equals(other)
get_class == other.get_class && mob == other.mob &&
@teleport_position == other.teleport_position
end
end
# Appends an altar to the list.
def altar(name, hash)
unless hash.has_keys?(:entrance_altar, :crafting, :portal, :entrance, :exit, :altar_centre)
fail "#{name} is missing one of: entrance altar id, crafting altar id, entrance portal position, "\
"and altar centre position."
end
entrance_altar, crafting = hash[:entrance_altar], hash[:crafting]
portal_id = hash[:portal]
entrance = Position.new(*hash[:entrance])
exit_position = Position.new(*hash[:exit])
centre = Position.new(*hash[:altar_centre])
altar = Altar.new(entrance_altar, crafting, portal_id, entrance, exit_position, centre)
PORTALS[portal_id] = ENTRANCE_ALTARS[entrance_altar] = CRAFTING_ALTARS[crafting] = altar
end
altar :air, entrance_altar: 2452, crafting: 2478, portal: 2465,
entrance: [2841, 4829], exit: [2983, 3292], altar_centre: [2844, 4834]
altar :mind, entrance_altar: 2453, crafting: 2479, portal: 2466,
entrance: [2793, 4828], exit: [2980, 3514], altar_centre: [2786, 4841]
altar :water, entrance_altar: 2454, crafting: 2480, portal: 2467,
entrance: [2726, 4832], exit: [3187, 3166], altar_centre: [2716, 4836]
altar :earth, entrance_altar: 2455, crafting: 2481, portal: 2468,
entrance: [2655, 4830], exit: [3304, 3474], altar_centre: [2658, 4841]
altar :fire, entrance_altar: 2456, crafting: 2482, portal: 2469,
entrance: [2574, 4849], exit: [3311, 3256], altar_centre: [2585, 4838]
altar :body, entrance_altar: 2457, crafting: 2483, portal: 2470,
entrance: [2524, 4825], exit: [3051, 3445], altar_centre: [2525, 4832]
altar :cosmic, entrance_altar: 2458, crafting: 2484, portal: 2471,
entrance: [2142, 4813], exit: [2408, 4379], altar_centre: [2142, 4833]
altar :law, entrance_altar: 2459, crafting: 2485, portal: 2472,
entrance: [2464, 4818], exit: [2858, 3379], altar_centre: [2464, 4832]
altar :nature, entrance_altar: 2460, crafting: 2486, portal: 2473,
entrance: [2400, 4835], exit: [2867, 3019], altar_centre: [2400, 4841]
altar :chaos, entrance_altar: 2461, crafting: 2487, portal: 2474,
entrance: [2268, 4842], exit: [3058, 3591], altar_centre: [2271, 4842]
altar :death, entrance_altar: 2462, crafting: 2488, portal: 2475,
entrance: [2208, 4830], exit: [3222, 3222], altar_centre: [2205, 4836]
@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<plugin>
<id>skill-runecraft</id>
<version>0.9</version>
<name>Runecraft</name>
<description>Adds the Runecraft skill.</description>
<authors>
<author>Major</author>
<author>BugCrusher</author>
</authors>
<scripts>
<script>altar.rb</script>
<script>rune.rb</script>
<script>runecraft.rb</script>
<script>talisman.rb</script>
<script>tiara.rb</script>
</scripts>
<dependencies>
<dependency>util</dependency>
</dependencies>
</plugin>
+52
View File
@@ -0,0 +1,52 @@
require 'java'
# The hash of runes.
RUNES = {}
# Represents a rune that can be crafted.
class Rune
attr_reader :name, :id, :level, :experience
def initialize(id, level, experience, multiplier)
@id = id
@name = name_of(:item, id)
@level = level
@experience = experience
@multiplier = multiplier
end
def equals(other)
get_class == other.get_class && id == other.id
end
def multiplier(level)
@multiplier.call(level)
end
end
# Appends a rune to the list.
def rune(name, hash)
unless hash.has_keys?(:altar, :id, :level, :reward)
fail "#{name} is missing one of id, altar, level, or reward."
end
id, altar, level, experience = hash[:id], hash[:altar], hash[:level], hash[:reward]
bonus = hash[:bonus] || ->(_) { 1 }
RUNES[altar] = Rune.new(id, level, experience, bonus)
end
rune :air, altar: 2478, id: 556, level: 1, reward: 5, bonus: ->(level) { (level / 11).floor + 1 }
rune :mind, altar: 2479, id: 558, level: 1, reward: 5.5, bonus: ->(level) { (level / 14).floor + 1 }
rune :water, altar: 2480, id: 555, level: 5, reward: 6, bonus: ->(level) { (level / 19).floor + 1 }
rune :earth, altar: 2481, id: 557, level: 9, reward: 6.5,
bonus: ->(level) { (level / 26).floor + 1 }
rune :fire, altar: 2482, id: 554, level: 14, reward: 7, bonus: ->(level) { (level / 35).floor + 1 }
rune :body, altar: 2483, id: 559, level: 20, reward: 7.5,
bonus: ->(level) { (level / 46).floor + 1 }
rune :cosmic, altar: 2484, id: 564, level: 27, reward: 8, bonus: ->(level) { level >= 59 ? 2 : 1 }
rune :chaos, altar: 2487, id: 562, level: 35, reward: 8.5, bonus: ->(level) { level >= 74 ? 2 : 1 }
rune :nature, altar: 2486, id: 561, level: 44, reward: 9, bonus: ->(level) { level >= 91 ? 2 : 1 }
rune :law, altar: 2485, id: 563, level: 54, reward: 9.5
rune :death, altar: 2488, id: 560, level: 65, reward: 10
@@ -0,0 +1,58 @@
require 'java'
java_import 'org.apollo.game.action.DistancedAction'
java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.Graphic'
java_import 'org.apollo.game.model.entity.Skill'
RUNECRAFTING_ANIMATION = Animation.new(791)
RUNECRAFTING_GRAPHIC = Graphic.new(186, 0, 100)
RUNE_ESSENCE_ID = 1436
# An action when the player crafts a rune.
class RunecraftingAction < DistancedAction
attr_reader :player, :rune
def initialize(player, rune, object_position)
super(1, true, player, object_position, 3)
@player = player
@rune = rune
@position = object_position
@executions = 0
end
def executeAction
runecrafting_level = @player.skill_set.get_skill(Skill::RUNECRAFT).current_level
if runecrafting_level < @rune.level
@player.send_message("You need a runecrafting level of #{@rune.level} to craft this rune.")
stop
elsif !@player.inventory.contains(RUNE_ESSENCE_ID)
@player.send_message('You need rune essence to craft runes.')
stop
elsif @executions == 0
@player.turn_to(@position)
@player.play_animation(RUNECRAFTING_ANIMATION)
@player.play_graphic(RUNECRAFTING_GRAPHIC)
@executions += 1
elsif @executions == 1
inventory = @player.inventory
removed = inventory.remove(RUNE_ESSENCE_ID, inventory.get_amount(RUNE_ESSENCE_ID))
added = removed * @rune.multiplier(runecrafting_level)
inventory.add(@rune.id, added)
name = added > 1 ? 'some ' + @rune.name + 's' : 'an ' + @rune.name
@player.send_message("You craft the rune essence into #{name}.")
@player.skill_set.add_experience(Skill::RUNECRAFT, removed * @rune.experience)
stop
end
end
def equals(other)
get_class == other.get_class && @player == other.player && @rune == other.rune
end
end
@@ -0,0 +1,54 @@
require 'java'
java_import 'org.apollo.game.model.Position'
# The list of talismans.
TALISMANS = {}
# A talisman that will indicate a direction when activated.
class Talisman
def initialize(entrance_altar_position)
@locate_position = entrance_altar_position
end
def get_message(position)
return 'Your talisman glows brightly.' if position.is_within_distance(@locate_position, 10)
direction = (position.y > @locate_position.y ? 'North' : 'South') + '-'
direction += (position.x > @locate_position.x ? 'East' : 'West')
"The talisman pulls toward the #{direction}."
end
end
# Intercepts the item option message.
on :message, :fourth_item_option do |player, message|
talisman = TALISMANS[message.id]
unless talisman.nil?
player.send_message(talisman.get_message(player.position))
message.terminate
end
end
# Appends a talisman to the list.
def talisman(name, hash)
fail 'Hash must contain an id and an altar position.' unless hash.has_keys?(:id, :altar)
id, altar_position = hash[:id], Position.new(*hash[:altar])
TALISMANS[id] = Talisman.new(altar_position)
end
talisman :air_talisman, id: 1438, altar: [2985, 3292]
talisman :earth_talisman, id: 1440, altar: [3306, 3474]
talisman :fire_talisman, id: 1442, altar: [3313, 3255]
talisman :water_talisman, id: 1444, altar: [3185, 3165]
talisman :body_talisman, id: 1446, altar: [3053, 3445]
talisman :mind_talisman, id: 1448, altar: [2982, 3514]
talisman :chaos_talisman, id: 1452, altar: [3059, 3590]
talisman :cosmic_talisman, id: 1454, altar: [2408, 4377]
talisman :death_talisman, id: 1456, altar: [0, 0]
talisman :law_talisman, id: 1458, altar: [2858, 3381]
talisman :nature_talisman, id: 1462, altar: [2869, 3019]
+167
View File
@@ -0,0 +1,167 @@
require 'java'
java_import 'org.apollo.game.message.impl.ConfigMessage'
java_import 'org.apollo.game.model.entity.EquipmentConstants'
java_import 'org.apollo.game.action.DistancedAction'
# The hash of tiaras.
TIARAS_BY_ALTAR = {}
TIARAS_BY_ID = {}
TIARAS_BY_TALISMAN = {}
# A tiara will make an altar accessible with a single click.
class Tiara
attr_reader :altar, :bitshift, :tiara_id, :experience, :talisman
def initialize(tiara_id, altar, talisman, bitshift, experience)
@tiara_id = tiara_id
@name = name_of(:item, tiara_id)
@altar = altar
@talisman = talisman
@bitshift = bitshift
@experience = experience
end
# Sends a config message to change the altar object.
def send_config(player)
player.send(ConfigMessage.new(CHANGE_ALTAR_OBJECT_CONFIG, 1 << @bitshift))
end
end
private
# The id of the altar change config.
CHANGE_ALTAR_OBJECT_CONFIG = 491
# The id of the blank tiara.
TIARA_ITEM_ID = 5525
# Sends an empty altar config.
def send_empty_config(player)
player.send(ConfigMessage.new(CHANGE_ALTAR_OBJECT_CONFIG, 0))
end
# Sets the correct config upon login, if the player is wearing a tiara.
on :login do |_event, player|
hat = player.equipment.get(EquipmentConstants::HAT)
unless hat.nil?
tiara = TIARAS_BY_ID[hat]
tiara.nil? ? send_empty_config(player) : tiara.send_config
end
end
# Intercepts the SecondObjectAction message to support left-click access to the altar when wielding
# the correct tiara.
on :message, :second_object_action do |player, message|
object_id = message.id
tiara = TIARAS_BY_ALTAR[object_id]
next if tiara.nil?
hat = player.equipment.get(EquipmentConstants::HAT)
if !hat.nil? && hat.id == tiara.tiara_id
altar = ENTRANCE_ALTARS[tiara.altar]
message.terminate
unless altar.nil?
player.start_action(TeleportAction.new(player, message.position, 2, altar.entrance_position))
end
end
end
# Intercepts the SecondItemAction message to allow for config sending.
on :message, :second_item_option do |player, message|
tiara = TIARAS_BY_ID[message.id]
unless tiara.nil?
tiara.send_config(player)
message.terminate
end
end
# Intercepts the FirstItemAction message to allow for config sending.
on :message, :first_item_action do |player, message|
tiara = TIARAS_BY_ID[message.id]
unless tiara.nil?
send_empty_config(player)
message.terminate
end
end
# Intercepts the ItemOnObject message to create the tiara.
on :message, :item_on_object do |player, message|
tiara, altar = TIARAS_BY_TALISMAN[message.id], CRAFTING_ALTARS[message.object_id]
return if tiara.nil? || altar.nil?
player.start_action(CreateTiaraAction.new(player, message.position, tiara, altar))
message.terminate
end
# An action lets the player create a tiara when it comes within the specified distance of a
# specified position.
# noinspection JRubyImplementInterfaceInspection
class CreateTiaraAction < DistancedAction
# Creates the CreateTiaraAction.
def initialize(player, position, tiara, altar)
super(0, true, player, position, 2)
@player = player
@tiara = tiara
@altar = altar
end
def execute_action
inventory = @player.inventory
if inventory.contains_all(TIARA_ITEM_ID, @tiara.talisman)
if @tiara.altar == @altar.entrance_altar
inventory.remove(@tiara.talisman, TIARA_ITEM_ID)
inventory.add(@tiara.tiara_id)
@player.skill_set.add_experience(RUNECRAFT_SKILL_ID, @tiara.experience)
@player.play_animation(RUNECRAFTING_ANIMATION)
@player.play_graphic(RUNECRAFTING_GRAPHIC)
else
@player.send_message('You can\'t use that talisman on this altar.')
end
else
@player.send_message('You need to have a talisman and blank tiara to enchant a tiara.')
end
stop
end
def equals(other)
get_class == other.get_class && @player == other.player && @tiara == other.tiara
end
end
# Appends a tiara to the list.
def tiara(_name, hash)
unless hash.has_keys?(:altar, :bitshift, :experience, :talisman, :tiara_id)
fail 'Hash must contain a tiara id, altar id, talisman id, a bitshift number, and experience.'
end
tiara_id, altar, talisman = hash[:tiara_id], hash[:altar], hash[:talisman]
bitshift, experience = hash[:bitshift], hash[:experience]
tiara = Tiara.new(tiara_id, altar, talisman, bitshift, experience)
TIARAS_BY_TALISMAN[talisman] = TIARAS_BY_ID[tiara_id] = TIARAS_BY_ALTAR[altar] = tiara
end
tiara :air_tiara, tiara_id: 5527, altar: 2452, talisman: 1438, bitshift: 0, experience: 25
tiara :mind_tiara, tiara_id: 5529, altar: 2453, talisman: 1448, bitshift: 1, experience: 27.5
tiara :water_tiara, tiara_id: 5531, altar: 2454, talisman: 1444, bitshift: 2, experience: 30
tiara :body_tiara, tiara_id: 5533, altar: 2457, talisman: 1446, bitshift: 5, experience: 37.5
tiara :earth_tiara, tiara_id: 5535, altar: 2455, talisman: 1440, bitshift: 3, experience: 32.5
tiara :fire_tiara, tiara_id: 5537, altar: 2456, talisman: 1442, bitshift: 4, experience: 35
tiara :cosmic_tiara, tiara_id: 5539, altar: 2458, talisman: 1454, bitshift: 6, experience: 40
tiara :nature_tiara, tiara_id: 5541, altar: 2460, talisman: 1462, bitshift: 8, experience: 45
tiara :chaos_tiara, tiara_id: 5543, altar: 2461, talisman: 1452, bitshift: 9, experience: 42.5
tiara :law_tiara, tiara_id: 5545, altar: 2459, talisman: 1458, bitshift: 7, experience: 47.5
tiara :death_tiara, tiara_id: 5548, altar: 2462, talisman: 1456, bitshift: 10, experience: 50
# TODO: there are 2 other altars, which probably just aren't spawned on the map