From 3082fade1d5f8c96828754cdc0c624fc016c8f57 Mon Sep 17 00:00:00 2001 From: Gary Tierney Date: Sun, 3 Jan 2016 22:24:43 +0000 Subject: [PATCH] Add ranged combat support * Clean up the CombatAction and Attack code to make it easier to use for range. * Add collision detection to the distance checks before attacking in the CombatAction. * Create a Ruby DSL for defining projectile types and fix the ProjectileUpdateOperation so it uses the correct position offset. * Fix the packet structure of the HintIconMessageEncoder. --- data/plugins/combat/ammo.rb | 34 +++ data/plugins/combat/ammo/arrows.rb | 15 ++ data/plugins/combat/ammo/bolts.rb | 10 + data/plugins/combat/attack.rb | 192 +++++++++++---- .../{weapons/swords.rb => attack_effect.rb} | 0 data/plugins/combat/attack_requirements.rb | 60 ++++- data/plugins/combat/attributes.rb | 8 +- data/plugins/combat/bonuses.rb | 28 +-- data/plugins/combat/combat.rb | 32 ++- data/plugins/combat/combat_action.rb | 146 ++++-------- data/plugins/combat/combat_state.rb | 35 +-- data/plugins/combat/combat_style.rb | 11 + data/plugins/combat/equipment.rb | 4 +- data/plugins/combat/plugin.xml | 19 +- data/plugins/combat/projectile.rb | 3 - data/plugins/combat/projectile_type.rb | 20 ++ data/plugins/combat/util.rb | 28 ++- data/plugins/combat/weapon.rb | 101 ++++---- data/plugins/combat/weapon_class.rb | 219 +++++++++++------- data/plugins/combat/weapons/bows.rb | 20 -- data/plugins/combat/weapons/daggers.rb | 37 --- data/plugins/combat/weapons/melee/daggers.rb | 37 +++ .../plugins/combat/weapons/melee/godswords.rb | 0 .../combat/weapons/melee/granite_maul.rb | 9 + .../combat/weapons/{ => melee}/scimitars.rb | 12 +- data/plugins/combat/weapons/melee/swords.rb | 0 .../weapons/{ => melee}/two_handed_swords.rb | 18 +- data/plugins/combat/weapons/ranged/bows.rb | 25 ++ .../combat/weapons/ranged/crossbows.rb | 11 + data/plugins/combat/weapons/ranged/darts.rb | 0 .../combat/weapons/ranged/magic_bows.rb | 8 + data/plugins/combat/weapons/unarmed.rb | 10 +- data/plugins/combat/widgets/combat_tab.rb | 21 +- .../combat/widgets/equipment_stats_window.rb | 0 data/plugins/combat/widgets/special_bar.rb | 1 - 35 files changed, 741 insertions(+), 433 deletions(-) create mode 100644 data/plugins/combat/ammo.rb create mode 100644 data/plugins/combat/ammo/arrows.rb create mode 100644 data/plugins/combat/ammo/bolts.rb rename data/plugins/combat/{weapons/swords.rb => attack_effect.rb} (100%) create mode 100644 data/plugins/combat/combat_style.rb delete mode 100644 data/plugins/combat/projectile.rb create mode 100644 data/plugins/combat/projectile_type.rb delete mode 100644 data/plugins/combat/weapons/bows.rb delete mode 100644 data/plugins/combat/weapons/daggers.rb create mode 100644 data/plugins/combat/weapons/melee/daggers.rb create mode 100644 data/plugins/combat/weapons/melee/godswords.rb create mode 100644 data/plugins/combat/weapons/melee/granite_maul.rb rename data/plugins/combat/weapons/{ => melee}/scimitars.rb (75%) create mode 100644 data/plugins/combat/weapons/melee/swords.rb rename data/plugins/combat/weapons/{ => melee}/two_handed_swords.rb (72%) create mode 100644 data/plugins/combat/weapons/ranged/bows.rb create mode 100644 data/plugins/combat/weapons/ranged/crossbows.rb create mode 100644 data/plugins/combat/weapons/ranged/darts.rb create mode 100644 data/plugins/combat/weapons/ranged/magic_bows.rb create mode 100644 data/plugins/combat/widgets/equipment_stats_window.rb 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 -