diff --git a/data/plugins/combat/ammo.rb b/data/plugins/combat/ammo.rb
new file mode 100644
index 00000000..f151555c
--- /dev/null
+++ b/data/plugins/combat/ammo.rb
@@ -0,0 +1,34 @@
+AMMO = {}
+
+def create_ammo(item_matcher, properties, &block)
+ items = find_entities :item, item_matcher, -1
+ fail "Unable to find ammo matching #{item_matcher}" unless items.size > 0
+
+ projectile_type = properties[:projectile_type]
+ fail "Unable to find projectile type #{projectile_type}" unless PROJECTILE_TYPES.key? projectile_type
+
+ properties[:projectile_type] = PROJECTILE_TYPES[projectile_type]
+
+ items.each do |item_id|
+ AMMO[item_id] = Ammo.new(item_id, properties)
+ AMMO[item_id].instance_eval &block if block_given?
+ end
+end
+
+private
+
+class Ammo
+ attr_reader :item, :weapon_classes, :projectile, :projectile_type, :level_requirement, :drop_rate, :graphic
+
+ include Combat::BonusContainer
+
+ def initialize(item, weapon_classes:, projectile:, projectile_type:, level_requirement:, drop_rate:, graphic: nil)
+ @item = item
+ @weapon_classes = weapon_classes
+ @projectile = projectile
+ @projectile_type = projectile_type
+ @level_requirement = level_requirement
+ @drop_rate = drop_rate
+ @graphic = graphic
+ end
+end
diff --git a/data/plugins/combat/ammo/arrows.rb b/data/plugins/combat/ammo/arrows.rb
new file mode 100644
index 00000000..ef6d808b
--- /dev/null
+++ b/data/plugins/combat/ammo/arrows.rb
@@ -0,0 +1,15 @@
+def create_arrow(item, hash)
+ hash[:projectile_type] = :arrow
+ hash[:weapon_classes] = [:longbow, :shortbow]
+
+ create_ammo item, hash
+end
+
+create_projectile_type :arrow, start_height: 41, end_height: 37, delay: 41, speed: 6, slope: 15, radius: 10
+
+create_arrow :bronze_arrow, level_requirement: 1, projectile: 10, graphic: 19, drop_rate: 0.3
+create_arrow :iron_arrow, level_requirement: 1, projectile: 9, graphic: 18, drop_rate: 0.35
+create_arrow :steel_arrow, level_requirement: 5, projectile: 11, graphic: 20, drop_rate: 0.4
+create_arrow :mithril_arrow, level_requirement: 20, projectile: 12, graphic: 21, drop_rate: 0.45
+create_arrow :adamant_arrow, level_requirement: 30, projectile: 13, graphic: 22, drop_rate: 0.5
+create_arrow :rune_arrow, level_requirement: 40, projectile: 15, graphic: 24, drop_rate: 0.6
diff --git a/data/plugins/combat/ammo/bolts.rb b/data/plugins/combat/ammo/bolts.rb
new file mode 100644
index 00000000..c08b64a5
--- /dev/null
+++ b/data/plugins/combat/ammo/bolts.rb
@@ -0,0 +1,10 @@
+def create_bolt(item, hash)
+ hash[:projectile_type] = :bolt
+ hash[:weapon_classes] = [:crossbow]
+ hash[:projectile] = 27 unless hash.key? :projectile
+ create_ammo item, hash
+end
+
+create_projectile_type :bolt, start_height: 44, end_height: 44, delay: 41, speed: 5, slope: 5, radius: 10
+
+create_bolt :iron_bolts, level_requirement: 1, drop_rate: 0.35
diff --git a/data/plugins/combat/attack.rb b/data/plugins/combat/attack.rb
index dac67141..530dece8 100644
--- a/data/plugins/combat/attack.rb
+++ b/data/plugins/combat/attack.rb
@@ -3,9 +3,13 @@ java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.Graphic'
class BaseAttack
- attr_reader :requirements, :range
+ attr_reader :requirements, :range, :speed
- def initialize(animation, graphic = nil, range = 1, requirements = [])
+ def initialize(speed:, animation:, graphic: nil, range: 1, requirements: [])
+ fail 'Attack speed must be a non-negative number' if speed < 0
+ fail 'Attack range must be a non-negative number' if range < 0
+
+ @speed = speed
@animation = animation
@graphic = graphic
@range = range
@@ -17,7 +21,7 @@ class BaseAttack
unless @graphic.nil?
if @graphic.is_a?(Hash)
- source.play_graphic(Graphic.new(@graphic[:id], @graphic[:delay] || 0, @graphic[:height] | 0))
+ source.play_graphic(Graphic.new(@graphic[:id], @graphic[:delay] || 0, @graphic[:height] || 0))
else
source.play_graphic(Graphic.new(@graphic))
end
@@ -26,63 +30,167 @@ class BaseAttack
apply(source, target)
end
- def apply(source, target)
- raise "BaseAttack#apply unimplemented"
- end
-
- def damage!(source, target, amount, delay = 0)
- schedule delay do |task|
- task.stop && return if source.dead or target.dead
-
- target_combat_state = get_combat_state target
- target_hitpoints = target.skill_set.get_skill(Skill::HITPOINTS).get_current_level
- amount = target_hitpoints if target_hitpoints < amount
-
- type = amount > 0 ? 1 : 0
- target.play_animation(Animation.new(424)) unless target_combat_state.state == :attacking
- target.damage(amount, type, false)
-
- task.stop
- end
+ def apply(_source, _target)
+ fail 'BaseAttack#apply unimplemented'
end
end
class ProcAttack < BaseAttack
- def initialize(block, animation:, graphic:, range: 1, requirements: [])
- super(animation, graphic, range, requirements)
+ def initialize(block, hash)
+ super(hash)
@block = block
end
def apply(source, target)
- self.instance_exec(source, target, &@block)
+ instance_exec(source, target, &@block)
end
end
+##
+# A simple ranged attack, which sends a projectile based on the currently equipped ammo and weapon
+# and deals damaged delayed by the speed and travel distance of the projectile.
+
class RangedAttack < BaseAttack
- def initialize(animation:, graphic:, requirements: [])
-
- end
-
- def calculate_delay(source, target)
-
- end
-
def apply(source, target)
-
- end
-
- def projectile!(source, target, projectile_id)
-
+ do_ranged_damage! source, target
end
end
+##
+# A basic melee attack, which deals damage a tick after the attack was made.
+
class Attack < BaseAttack
- def initialize(animation:, graphic: nil, range: 1, requirements: [])
- super(animation, graphic, range, requirements)
- end
-
def apply(source, target)
- damage! source, target, CombatUtil::calculate_hit(source, target)
+ do_damage! source, target, CombatUtil.calculate_hit(source, target)
end
end
+
+##
+# A DSL-like builder object for creating a new Attack, used mainly (exclusively?) for special attacks.
+
+class AttackDSL
+ attr_accessor :animation, :speed, :range, :graphic
+
+ def initialize()
+ @requirements = []
+ @subattacks = []
+ end
+
+ def add_requirement(requirement)
+ @requirements.push requirement
+ end
+
+ ##
+ # Deal ranged damage.
+
+ def range_damage!(hash)
+ @subattacks.push lambda { |source, target|
+ do_ranged_damage! source, target, hash
+ }
+ end
+
+ ##
+ # Deal melee damage.
+
+ def damage!(damage_modifier: 1, delay: 0)
+ @subattacks.push lambda { |source, target|
+ do_damage! source, target, 1, delay
+ }
+ end
+
+ ##
+ # Process the attack instructions in this DSL then build and return an {@link Attack} object.
+
+ def to_attack
+ subattacks = @subattacks
+
+ attack = lambda do |source, target|
+ subattacks.each do |subattack|
+ subattack[source, target]
+ end
+ end
+
+ # TODO: clean up? Decide where keyword arguments are appropriate and where not
+ ProcAttack.new(attack, speed: speed, animation: animation, graphic: graphic, range: range, requirements: @requirements)
+ end
+end
+
+private
+
+##
+# TODO: refactor
+def do_damage!(source, target, amount, delay = 0, secondary = false, &_block)
+ schedule delay do |task|
+ task.stop && return if source.dead || target.dead
+
+ target_combat_state = get_combat_state target
+ target_hitpoints = target.skill_set.get_skill(Skill::HITPOINTS).get_current_level
+ amount = target_hitpoints if target_hitpoints < amount
+
+ type = amount > 0 ? 1 : 0
+ target.play_animation(Animation.new(424)) unless target_combat_state.will_attack?
+ target.damage(amount, type, secondary)
+
+ if target.auto_retaliate && !target_combat_state.is_attacking?
+ target_combat_state.target = source
+ target.start_action CombatAction.new(target)
+ end
+
+ task.stop
+ end
+end
+
+##
+# TODO: refactor
+
+def do_ranged_damage!(source, target, _damage_modifier = 1, speed_modifier = 1, projectile = nil, projectile_type = nil, projectile_graphic = nil,
+ delay = 0, secondary = false)
+ apply = lambda do
+ distance = source.position.get_distance(target.position)
+
+ ammo = EquipmentUtil.equipped_ammo source
+
+ if projectile.nil? && projectile_type.nil?
+ projectile = ammo.projectile
+ projectile_type = ammo.projectile_type
+ projectile_graphic = ammo.graphic
+ end
+
+ projectile! source, target, projectile, projectile_type, speed_modifier
+ do_damage! source, target, rand(1...50), (projectile_type.delay + projectile_type.speed + distance * 5) * 0.02857, secondary
+
+ unless projectile_graphic.nil?
+ source.play_graphic Graphic.new(projectile_graphic, 0, 100)
+ end
+ end
+
+ if delay == 0
+ apply.call
+ else
+ # account for the one tick delay in the scheduler
+ schedule(delay - 1) do |task|
+ apply.call
+ task.stop
+ end
+ end
+end
+
+##
+# TODO: refactor
+
+def projectile!(source, target, projectile_id, projectile_type, speed_modifier = 1)
+ distance = source.position.get_distance(target.position)
+
+ projectile_parameters = {
+ angle: -1,
+ start_height: projectile_type.start_height,
+ end_height: projectile_type.end_height,
+ delay: projectile_type.delay * (2 - speed_modifier),
+ speed: (projectile_type.delay + projectile_type.speed + distance * 5) * speed_modifier,
+ slope: projectile_type.slope,
+ radius: projectile_type.radius
+ }
+
+ ProjectileModule.fire_projectile(source.position, target.position, projectile_id, projectile_parameters, target)
+end
diff --git a/data/plugins/combat/weapons/swords.rb b/data/plugins/combat/attack_effect.rb
similarity index 100%
rename from data/plugins/combat/weapons/swords.rb
rename to data/plugins/combat/attack_effect.rb
diff --git a/data/plugins/combat/attack_requirements.rb b/data/plugins/combat/attack_requirements.rb
index 3091fb43..3c0fd449 100644
--- a/data/plugins/combat/attack_requirements.rb
+++ b/data/plugins/combat/attack_requirements.rb
@@ -1,4 +1,6 @@
java_import 'org.apollo.cache.def.ItemDefinition'
+java_import 'org.apollo.game.model.entity.EquipmentConstants'
+java_import 'org.apollo.cache.def.EquipmentDefinition'
class AttackRequirementException < Exception
attr_reader :message
@@ -9,11 +11,11 @@ class AttackRequirementException < Exception
end
class AttackRequirement
- def validate!(player)
+ def validate(_player)
throw RuntimeError.new('validate! not implemented')
end
- def apply(player)
+ def apply!(_player)
throw RuntimeError.new('apply not implemented')
end
end
@@ -23,19 +25,19 @@ class SpecialEnergyRequirement < AttackRequirement
@amount = amount
end
- def validate!(player)
- if player.special_energy < @amount
+ def validate(player)
+ if false
player.using_special = false
-
+
update_special_bar(player)
- raise AttackRequirementException.new('Not enough special attack energy.')
+ fail AttackRequirementException.new('Not enough special attack energy.')
end
end
- def apply(player)
+ def apply!(player)
player.special_energy = player.special_energy - @amount
- player.using_special = false
-
+ player.using_special = false
+
update_special_bar player
end
end
@@ -46,11 +48,11 @@ class ItemRequirement < AttackRequirement
@amount = amount
end
- def validate!(player)
+ def validate(player)
throw AttackRequirementException.new(item_missing_message) unless player.inventory.get_amount(@item) >= @amount
end
- def apply(player)
+ def apply!(player)
player.inventory.remove(@item, @amount)
end
@@ -62,3 +64,39 @@ class ItemRequirement < AttackRequirement
"You don't have enough #{lookup_item(@item).name}s"
end
end
+
+class AmmoRequirement < AttackRequirement
+ def initialize(amount = 1)
+ @amount = amount
+ end
+
+ def validate(player)
+ equipped_weapon_item = player.equipment.get(EquipmentConstants::WEAPON)
+ equipped_weapon_def = EquipmentDefinition.lookup(equipped_weapon_item.id)
+ equipped_weapon = EquipmentUtil.equipped_weapon player
+ equipped_ammo = EquipmentUtil.equipped_ammo player
+ equipped_ammo_amt = player.equipment.get(EquipmentConstants::AMMO).amount
+
+ if equipped_ammo.nil?
+ fail AttackRequirementException.new('You have no ammo left in your quiver!')
+ end
+
+ if @amount > 1 && equipped_ammo_amt < @amount
+ fail AttackRequirementException.new('You don\'t have enough ammo left in your quiver!')
+ end
+
+ unless equipped_ammo.weapon_classes.include? equipped_weapon.weapon_class.name
+ fail AttackRequirementException.new('You can\'t use this ammo with this weapon.')
+ end
+
+ if equipped_ammo.level_requirement > equipped_weapon_def.ranged_level
+ fail AttackRequirementException.new('You can\'t use this ammo with this weapon.')
+ end
+ end
+
+ def apply!(player)
+ equipped_ammo = EquipmentUtil.equipped_ammo player
+
+ player.equipment.remove equipped_ammo.item
+ end
+end
diff --git a/data/plugins/combat/attributes.rb b/data/plugins/combat/attributes.rb
index 5fe3974b..ddcabf27 100644
--- a/data/plugins/combat/attributes.rb
+++ b/data/plugins/combat/attributes.rb
@@ -1,6 +1,6 @@
##
-# The delay a Mob must wait before attacking again.
-declare_attribute(:attack_delay, 0)
+# The number of ticks since a Mobs last attack.
+declare_attribute(:attack_timer, 100)
##
# A flag indicating whether this Mob is currently in combat.
@@ -22,6 +22,10 @@ declare_attribute(:combat_style, 0, :persistent)
# A flag indicating whether the special bar is flagged for the next attack.
declare_attribute(:using_special, false, :persistent)
+##
+# A flag indicating whether auto retaliation is enabled.
+declare_attribute(:auto_retaliate, true, :persistent)
+
##
# An integer between 0 and 100 indicating the amount of special energy a Player has.
declare_attribute(:special_energy, 100, :persistent)
diff --git a/data/plugins/combat/bonuses.rb b/data/plugins/combat/bonuses.rb
index e13ee709..2d6a8f00 100644
--- a/data/plugins/combat/bonuses.rb
+++ b/data/plugins/combat/bonuses.rb
@@ -15,30 +15,30 @@ module Combat
def other_bonuses(melee_strength: 0, ranged_strength: 0, prayer: 0)
@other_bonuses = {
- :melee_strength => melee_strength,
- :ranged_strength => ranged_strength,
- :prayer => prayer
+ melee_strength: melee_strength,
+ ranged_strength: ranged_strength,
+ prayer: prayer
}
end
def defence_bonuses(stab: 0, slash: 0, crush: 0, magic: 0, range: 0)
@defence_bonuses = {
- :stab => stab,
- :slash => slash,
- :crush => crush,
- :magic => magic,
- :range => range
+ stab: stab,
+ slash: slash,
+ crush: crush,
+ magic: magic,
+ range: range
}
end
def attack_bonuses(stab: 0, slash: 0, crush: 0, magic: 0, range: 0)
@attack_bonuses = {
- :stab => stab,
- :slash => slash,
- :crush => crush,
- :magic => magic,
- :range => range
+ stab: stab,
+ slash: slash,
+ crush: crush,
+ magic: magic,
+ range: range
}
end
end
-end
\ No newline at end of file
+end
diff --git a/data/plugins/combat/combat.rb b/data/plugins/combat/combat.rb
index 6cf9ead0..ff2c946b 100644
--- a/data/plugins/combat/combat.rb
+++ b/data/plugins/combat/combat.rb
@@ -1,12 +1,28 @@
-on :message, :npc_action do |player, message|
- player_combat_state = get_combat_state player
- player_combat_state.target = $world.npc_repository.get message.index
+java_import 'org.apollo.game.message.impl.HintIconMessage'
- unless player.attacking
- player.start_action CombatAction.new(player)
- end
+on :message, :npc_action do |player, message|
+ target = $world.npc_repository.get message.index
+
+ # unless target.attacking
+ # target_combat_state = get_combat_state target
+ # target_combat_state.target = player
+ #
+ # target.start_action CombatAction.new(target)
+ # end
+
+ player_combat_state = get_combat_state player
+ player_combat_state.target = target
+
+ player.send HintIconMessage.for_npc(target.index)
+ player.walking_queue.clear
+ player.start_action CombatAction.new(player) unless player_combat_state.is_attacking?
end
on :message, :player_action do |player, message|
-
-end
\ No newline at end of file
+
+end
+
+schedule 0 do |_task|
+ $world.player_repository.each { |player| player.attack_timer = player.attack_timer + 1 }
+ $world.npc_repository.each { |npc| npc.attack_timer = npc.attack_timer + 1 }
+end
diff --git a/data/plugins/combat/combat_action.rb b/data/plugins/combat/combat_action.rb
index b97149a6..f347ee4a 100644
--- a/data/plugins/combat/combat_action.rb
+++ b/data/plugins/combat/combat_action.rb
@@ -1,122 +1,66 @@
java_import 'org.apollo.game.action.Action'
-
-# Represents a one off {@code Attack}, which will not continue a combat session
-# upon completion.
-class AttackAction < Action
-
- def initialize(source, target, delay, attack)
- @source = source
- @target = target
- @delay = delay
- @attack = attack
- end
-
- def execute
- begin
- @attack.do(mob, @target)
-
- stop
- rescue AttackRequirementException => e
- player.send_message e.message
- stop
- end
- end
-end
+java_import 'org.apollo.game.model.entity.EntityType'
class CombatAction < Action
- def initialize(source, attack = nil)
+ def initialize(source, once = false)
super(0, true, source)
+ mob.attacking = true
+
@combat_state = get_combat_state(source)
- @attack = attack
+ @attack_timer = 100
+ @once = once
+ end
+
+ def can_attack?
+ next_attack = @combat_state.next_attack true
+ target = @combat_state.target
+ current_distance = mob.position.get_distance target.position
+ attack_collision_type = next_attack.range > 1 ? EntityType::PROJECTILE : EntityType::NPC
+
+ in_range = current_distance <= next_attack.range && !$world.intersects(mob.position, target.position, attack_collision_type)
+
+ unless in_range
+ mob.follow @combat_state.target, next_attack.range
+
+ return false
+ end
+
+ mob.attack_timer >= next_attack.speed
+ end
+
+ def try_attack
+ next_attack = @combat_state.next_attack false
+
+ if mob.is_a? Player
+ next_attack.requirements.each { |requirement| requirement.validate mob }
+ next_attack.requirements.each { |requirement| requirement.apply! mob }
+ end
+
+ next_attack.do(mob, @combat_state.target)
+ rescue AttackRequirementException => e
+ mob.send_message e.message
+ ensure
+ mob.attack_timer = 0
end
def execute
- if @combat_state.target.nil? and @combat_state.queued_attacks.empty?
- @combat_state.reset
+ if @combat_state.target.nil? || !@combat_state.target.is_active || @combat_state.next_attack(true).nil?
stop
- end
-
- case @combat_state.state
- when :idle
- update_idle
- when :attacking
- update_attacking
- when :chasing
- update_chasing
- else
- throw ArgumentError.new('invalid combat state')
- end
- end
-
- def update_idle
- next_attack = @combat_state.next_attack true
- current_distance = mob.position.get_distance @combat_state.target.position
-
- if current_distance > next_attack.range
- @combat_state.state = :chasing
-
- mob.follow @combat_state.target, next_attack.range
- set_delay 0
-
return
end
- if @combat_state.supports_weapon
- weapon = EquipmentUtil.equipped_weapon mob
- weapon_class = weapon.weapon_class
- combat_style = weapon_class.style_at mob.combat_style
+ return unless can_attack?
- if mob.attacking
- set_delay weapon_class.speed(combat_style) - 1
- else
- set_delay 0
- mob.attacking = true
- end
-
- @combat_state.state = :attacking
- else
- stop
- @combat_state.reset
- end
- end
-
- def update_attacking
- begin
- next_attack = @combat_state.next_attack
- next_attack.requirements.each do |requirement|
- requirement.validate! mob
- end
-
- next_attack.requirements.each do |requirement|
- requirement.apply mob
- end
-
- next_attack.do(mob, @combat_state.target)
- rescue AttackRequirementException => e
- mob.send_message e.message
- ensure
- @combat_state.state = :idle
- set_delay 0
- end
- end
-
- def update_chasing
- next_attack = @combat_state.next_attack true
- current_distance = mob.position.get_distance @combat_state.target.position
-
- if current_distance <= next_attack.range
- @combat_state.state = :attacking
-
- mob.following = -1
- set_delay 0
- end
+ try_attack
+ stop if @once
end
def stop
super
- @combat_state.reset
mob.attacking = false
+ mob.following = -1
+ @combat_state.reset
end
-end
\ No newline at end of file
+end
diff --git a/data/plugins/combat/combat_state.rb b/data/plugins/combat/combat_state.rb
index 9c0088a6..3e20608a 100644
--- a/data/plugins/combat/combat_state.rb
+++ b/data/plugins/combat/combat_state.rb
@@ -1,7 +1,7 @@
def get_combat_state(mob)
mob.is_a?(Player) ? type = :player : type = :npc
- unless MOB_COMBAT_STATE_CACHE[type].has_key? mob.index
+ unless MOB_COMBAT_STATE_CACHE[type].key? mob.index
MOB_COMBAT_STATE_CACHE[type][mob.index] = CombatState.new(mob)
end
@@ -11,8 +11,7 @@ end
private
class CombatState
- attr_accessor :state
- attr_reader :queued_attacks, :supports_weapon
+ attr_reader :target
def initialize(mob, supports_weapon = true)
@mob = mob
@@ -21,20 +20,22 @@ class CombatState
end
def reset
- @state = :idle
@target = nil
@next_attack = nil
@queued_attacks = []
- @mob.reset_interacting_mob
end
- def target
- @target
+ def will_attack?
+ is_attacking? && next_attack(true).speed >= @mob.attack_timer
+ end
+
+ def is_attacking?
+ !target.nil? && @mob.attacking
end
def target=(target)
@mob.reset_interacting_mob
- @mob.interacting_mob = target
+ @mob.interacting_mob = target unless target.nil?
@target = target
end
@@ -44,23 +45,25 @@ class CombatState
def next_attack(peek = false)
if @queued_attacks.size > 0
- peek ? @queued_attacks.first : @queued_attacks.pop
+ peek ? @queued_attacks.last : @queued_attacks.pop
else
weapon = EquipmentUtil.equipped_weapon @mob
- if @mob.using_special and weapon.special_attack?
+ if @mob.using_special && weapon.special_attack?
weapon.special_attack
else
- weapon_class = weapon.weapon_class
- combat_style = weapon_class.style_at @mob.combat_style
-
- weapon_class.attack combat_style
+ combat_style = weapon.weapon_class.selected_style @mob
+ combat_style.attack
end
end
end
end
MOB_COMBAT_STATE_CACHE = {
- :player => {},
- :npc => {}
+ player: {},
+ npc: {}
}
+
+on :logout do |event|
+ MOB_COMBAT_STATE_CACHE[:player].delete event.player.index
+end
diff --git a/data/plugins/combat/combat_style.rb b/data/plugins/combat/combat_style.rb
new file mode 100644
index 00000000..c50d6a27
--- /dev/null
+++ b/data/plugins/combat/combat_style.rb
@@ -0,0 +1,11 @@
+class CombatStyle
+ attr_reader :button, :config, :attack_type, :block_animation
+ attr_accessor :attack
+
+ def initialize(button, config, attack_type, block_animation)
+ @button = button
+ @config = config
+ @attack_type = attack_type
+ @block_animation = block_animation
+ end
+end
diff --git a/data/plugins/combat/equipment.rb b/data/plugins/combat/equipment.rb
index cfec43f3..2e228ff3 100644
--- a/data/plugins/combat/equipment.rb
+++ b/data/plugins/combat/equipment.rb
@@ -6,7 +6,7 @@ def create_equipment(item, &block)
equipment = Equipment.new
equipment.instance_eval block
- find_entities :item, item do |equipment_item|
+ find_entities :item, item do |_equipment_item|
EQUIPMENT[id] = equipment
end
end
@@ -15,4 +15,4 @@ private
class Equipment
include Combat::BonusContainer
-end
\ No newline at end of file
+end
diff --git a/data/plugins/combat/plugin.xml b/data/plugins/combat/plugin.xml
index 0047e739..7219e4dd 100644
--- a/data/plugins/combat/plugin.xml
+++ b/data/plugins/combat/plugin.xml
@@ -6,8 +6,12 @@
Adds fully functioning melee, ranged and magic combat.
garyttierney
+ Shiver
+ Major
+
+
@@ -15,9 +19,10 @@
+
-
+
@@ -25,12 +30,18 @@
+
+
+
+
+
+
+
+
+
-
-
-
attributes
diff --git a/data/plugins/combat/projectile.rb b/data/plugins/combat/projectile.rb
deleted file mode 100644
index d740e2d8..00000000
--- a/data/plugins/combat/projectile.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-def create_projectile(item, drop_rate:, graphic:, projectile:)
- items = i
-end
\ No newline at end of file
diff --git a/data/plugins/combat/projectile_type.rb b/data/plugins/combat/projectile_type.rb
new file mode 100644
index 00000000..a6b8707b
--- /dev/null
+++ b/data/plugins/combat/projectile_type.rb
@@ -0,0 +1,20 @@
+PROJECTILE_TYPES = {}
+
+def create_projectile_type(name, start_height:, end_height:, delay:, speed:, slope:, radius:)
+ PROJECTILE_TYPES[name] = ProjectileType.new(start_height, end_height, delay, speed, slope, radius)
+end
+
+private
+
+class ProjectileType
+ attr_reader :start_height, :end_height, :delay, :speed, :slope, :radius
+
+ def initialize(start_height, end_height, delay, speed, slope, radius)
+ @start_height = start_height
+ @end_height = end_height
+ @delay = delay
+ @speed = speed
+ @slope = slope
+ @radius = radius
+ end
+end
diff --git a/data/plugins/combat/util.rb b/data/plugins/combat/util.rb
index f9f104c4..e5252843 100644
--- a/data/plugins/combat/util.rb
+++ b/data/plugins/combat/util.rb
@@ -1,12 +1,14 @@
java_import 'org.apollo.game.model.entity.EquipmentConstants'
class CombatUtil
+ def self.current_speed(_mob)
+ end
def self.calculate_max_hit(source)
strength = source.skill_set.get_skill(Skill::STRENGTH)
- strength_stat = 5 #source.bonus_stat(:other, :strength)
+ strength_stat = 5 # source.bonus_stat(:other, :strength)
- effective_strength_damage = (strength.current_level) #* prayer_multiplier
+ effective_strength_damage = (strength.current_level) # * prayer_multiplier
if [:aggressive, :alt_aggressive].include? source.combat_style
effective_strength_damage += 3
@@ -19,8 +21,8 @@ class CombatUtil
def self.calculate_accuracy(source, target)
weapon = EquipmentUtil.equipped_weapon source
weapon_class = weapon.weapon_class
- combat_style = weapon_class.style_at source.combat_style
- attack_type = weapon.weapon_class.attack_type combat_style
+ combat_style = weapon_class.selected_style source
+ attack_type = combat_style.attack_type
attack_stat = [1, 1].max
defence_stat = [1, 1].max
@@ -28,11 +30,11 @@ class CombatUtil
attack = source.skill_set.get_skill(Skill::ATTACK).current_level.to_f
defence = target.skill_set.get_skill(Skill::DEFENCE).current_level.to_f
- attack_prayer_multiplier = 1 #TODO: Prayer
+ attack_prayer_multiplier = 1 # TODO: Prayer
attack_accuracy = attack_stat * attack * attack_prayer_multiplier
attack_accuracy = 0.1 if attack_accuracy < 0
- defence_prayer_multiplier = 1 #TODO: Prayer
+ defence_prayer_multiplier = 1 # TODO: Prayer
defence_accuracy = defence_stat * defence * defence_prayer_multiplier
defence_accuracy = 1 if defence_accuracy < 0
@@ -42,7 +44,7 @@ class CombatUtil
# Calculates a hit for the given Mob and special attack flag.
def self.calculate_hit(source, target)
- accuracy = calculate_accuracy source, target
+ accuracy = calculate_accuracy(source, target)
max_hit = calculate_max_hit(source) + 1
if rand <= accuracy
@@ -57,14 +59,16 @@ class EquipmentUtil
def self.equipped_weapon(source)
item = source.equipment.get(EquipmentConstants::WEAPON)
- if item.nil?
- return NAMED_WEAPONS[:unarmed]
- end
+ return NAMED_WEAPONS[:unarmed] if item.nil?
WEAPONS[item.id]
end
- def self.equipped_projectile(source)
+ def self.equipped_ammo(source)
item = source.equipment.get(EquipmentConstants::ARROWS)
+
+ return nil if item.nil?
+
+ AMMO[item.id]
end
-end
\ No newline at end of file
+end
diff --git a/data/plugins/combat/weapon.rb b/data/plugins/combat/weapon.rb
index 930e7ec1..b0069a6b 100644
--- a/data/plugins/combat/weapon.rb
+++ b/data/plugins/combat/weapon.rb
@@ -1,4 +1,5 @@
java_import 'org.apollo.cache.def.ItemDefinition'
+java_import 'org.apollo.cache.def.EquipmentDefinition'
java_import 'org.apollo.game.model.inv.InventoryAdapter'
java_import 'org.apollo.game.model.entity.EquipmentConstants'
java_import 'org.apollo.game.model.entity.AnimationSet'
@@ -16,41 +17,52 @@ def create_weapon(identifier, class_name = nil, named: false, &block)
end
end
-
private
def create_normal_weapon(item_matcher, class_name = nil, &block)
items = find_entities :item, item_matcher, -1
+ fail "Unable to find weapon matching #{item_matcher}" if items.empty?
+
items.each do |item_id|
+ equipment_def = EquipmentDefinition.lookup(item_id)
+
+ next if equipment_def.nil? || equipment_def.slot != EquipmentConstants::WEAPON
+
definition = ItemDefinition.lookup(item_id)
definition_name = definition.name.downcase.to_s
-
+
if class_name.nil?
class_name =
case definition_name
- when /[a-zA-Z]+ 2h sword/
- :two_handed_sword
- when /[a-zA-Z]+ scimitar/
- :scimitar
- when /[a-zA-Z]+ dagger/
- :dagger
- else
- raise "Couldn't find a suitable weapon class for the given weapon."
+ when /[a-zA-Z]+ 2h sword/
+ :two_handed_sword
+ when /[a-zA-Z]+ scimitar/
+ :scimitar
+ when /[a-zA-Z]+ dagger/
+ :dagger
+ when /[a-z\s]*longbow/
+ :longbow
+ when /[a-z\s]*shortbow/
+ :shortbow
+ when /[a-z]+ c'bow/
+ :crossbow
+ else
+ fail "Couldn't find a suitable weapon class for the given weapon."
end
end
WEAPONS[item_id] = Weapon.new(definition.name, WEAPON_CLASSES[class_name])
- WEAPONS[item_id].instance_eval &block
+ WEAPONS[item_id].instance_eval &block if block_given?
end
end
def create_named_weapon(name, class_name, &block)
NAMED_WEAPONS[name] = Weapon.new name.to_s.capitalize, WEAPON_CLASSES[class_name]
- NAMED_WEAPONS[name].instance_eval &block
+ NAMED_WEAPONS[name].instance_eval &block if block_given?
end
# Represents an equippable weapon, and the class it belongs to.
-#
+#
# * has an optional special_attack
# * belongs to a certain WeaponClass, and inherits bonuses from it.
class Weapon
@@ -65,18 +77,22 @@ class Weapon
end
def special_attack?
- not special_attack.nil?
+ !special_attack.nil?
end
- def set_special_attack(energy_requirement:, animation:, graphic: nil, &block)
- # todo figure out if ranged or melee
+ def set_special_attack(speed:, range: 1, energy_requirement:, animation:, graphic: nil, &block)
+ attack_dsl = AttackDSL.new
+ attack_dsl.speed = speed
+ attack_dsl.range = range
+ attack_dsl.animation = animation
+ attack_dsl.graphic = graphic
+ attack_dsl.add_requirement SpecialEnergyRequirement.new(energy_requirement)
+ attack_dsl.instance_eval &block
- requirements = [SpecialEnergyRequirement.new(energy_requirement)]
- @special_attack = ProcAttack.new(block, animation: animation, graphic: graphic, requirements: requirements)
+ @special_attack = attack_dsl.to_attack
end
end
-
def update_weapon_animations(player)
default_animations = AnimationSet::DEFAULT_ANIMATION_SET
player_animations = player.animation_set
@@ -93,33 +109,33 @@ def update_weapon_animations(player)
weapon_class = weapon.weapon_class
[:stand, :walk, :run, :idle_turn, :turn_around, :turn_left, :turn_right].each do |key|
- animation = weapon_class.other_animation(key)
+ animation = weapon_class.animation(key)
- unless animation.nil?
- case key
- when :stand
- player_animations.stand = animation
- when :walk
- player_animations.walking = animation
- when :run
- player_animations.running = animation
- when :idle_turn
- player_animations.idle_turn = animation
- when :turn_around
- player_animations.turn_around = animation
- when :turn_left
- player_animations.turn_left = animation
- when :turn_right
- player_animations.turn_right = animation
- else
- # type code here
- end
+ next if animation.nil?
+ case key
+ when :stand
+ player_animations.stand = animation
+ when :walk
+ player_animations.walking = animation
+ when :run
+ player_animations.running = animation
+ when :idle_turn
+ player_animations.idle_turn = animation
+ when :turn_around
+ player_animations.turn_around = animation
+ when :turn_left
+ player_animations.turn_left = animation
+ when :turn_right
+ player_animations.turn_right = animation
end
end
end
on :message, :item_option do |player, message|
- update_weapon_animations(player) if message.option == 2 and message.interface_id == SynchronizationInventoryListener::INVENTORY_ID
+ next unless message.interface_id == SynchronizationInventoryListener::INVENTORY_ID &&
+ message.option == 2
+
+ update_weapon_animations(player)
end
on :login do |event|
@@ -127,5 +143,8 @@ on :login do |event|
end
on :message, :item_action do |player, message|
- update_weapon_animations(player) if message.interface_id == SynchronizationInventoryListener::EQUIPMENT_ID and message.slot == EquipmentConstants::WEAPON
+ next unless message.interface_id == SynchronizationInventoryListener::EQUIPMENT_ID &&
+ message.slot == EquipmentConstants::WEAPON
+
+ update_weapon_animations(player)
end
diff --git a/data/plugins/combat/weapon_class.rb b/data/plugins/combat/weapon_class.rb
index fd57921b..64a347d1 100644
--- a/data/plugins/combat/weapon_class.rb
+++ b/data/plugins/combat/weapon_class.rb
@@ -4,122 +4,169 @@ COMBAT_STYLES = [
:defensive,
:controlled,
:alt_aggressive,
- :accurate_ranged,
:rapid,
:long_range
]
-MELEE_COMBAT_STYLES = [
- :accurate,
- :aggressive,
- :alt_aggressive,
- :controlled,
- :defensive
-]
-
-RANGE_COMBAT_STYLES = [
- :accurate_ranged,
- :rapid,
- :long_range
-]
+##
+# A model for a weapon class, which contains the necessary inform
class WeaponClass
- attr_reader :widget, :name, :special_bar_button, :special_bar_config
+ ##
+ # Allow this class to set combat bonuses.
include Combat::BonusContainer
- def initialize(name, widget)
- @name = name
- @widget = widget
- @styles = {}
- @style_attacks = {}
- @style_offsets = []
- @animations = {}
+ ##
+ # The widget which is displayed on the combat tab when this weapon class
+ # is in use.
+
+ attr_reader :widget
+
+ ##
+ # A unique identifier for this class.
+
+ attr_reader :name
+
+ ##
+ # The id of the special bar button for this weapon class, if applicable.
+
+ attr_reader :special_bar_button
+
+ ##
+ # The id of the widget config used to hide this weapon classes special bar, if applicable.
+
+ attr_reader :special_bar_config
+
+ ##
+ # Create a new WeaponClass with the given name and specified widget id.
+ #
+ # @param [Symbol] name The unique name to give this weapon class.
+ # @param [Number] widget The id of the widget associated with this weapon class.
+
+ def initialize(name, widget, type)
+ @name = name
+ @widget = widget
+ @type = type
+ @styles = {}
+ @defaults = {}
+ @animations = {}
end
- def add_style(style, button: nil, attack_type: nil, speed: nil, animation: nil, block_animation: nil, range: 1)
- raise 'Invalid combat style given' unless COMBAT_STYLES.include? style
+ ##
+ # Set default properties which are used for styles which don't have
+ # those properties defined.
+ #
+ # @param [Hash] props
+ # * :attack_type [Symbol] The default attack type for styles in this class.
+ # * :speed [Number] The default attack speed for styles in this class.
+ # * :animation [Number] The default attack animation id for styles in this class.
+ # * :block_animation [Number] The default block animation id for styles in this class.
+ # * :range [Number] The default attack range for styles in this class.
- @styles[style] = {
- :attack_type => attack_type,
- :speed => speed,
- :animation => animation,
- :block_animation => block_animation,
- :range => range,
- :button => button == nil ? @styles.size + 1 : button
- }
-
- @style_offsets.push style
-
- if MELEE_COMBAT_STYLES.include? style
- @style_attacks[style] = Attack.new(animation: animation)
+ def defaults(props)
+ @defaults = props
+ end
+
+ ##
+ # Add a new {@link CombatStyle} to to this class.
+ #
+ # @param [Symbol] style The name of the style, one of {@link COMBAT_STYLES}.
+ # @param [Hash] properties
+ # * :button [Number] The id of the button which activates this style.
+ # * :attack_type [Symbol] The attack type for this style.
+ # * :speed [Number] The attack speed for this style.
+ # * :animation [Number] The attack animation id for this style.
+ # * :graphic [Hash|Number] The id, or a hash specifying height and id for the graphic for this style.
+ # * :requirements [Array] An array of [AttackRequirement] objects to be applied to this styles attack.
+ # * :block_animation [Number] The block animation id for this style.
+ # * :range [Number] The attack range for this style.
+ #
+ # @param [Proc] block An optional block which is called in the context of an AttackDSL if present, to create an attack for
+ # this style.
+
+ def style(style, properties = {}, &block)
+ fail 'Invalid combat style given' unless COMBAT_STYLES.include? style
+
+ properties = @defaults.merge properties
+
+ ## The config ID used to set the active combat style, typically 0-3. TODO: does this always work?
+ config = @styles.size
+ button, attack_type, block_animation = properties[:button], properties[:attack_type], properties[:block_animation]
+
+ @styles[style] = CombatStyle.new(button, config, attack_type, block_animation)
+
+ if block_given?
+ attack_dsl = AttackDSL.new
+ attack_dsl.speed = properties[:speed]
+ attack_dsl.graphic = properties[:graphic]
+ attack_dsl.animation = properties[:animation]
+ attack_dsl.range = properties[:range]
+ properties[:requirements].each {|requirement| attack_dsl.add_requirement requirement }
+ attack_dsl.instance_eval &block
+
+ @styles[style].attack = attack_dsl.to_attack
+ end
+
+ return unless @styles[style].attack.nil?
+
+ ## Get rid of any properties which aren't included in the keyword argument list
+ properties.delete_if {|key| ![:speed, :animation, :range, :requirements, :graphic].include? key }
+
+ if @type == :melee
+ @styles[style].attack = Attack.new(properties)
+ elsif @type == :ranged
+ @styles[style].attack = RangedAttack.new(properties)
end
end
- def attack(style)
- @style_attacks[style]
+ ##
+ # Get the currently selected combat style in this class for the given mob.
+ # @param [Mob] mob
+ # @return [CombatStyle]
+
+ def selected_style(mob)
+ style = @styles.find { |_key, value| value.button == mob.combat_style }
+
+ style = @styles.min_by { |_key, value| value.button } if style.nil?
+
+ # We want the last element because Hash#find returns [key, value]
+ style.last
end
- def attack_type(style)
- @styles[style][:attack_type]
- end
+ ##
+ # Get the animation id for the given animation type.
- def block_animation(style)
- @styles[style][:block_animation]
- end
-
- def button(style)
- @styles[style][:button]
- end
-
- def config(style)
- @style_offsets.find_index {|v| v == style }
- end
-
- def other_animation(type)
+ def animation(type)
@animations[type]
end
- def speed(style)
- @styles[style][:speed] || default_speed
- end
-
- def style_at(button)
- selected_style = @styles.select { |key, hash| hash[:button] == button }.keys[0]
-
- if selected_style == nil
- selected_style = @styles.min_by { |key, hash| hash[:button] }.first
- end
-
- selected_style
- end
-
- def default_speed(speed = nil)
- unless speed.nil?
- @default_speed = speed
- end
-
- @default_speed
- end
+ ##
+ # Set the widget config and button for this classes special attack bar.
def special_bar(config, button)
@special_bar_config = config
@special_bar_button = button
end
+ ##
+ # Check if this weapon class supports a special attack bar.
+
def special_bar?
- !@special_bar_button.nil? and !@special_bar_config.nil?
+ !@special_bar_button.nil? && !@special_bar_config.nil?
end
+ ##
+
def animations(stand: nil, walk: nil, run: nil, idle_turn: nil, turn_around: nil, turn_left: nil, turn_right: nil)
@animations = {
- :stand => stand,
- :walk => walk,
- :run => run,
- :idle_turn => idle_turn,
- :turn_around => turn_around,
- :turn_left => turn_left,
- :turn_right => turn_right
+ stand: stand,
+ walk: walk,
+ run: run,
+ idle_turn: idle_turn,
+ turn_around: turn_around,
+ turn_left: turn_left,
+ turn_right: turn_right
}
end
end
@@ -127,8 +174,8 @@ end
WEAPON_CLASSES = {}
WEAPON_CLASS_INTERFACE_MAP = {}
-def create_weapon_class(name, widget:, &block)
- weapon_class = WeaponClass.new(name, widget)
+def create_weapon_class(name, widget:, type: :melee, &block)
+ weapon_class = WeaponClass.new(name, widget, type)
weapon_class.instance_eval &block
WEAPON_CLASSES[name.to_sym] = weapon_class
diff --git a/data/plugins/combat/weapons/bows.rb b/data/plugins/combat/weapons/bows.rb
deleted file mode 100644
index b2f242ed..00000000
--- a/data/plugins/combat/weapons/bows.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-BOW_WIDGET_ID = 10
-BOW_SPECIAL_BAR_ID = 10
-#
-# create_weapon_class :longbow, widget: BOW_WIDGET_ID do
-# special_bar = BOW_SPECIAL_BAR_ID
-#
-#
-# add_style :accurate, speed: 6, range: 7
-# add_style :rapid, speed: 6, range: 7
-# add_style :long_range, speed: 6, range: 9
-# end
-#
-# create_weapon_class :shortbow, widget: BOW_WIDGET_ID do
-# special_bar = BOW_SPECIAL_BAR_ID
-#
-# add_style :accurate, speed: 4, range: 7
-# add_style :rapid, speed: 3, range: 7
-# add_style :long_range, speed: 4, range: 9
-# end
-#
diff --git a/data/plugins/combat/weapons/daggers.rb b/data/plugins/combat/weapons/daggers.rb
deleted file mode 100644
index 0405148f..00000000
--- a/data/plugins/combat/weapons/daggers.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-DAGGER_WIDGET_ID = 89
-DAGGER_SPECIAL_CONFIG_ID = 12
-DAGGER_SPECIAL_BUTTON_ID = 10
-
-create_weapon_class :dagger, widget: DAGGER_WIDGET_ID do
- default_speed 4
-
- attack_bonuses crush: -4, magic: 1
- defence_bonuses magic: 1
-
- add_style :accurate, attack_type: :stab, animation: 7041, button: 2
- add_style :aggressive, attack_type: :stab, animation: 7041, button: 3
- add_style :alt_aggressive, attack_type: :slash, animation: 7048, button: 4
- add_style :defensive, attack_type: :stab, animation: 7049, button: 5
-end
-
-# Need a separate WeaponClass for the dragon dagger because
-# of the differing animations
-create_weapon_class :dragon_dagger, widget: DAGGER_WIDGET_ID do
- default_speed 4
- special_bar DAGGER_SPECIAL_CONFIG_ID, DAGGER_SPECIAL_BUTTON_ID
-
- attack_bonuses crush: -4, magic: 1
- defence_bonuses magic: 1
-
- add_style :accurate, attack_type: :stab, animation: 402, button: 2
- add_style :aggressive, attack_type: :stab, animation: 402, button: 3
- add_style :alt_aggressive, attack_type: :slash, animation: 402, button: 4
- add_style :defensive, attack_type: :stab, animation: 402, button: 5
-end
-
-create_weapon /(?:drag|dragon) dagger.*/, :dragon_dagger do
- set_special_attack energy_requirement: 25, animation: 1062, graphic: {id: 252, height: 100} do |source, target|
- damage! source, target, CombatUtil::calculate_hit(source, target)
- damage! source, target, CombatUtil::calculate_hit(source, target), 1
- end
-end
\ No newline at end of file
diff --git a/data/plugins/combat/weapons/melee/daggers.rb b/data/plugins/combat/weapons/melee/daggers.rb
new file mode 100644
index 00000000..dc315b6c
--- /dev/null
+++ b/data/plugins/combat/weapons/melee/daggers.rb
@@ -0,0 +1,37 @@
+DAGGER_WIDGET_ID = 89
+DAGGER_SPECIAL_CONFIG_ID = 12
+DAGGER_SPECIAL_BUTTON_ID = 10
+
+create_weapon_class :dagger, widget: DAGGER_WIDGET_ID do
+ defaults speed: 4, animation: 7041, attack_type: :stab
+
+ attack_bonuses crush: -4, magic: 1
+ defence_bonuses magic: 1
+
+ style :accurate, button: 2
+ style :aggressive, button: 3
+ style :alt_aggressive, attack_type: :slash, animation: 7048, button: 4
+ style :defensive, animation: 7049, button: 5
+end
+
+# Need a separate WeaponClass for the dragon dagger because
+# of the differing animations
+create_weapon_class :dragon_dagger, widget: DAGGER_WIDGET_ID do
+ defaults speed: 4, animation: 402, attack_type: :stab
+ special_bar DAGGER_SPECIAL_CONFIG_ID, DAGGER_SPECIAL_BUTTON_ID
+
+ attack_bonuses crush: -4, magic: 1
+ defence_bonuses magic: 1
+
+ style :accurate, button: 2
+ style :aggressive, button: 3
+ style :alt_aggressive, attack_type: :slash, button: 4
+ style :defensive, button: 5
+end
+
+create_weapon /(?:drag|dragon) dagger.*/, :dragon_dagger do
+ set_special_attack speed: 4, energy_requirement: 25, animation: 1062, graphic: { id: 252, height: 100 } do
+ damage! delay: 0
+ damage! delay: 1
+ end
+end
diff --git a/data/plugins/combat/weapons/melee/godswords.rb b/data/plugins/combat/weapons/melee/godswords.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/data/plugins/combat/weapons/melee/granite_maul.rb b/data/plugins/combat/weapons/melee/granite_maul.rb
new file mode 100644
index 00000000..eeff0043
--- /dev/null
+++ b/data/plugins/combat/weapons/melee/granite_maul.rb
@@ -0,0 +1,9 @@
+create_weapon :granite_maul do
+ attack_bonuses slash: 92, crush: 80
+ other_bonuses melee_strength: 70
+
+ set_special_attack speed: 0, energy_requirement: 60, animation: 3157, graphic: 1225 do |_source, _target|
+ damage! delay: 0
+ damage! delay: 1
+ end
+end
diff --git a/data/plugins/combat/weapons/scimitars.rb b/data/plugins/combat/weapons/melee/scimitars.rb
similarity index 75%
rename from data/plugins/combat/weapons/scimitars.rb
rename to data/plugins/combat/weapons/melee/scimitars.rb
index f7f78b1f..ffee0053 100644
--- a/data/plugins/combat/weapons/scimitars.rb
+++ b/data/plugins/combat/weapons/melee/scimitars.rb
@@ -3,16 +3,16 @@ SCIMITAR_SPECIAL_BAR_CONFIG_ID = 21
SCIMITAR_SPECIAL_BAR_BUTTON_ID = 21
create_weapon_class :scimitar, widget: SCIMITAR_WIDGET_ID do
- default_speed 4
+ defaults speed: 4, animation: 390, attack_type: :slash
special_bar SCIMITAR_SPECIAL_BAR_CONFIG_ID, SCIMITAR_SPECIAL_BAR_BUTTON_ID
attack_bonuses crush: -2
defence_bonuses slash: -1
- add_style :accurate, attack_type: :slash, animation: 390, button: 2
- add_style :aggressive, attack_type: :slash, animation: 390, button: 3
- add_style :alt_aggressive, attack_type: :stab, animation: 391, button: 4
- add_style :defensive, attack_type: :slash, animation: 390, button: 5
+ style :accurate, button: 2
+ style :aggressive, button: 3
+ style :alt_aggressive, attack_type: :stab, animation: 391, button: 4
+ style :defensive, button: 5
end
create_weapon :iron_scimitar do
@@ -46,6 +46,6 @@ create_weapon :rune_scimitar do
end
create_weapon :dragon_scimitar do
- attack_bonuses :stab => 8, slash: 67
+ attack_bonuses stab: 8, slash: 67
other_bonuses melee_strength: 66
end
diff --git a/data/plugins/combat/weapons/melee/swords.rb b/data/plugins/combat/weapons/melee/swords.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/data/plugins/combat/weapons/two_handed_swords.rb b/data/plugins/combat/weapons/melee/two_handed_swords.rb
similarity index 72%
rename from data/plugins/combat/weapons/two_handed_swords.rb
rename to data/plugins/combat/weapons/melee/two_handed_swords.rb
index 3fcf374f..18eb8195 100644
--- a/data/plugins/combat/weapons/two_handed_swords.rb
+++ b/data/plugins/combat/weapons/melee/two_handed_swords.rb
@@ -1,9 +1,9 @@
-TWO_HANDED_SWORD_WIDGET_ID = 82
+TWO_HANDED_SWORD_WIDGET_ID = 82
TWO_HANDED_SWORD_SPECIAL_CONFIG_ID = 12
TWO_HANDED_SWORD_SPECIAL_BUTTON_ID = 10
create_weapon_class :two_handed_sword, widget: TWO_HANDED_SWORD_WIDGET_ID do
- default_speed 7
+ defaults speed: 7, animation: 7041, attack_type: :slash
special_bar TWO_HANDED_SWORD_SPECIAL_CONFIG_ID, TWO_HANDED_SWORD_SPECIAL_BUTTON_ID
animations stand: 7047, walk: 7046, run: 7039, idle_turn: 7044, turn_around: 7044, turn_left: 7043, turn_right: 7044
@@ -11,10 +11,10 @@ create_weapon_class :two_handed_sword, widget: TWO_HANDED_SWORD_WIDGET_ID do
attack_bonuses stab: -4, magic: -4
defence_bonuses range: -1
- add_style :accurate, attack_type: :slash, animation: 7041, button: 2
- add_style :aggressive, attack_type: :crush, animation: 7041, button: 3
- add_style :alt_aggressive, attack_type: :crush, animation: 7048, button: 4
- add_style :defensive, attack_type: :slash, animation: 7049, button: 5
+ style :accurate, button: 2
+ style :aggressive, attack_type: :crush, button: 3
+ style :alt_aggressive, attack_type: :crush, animation: 7048, button: 4
+ style :defensive, animation: 7049, button: 5
end
create_weapon :iron_2h_sword do
@@ -51,7 +51,7 @@ create_weapon :dragon_2h_sword do
attack_bonuses slash: 92, crush: 80
other_bonuses melee_strength: 70
- set_special_attack energy_requirement: 60, animation: 3157, graphic: 1225 do |source, target|
- damage! source, target, 5
+ set_special_attack speed: 7, energy_requirement: 60, animation: 3157, graphic: 1225 do |_source, _target|
+ damage!
end
-end
\ No newline at end of file
+end
diff --git a/data/plugins/combat/weapons/ranged/bows.rb b/data/plugins/combat/weapons/ranged/bows.rb
new file mode 100644
index 00000000..82f98ebe
--- /dev/null
+++ b/data/plugins/combat/weapons/ranged/bows.rb
@@ -0,0 +1,25 @@
+BOW_WIDGET_ID = 77
+BOW_SPECIAL_CONFIG_ID = 10
+BOW_SPECIAL_BUTTON_ID = 8
+
+create_weapon_class :shortbow, widget: BOW_WIDGET_ID, type: :ranged do
+ defaults animation: 426
+ special_bar BOW_SPECIAL_CONFIG_ID, BOW_SPECIAL_BUTTON_ID
+
+ style :accurate, speed: 4, range: 7, button: 2
+ style :rapid, speed: 3, range: 7, button: 3
+ style :long_range, speed: 4, range: 9, button: 4
+end
+
+create_weapon :shortbow
+
+create_weapon_class :longbow, widget: BOW_WIDGET_ID, type: :ranged do
+ defaults animation: 426
+ special_bar BOW_SPECIAL_CONFIG_ID, BOW_SPECIAL_BUTTON_ID
+
+ style :accurate, speed: 6, range: 7, button: 2
+ style :rapid, speed: 6, range: 7, button: 3
+ style :long_range, speed: 6, range: 9, button: 4
+end
+
+create_weapon :longbow
diff --git a/data/plugins/combat/weapons/ranged/crossbows.rb b/data/plugins/combat/weapons/ranged/crossbows.rb
new file mode 100644
index 00000000..a7dc8bf3
--- /dev/null
+++ b/data/plugins/combat/weapons/ranged/crossbows.rb
@@ -0,0 +1,11 @@
+CROSSBOW_WIDGET_ID = 79
+
+create_weapon_class :crossbow, widget: CROSSBOW_WIDGET_ID, type: :ranged do
+ defaults animation: 426, speed: 6, range: 7
+
+ style :accurate, range: 7, button: 2
+ style :rapid, speed: 5, button: 3
+ style :long_range, range: 9, button: 4
+end
+
+create_weapon /rune c'bow/, :crossbow
diff --git a/data/plugins/combat/weapons/ranged/darts.rb b/data/plugins/combat/weapons/ranged/darts.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/data/plugins/combat/weapons/ranged/magic_bows.rb b/data/plugins/combat/weapons/ranged/magic_bows.rb
new file mode 100644
index 00000000..08f8a662
--- /dev/null
+++ b/data/plugins/combat/weapons/ranged/magic_bows.rb
@@ -0,0 +1,8 @@
+create_projectile_type :msb, start_height: 41, end_height: 37, delay: 31, speed: 3, slope: 15, radius: 8
+
+create_weapon :magic_shortbow do
+ set_special_attack speed: 3, range: 7, energy_requirement: 60, animation: 1074, graphic: { id: 256, height: 100 } do
+ range_damage! projectile: 249, projectile_type: PROJECTILE_TYPES[:msb]
+ range_damage! projectile: 249, projectile_type: PROJECTILE_TYPES[:msb], projectile_graphic: 256, delay: 1
+ end
+end
diff --git a/data/plugins/combat/weapons/unarmed.rb b/data/plugins/combat/weapons/unarmed.rb
index dbee6cd6..efa724a2 100644
--- a/data/plugins/combat/weapons/unarmed.rb
+++ b/data/plugins/combat/weapons/unarmed.rb
@@ -1,11 +1,11 @@
create_weapon_class :no_weapon, widget: 92 do
- default_speed 4
+ defaults speed: 4, animation: 422
- add_style :accurate, animation: 422, block_animation: 424
- add_style :aggressive, animation: 423, block_animation: 424
- add_style :defensive, animation: 422, block_animation: 424
+ style :accurate, button: 2
+ style :aggressive, animation: 423, button: 3
+ style :defensive, button: 4
end
create_weapon :unarmed, :no_weapon, named: true do
# Todo factor out empty blocks
-end
\ No newline at end of file
+end
diff --git a/data/plugins/combat/widgets/combat_tab.rb b/data/plugins/combat/widgets/combat_tab.rb
index 03bf633a..36789f52 100644
--- a/data/plugins/combat/widgets/combat_tab.rb
+++ b/data/plugins/combat/widgets/combat_tab.rb
@@ -5,9 +5,8 @@ java_import 'org.apollo.game.message.impl.SetInterfaceConfigMessage'
java_import 'org.apollo.game.message.impl.ConfigMessage'
java_import 'org.apollo.game.model.inv.SynchronizationInventoryListener'
-
on :message, :item_option do |player, message|
- update_combat_tab(player) if message.option == 2 and message.interface_id == SynchronizationInventoryListener::INVENTORY_ID
+ update_combat_tab(player) if message.option == 2 && message.interface_id == SynchronizationInventoryListener::INVENTORY_ID
end
on :login do |event|
@@ -15,10 +14,9 @@ on :login do |event|
end
on :message, :item_action do |player, message|
- update_combat_tab(player) if message.interface_id == SynchronizationInventoryListener::EQUIPMENT_ID and message.slot == EquipmentConstants::WEAPON
+ update_combat_tab(player) if message.interface_id == SynchronizationInventoryListener::EQUIPMENT_ID && message.slot == EquipmentConstants::WEAPON
end
-
on :message, :button do |player, msg|
weapon = EquipmentUtil.equipped_weapon player
weapon_class = weapon.weapon_class
@@ -26,7 +24,7 @@ on :message, :button do |player, msg|
next unless weapon_class_widget == msg.interface_id
- if weapon_class.special_bar? and msg.button == weapon_class.special_bar_button
+ if weapon_class.special_bar? && msg.button == weapon_class.special_bar_button
player.using_special = !player.using_special
update_special_bar(player)
else
@@ -57,14 +55,11 @@ def update_combat_tab(player)
end
def update_combat_style(player)
- weapon = EquipmentUtil.equipped_weapon player
- weapon_class = weapon.weapon_class
+ weapon = EquipmentUtil.equipped_weapon player
+ combat_style = weapon.weapon_class.selected_style player
- # Update the combat style in case we had an invalid style selected,
+ # Update the combat style in case we had an invalid style selected,
# and therefore reverted to the first combat style
- selected_style = weapon_class.style_at(player.combat_style)
-
- player.combat_style = weapon_class.button selected_style
- player.send ConfigMessage.new(43, weapon_class.config(selected_style))
+ player.combat_style = combat_style.button
+ player.send ConfigMessage.new(43, combat_style.config)
end
-
diff --git a/data/plugins/combat/widgets/equipment_stats_window.rb b/data/plugins/combat/widgets/equipment_stats_window.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/data/plugins/combat/widgets/special_bar.rb b/data/plugins/combat/widgets/special_bar.rb
index dd522e05..6f4cdea0 100644
--- a/data/plugins/combat/widgets/special_bar.rb
+++ b/data/plugins/combat/widgets/special_bar.rb
@@ -20,4 +20,3 @@ on :login do |event|
update_special_bar player
end
end
-