diff --git a/data/plugins/combat/attack.rb b/data/plugins/combat/attack.rb
new file mode 100644
index 00000000..dac67141
--- /dev/null
+++ b/data/plugins/combat/attack.rb
@@ -0,0 +1,88 @@
+java_import 'org.apollo.cache.def.ItemDefinition'
+java_import 'org.apollo.game.model.Animation'
+java_import 'org.apollo.game.model.Graphic'
+
+class BaseAttack
+ attr_reader :requirements, :range
+
+ def initialize(animation, graphic = nil, range = 1, requirements = [])
+ @animation = animation
+ @graphic = graphic
+ @range = range
+ @requirements = requirements
+ end
+
+ def do(source, target)
+ source.play_animation(Animation.new(@animation))
+
+ unless @graphic.nil?
+ if @graphic.is_a?(Hash)
+ source.play_graphic(Graphic.new(@graphic[:id], @graphic[:delay] || 0, @graphic[:height] | 0))
+ else
+ source.play_graphic(Graphic.new(@graphic))
+ end
+ end
+
+ 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
+ end
+end
+
+class ProcAttack < BaseAttack
+ def initialize(block, animation:, graphic:, range: 1, requirements: [])
+ super(animation, graphic, range, requirements)
+
+ @block = block
+ end
+
+ def apply(source, target)
+ self.instance_exec(source, target, &@block)
+ end
+end
+
+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)
+
+ end
+end
+
+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)
+ end
+end
diff --git a/data/plugins/combat/attack_requirements.rb b/data/plugins/combat/attack_requirements.rb
new file mode 100644
index 00000000..9c2fe1c6
--- /dev/null
+++ b/data/plugins/combat/attack_requirements.rb
@@ -0,0 +1,56 @@
+java_import 'org.apollo.cache.def.ItemDefinition'
+
+class AttackRequirementException < Exception
+ attr_reader :message
+
+ def initialize(message)
+ @message = message
+ end
+end
+
+class AttackRequirement
+ def validate!(player)
+ throw RuntimeError.new('validate! not implemented')
+ end
+
+ def apply(player)
+ throw RuntimeError.new('apply not implemented')
+ end
+end
+
+class SpecialEnergyRequirement < AttackRequirement
+ def initialize(amount)
+ @amount = amount
+ end
+
+ def validate!(player)
+ throw AttackRequirementException.new('Not enough special attack energy.') unless player.special_energy >= @amount
+ end
+
+ def apply(player)
+ player.special_energy = player.special_energy - @amount
+ end
+end
+
+class ItemRequirement < AttackRequirement
+ def initialize(item, amount)
+ @item = item
+ @amount = amount
+ end
+
+ def validate!(player)
+ throw AttackRequirementException.new(item_missing_message) unless player.inventory.get_amount(@item) >= @amount
+ end
+
+ def apply(player)
+ player.inventory.remove(@item, @amount)
+ end
+
+ private
+
+ def item_missing_message
+ definition = ItemDefinition.lookup(@item)
+
+ "You don't have enough #{lookup_item(@item).name}s"
+ end
+end
diff --git a/data/plugins/combat/attributes.rb b/data/plugins/combat/attributes.rb
new file mode 100644
index 00000000..2b0bcd07
--- /dev/null
+++ b/data/plugins/combat/attributes.rb
@@ -0,0 +1,29 @@
+module CombatModule
+ ##
+ # The delay a Mob must wait before attacking again.
+ declare_attribute(:attack_delay, 0)
+
+ ##
+ # A flag indicating whether this Mob is currently in combat.
+ declare_attribute(:attacking, false)
+
+ ##
+ # A flag indicating whether our Mob is dead.
+ declare_attribute(:dead, false)
+
+ ##
+ # The amount of ticks a Player must wait before logging out after combat.
+ declare_attribute(:logout_timer, Time.now.to_i)
+
+ ##
+ # The CombatStyle offset that a Mob is currently using.
+ 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)
+
+ ##
+ # An integer between 0 and 100 indicating the amount of special energy a Player has.
+ declare_attribute(:special_energy, 100, :persistent)
+end
\ No newline at end of file
diff --git a/data/plugins/combat/bonuses.rb b/data/plugins/combat/bonuses.rb
new file mode 100644
index 00000000..e13ee709
--- /dev/null
+++ b/data/plugins/combat/bonuses.rb
@@ -0,0 +1,44 @@
+module Combat
+ # A module for units which can have their own bonuses. E.G., weapons, equipment, ammo.
+ module BonusContainer
+ def attack_bonus(type)
+ @attack_bonuses[type]
+ end
+
+ def defence_bonus(type)
+ @defence_bonuses[type]
+ end
+
+ def other_bonus(type)
+ @other_bonuses[type]
+ end
+
+ def other_bonuses(melee_strength: 0, ranged_strength: 0, prayer: 0)
+ @other_bonuses = {
+ :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
+ }
+ 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
+ }
+ end
+ end
+end
\ No newline at end of file
diff --git a/data/plugins/combat/combat.rb b/data/plugins/combat/combat.rb
new file mode 100644
index 00000000..6cf9ead0
--- /dev/null
+++ b/data/plugins/combat/combat.rb
@@ -0,0 +1,12 @@
+on :message, :npc_action do |player, message|
+ player_combat_state = get_combat_state player
+ player_combat_state.target = $world.npc_repository.get message.index
+
+ unless player.attacking
+ player.start_action CombatAction.new(player)
+ end
+end
+
+on :message, :player_action do |player, message|
+
+end
\ No newline at end of file
diff --git a/data/plugins/combat/combat_action.rb b/data/plugins/combat/combat_action.rb
new file mode 100644
index 00000000..b886a8ce
--- /dev/null
+++ b/data/plugins/combat/combat_action.rb
@@ -0,0 +1,106 @@
+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
+
+class CombatAction < Action
+ def initialize(source, attack = nil)
+ super(0, true, source)
+
+ @combat_state = get_combat_state(source)
+ @attack = attack
+ end
+
+ def execute
+ if @combat_state.target.nil? and @combat_state.queued_attacks.empty?
+ @combat_state.reset
+ 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
+ if @combat_state.queued_attacks.empty? and @combat_state.supports_weapon
+ weapon = EquipmentUtil.equipped_weapon mob
+ weapon_class = weapon.weapon_class
+ combat_style = weapon_class.style_at mob.combat_style
+
+ if mob.attacking
+ set_delay weapon_class.speed(combat_style) - 1
+ else
+ set_delay 0
+ mob.attacking = true
+ end
+
+ if mob.using_special and weapon.special_attack?
+ @combat_state.next_attack = weapon.special_attack
+ else
+ @combat_state.next_attack = weapon_class.attack(combat_style)
+ end
+
+ @combat_state.state = :attacking
+ elsif @combat_state.queued_attacks.size > 0
+ @combat_state.next_attack = @combat_state.queued_attacks.pop
+ @combat_state.state = :attacking
+ else
+ stop
+ @combat_state.reset
+ end
+ end
+
+ def update_attacking
+ raise RuntimeError.new('no attack when in :attacking state') if @combat_state.next_attack.nil?
+
+ begin
+ @combat_state.next_attack.do(mob, @combat_state.target)
+
+ @combat_state.state = :idle
+ set_delay 0
+ rescue AttackRequirementException => e
+ mob.send_message e.message
+ end
+ end
+
+ def update_chasing
+
+ end
+
+ def stop
+ super
+
+ @combat_state.reset
+ mob.attacking = false
+
+ puts 'stopped combat action'
+ end
+end
\ No newline at end of file
diff --git a/data/plugins/combat/combat_state.rb b/data/plugins/combat/combat_state.rb
new file mode 100644
index 00000000..8bc0f025
--- /dev/null
+++ b/data/plugins/combat/combat_state.rb
@@ -0,0 +1,49 @@
+def get_combat_state(mob)
+ mob.is_a?(Player) ? type = :player : type = :npc
+
+ unless MOB_COMBAT_STATE_CACHE[type].has_key? mob.index
+ MOB_COMBAT_STATE_CACHE[type][mob.index] = CombatState.new(mob)
+ end
+
+ MOB_COMBAT_STATE_CACHE[type][mob.index]
+end
+
+private
+
+class CombatState
+ attr_accessor :state, :next_attack
+ attr_reader :queued_attacks, :supports_weapon
+
+ def initialize(mob, supports_weapon = true)
+ @mob = mob
+ @supports_weapon = supports_weapon
+ reset
+ end
+
+ def reset
+ @state = :idle
+ @target = nil
+ @next_attack = nil
+ @queued_attacks = []
+ @mob.reset_interacting_mob
+ end
+
+ def target
+ @target
+ end
+
+ def target=(target)
+ @mob.reset_interacting_mob
+ @mob.interacting_mob = target
+ @target = target
+ end
+
+ def queue_attack(attack)
+ @queued_attacks.push(attack)
+ end
+end
+
+MOB_COMBAT_STATE_CACHE = {
+ :player => {},
+ :npc => {}
+}
diff --git a/data/plugins/combat/equipment.rb b/data/plugins/combat/equipment.rb
new file mode 100644
index 00000000..cfec43f3
--- /dev/null
+++ b/data/plugins/combat/equipment.rb
@@ -0,0 +1,18 @@
+java_import 'org.apollo.cache.def.EquipmentDefinition'
+
+EQUIPMENT = {}
+
+def create_equipment(item, &block)
+ equipment = Equipment.new
+ equipment.instance_eval block
+
+ find_entities :item, item do |equipment_item|
+ EQUIPMENT[id] = equipment
+ end
+end
+
+private
+
+class Equipment
+ include Combat::BonusContainer
+end
\ No newline at end of file
diff --git a/data/plugins/combat/plugin.xml b/data/plugins/combat/plugin.xml
index b236b6b2..a05b0548 100644
--- a/data/plugins/combat/plugin.xml
+++ b/data/plugins/combat/plugin.xml
@@ -1,17 +1,36 @@
- combat
- 1
- Combat
- Manages combat between game characters.
-
- Ryley
-
-
-
-
-
- attributes
- areas
-
+ combat
+ 0.1
+ combat
+ Adds fully functioning melee, ranged and magic combat.
+
+ garyttierney
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ attributes
+ util
+
diff --git a/data/plugins/combat/projectile.rb b/data/plugins/combat/projectile.rb
new file mode 100644
index 00000000..d740e2d8
--- /dev/null
+++ b/data/plugins/combat/projectile.rb
@@ -0,0 +1,3 @@
+def create_projectile(item, drop_rate:, graphic:, projectile:)
+ items = i
+end
\ No newline at end of file
diff --git a/data/plugins/combat/util.rb b/data/plugins/combat/util.rb
new file mode 100644
index 00000000..d41caf17
--- /dev/null
+++ b/data/plugins/combat/util.rb
@@ -0,0 +1,70 @@
+java_import 'org.apollo.game.model.entity.EquipmentConstants'
+
+class CombatUtil
+
+ def self.calculate_max_hit(source)
+ strength = source.skill_set.get_skill(Skill::STRENGTH)
+ strength_stat = 5 #source.bonus_stat(:other, :strength)
+
+ effective_strength_damage = (strength.current_level) #* prayer_multiplier
+
+ if [:aggressive, :alt_aggressive].include? source.combat_style
+ effective_strength_damage += 3
+ end
+
+ (1.3 + (effective_strength_damage / 10) + (strength_stat / 80) +
+ ((effective_strength_damage * strength_stat) / 640))
+ end
+
+ 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
+
+ attack_stat = [1, 1].max
+ defence_stat = [1, 1].max
+
+ 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_accuracy = attack_stat * attack * attack_prayer_multiplier
+ attack_accuracy = 0.1 if attack_accuracy < 0
+
+ defence_prayer_multiplier = 1 #TODO: Prayer
+ defence_accuracy = defence_stat * defence * defence_prayer_multiplier
+ defence_accuracy = 1 if defence_accuracy < 0
+
+ base = attack_accuracy / defence_accuracy
+ base > 1 ? 0.9 : base * 0.9
+ end
+
+ # Calculates a hit for the given Mob and special attack flag.
+ def self.calculate_hit(source, target)
+ accuracy = calculate_accuracy source, target
+ max_hit = calculate_max_hit(source) + 1
+
+ if rand <= accuracy
+ return rand(max_hit)
+ else
+ return 0
+ end
+ end
+end
+
+class EquipmentUtil
+ def self.equipped_weapon(source)
+ item = source.equipment.get(EquipmentConstants::WEAPON)
+
+ if item.nil?
+ return NAMED_WEAPONS[:no_weapon]
+ end
+
+ WEAPONS[item.id]
+ end
+
+ def self.equipped_projectile(source)
+ item = source.equipment.get(EquipmentConstants::ARROWS)
+ end
+end
\ No newline at end of file
diff --git a/data/plugins/combat/weapon.rb b/data/plugins/combat/weapon.rb
new file mode 100644
index 00000000..428efe52
--- /dev/null
+++ b/data/plugins/combat/weapon.rb
@@ -0,0 +1,135 @@
+java_import 'org.apollo.cache.def.ItemDefinition'
+java_import 'org.apollo.game.model.inv.InventoryAdapter'
+java_import 'org.apollo.game.model.entity.EquipmentConstants'
+java_import 'org.apollo.game.model.entity.AnimationSet'
+java_import 'org.apollo.game.model.Animation'
+java_import 'org.apollo.game.model.inv.SynchronizationInventoryListener'
+
+WEAPONS = {}
+NAMED_WEAPONS = {}
+
+COMBAT_STYLES = [
+ :accurate,
+ :aggressive,
+ :defensive,
+ :controlled,
+ :alt_aggressive
+]
+
+def create_weapon(identifier, class_name = nil, named: false, &block)
+ if named
+ create_named_weapon(identifier, class_name, &block)
+ else
+ create_normal_weapon(identifier, class_name, &block)
+ end
+end
+
+
+private
+
+def create_normal_weapon(item_matcher, class_name = nil, &block)
+ items = find_entities :item, item_matcher.to_s.gsub(/_/, ' '), -1
+ items.each do |item_id|
+ 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
+ else
+ raise "Couldn't find a suitable weapon class for the given weapon."
+ end
+ end
+
+ WEAPONS[item_id] = Weapon.new(WEAPON_CLASSES[class_name])
+ WEAPONS[item_id].instance_eval &block
+ end
+end
+
+def create_named_weapon(name, class_name, &block)
+ NAMED_WEAPONS[name] = Weapon.new WEAPON_CLASSES[class_name]
+ NAMED_WEAPONS[name].instance_eval &block
+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
+ attr_reader :weapon_class, :special_attack
+
+ include Combat::BonusContainer
+
+ def initialize(weapon_class)
+ @weapon_class = weapon_class
+ @special_attack = nil
+ end
+
+ def special_attack?
+ not special_attack.nil?
+ end
+
+ def set_special_attack(energy_requirement:, animation:, graphic: nil, &block)
+
+ end
+end
+
+
+def update_weapon_animations(player)
+ default_animations = AnimationSet::DEFAULT_ANIMATION_SET
+ player_animations = player.animation_set
+
+ player_animations.stand = default_animations.stand
+ player_animations.walking = default_animations.walking
+ player_animations.running = default_animations.running
+ player_animations.idle_turn = default_animations.idle_turn
+ player_animations.turn_around = default_animations.turn_around
+ player_animations.turn_left = default_animations.turn_left
+ player_animations.turn_right = default_animations.turn_right
+
+ weapon = EquipmentUtil.equipped_weapon(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)
+ puts animation
+
+ puts 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
+ 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
+end
+
+on :login do |event|
+ update_weapon_animations(event.player)
+end
+
+on :message, :item_action do |player, message|
+ update_weapon_animations(player) if message.interface_id == SynchronizationInventoryListener::EQUIPMENT_ID and message.slot == EquipmentConstants::WEAPON
+end
diff --git a/data/plugins/combat/weapon_class.rb b/data/plugins/combat/weapon_class.rb
new file mode 100644
index 00000000..c427644a
--- /dev/null
+++ b/data/plugins/combat/weapon_class.rb
@@ -0,0 +1,119 @@
+COMBAT_STYLES = [
+ :accurate,
+ :aggressive,
+ :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
+]
+
+class WeaponClass
+ attr_reader :widget, :name
+
+ include Combat::BonusContainer
+
+ def initialize(name, widget)
+ @name = name
+ @widget = widget
+ @styles = {}
+ @style_attacks = {}
+ @style_offsets = []
+ @animations = {}
+ end
+
+ def add_style(style, attack_type: nil, speed: nil, animation: nil, block_animation: nil, range: 1)
+ raise 'Invalid combat style given' unless COMBAT_STYLES.include? style
+
+ @styles[style] = {
+ :attack_type => attack_type,
+ :speed => speed,
+ :animation => animation,
+ :block_animation => block_animation,
+ :range => range
+ }
+
+ @style_offsets.push style
+
+ if MELEE_COMBAT_STYLES.include? style
+ @style_attacks[style] = Attack.new(animation: animation)
+ end
+ end
+
+ def attack(style)
+ @style_attacks[style]
+ end
+
+ def attack_type(style)
+ @styles[style][:attack_type]
+ end
+
+ def block_animation(style)
+ @styles[style][:block_animation]
+ end
+
+ def other_animation(type)
+ @animations[type]
+ end
+
+ def speed(style)
+ @styles[style][:speed] || default_speed
+ end
+
+ def style_at(offset)
+ @style_offsets[offset]
+ end
+
+ def default_speed(speed = nil)
+ unless speed.nil?
+ @default_speed = speed
+ end
+
+ @default_speed
+ end
+
+ def special_bar(id = nil)
+ unless id.nil?
+ @special_bar = id
+ end
+
+ @special_bar
+ 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
+ }
+ end
+end
+
+WEAPON_CLASSES = {}
+WEAPON_CLASS_INTERFACE_MAP = {}
+
+def create_weapon_class(name, widget:, &block)
+ weapon_class = WeaponClass.new(name, widget)
+ weapon_class.instance_eval &block
+
+ WEAPON_CLASSES[name.to_sym] = weapon_class
+end
diff --git a/data/plugins/combat/weapons/bows.rb b/data/plugins/combat/weapons/bows.rb
new file mode 100644
index 00000000..b2f242ed
--- /dev/null
+++ b/data/plugins/combat/weapons/bows.rb
@@ -0,0 +1,20 @@
+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
new file mode 100644
index 00000000..e69de29b
diff --git a/data/plugins/combat/weapons/scimitars.rb b/data/plugins/combat/weapons/scimitars.rb
new file mode 100644
index 00000000..74e94692
--- /dev/null
+++ b/data/plugins/combat/weapons/scimitars.rb
@@ -0,0 +1,50 @@
+SCIMITAR_WIDGET_ID = 81
+SCIMITAR_SPECIAL_BAR_ID = 21
+
+create_weapon_class :scimitar, widget: SCIMITAR_WIDGET_ID do
+ default_speed 4
+ special_bar SCIMITAR_SPECIAL_BAR_ID
+
+ attack_bonuses crush: -2
+ defence_bonuses slash: -1
+
+ add_style :accurate, attack_type: :slash, animation: 390
+ add_style :aggressive, attack_type: :slash, animation: 390
+ add_style :alt_aggressive, attack_type: :stab, animation: 391
+ add_style :defensive, attack_type: :slash, animation: 390
+end
+
+create_weapon :iron_scimitar do
+ attack_bonuses stab: 2, slash: 10
+ other_bonuses melee_strength: 9
+end
+
+create_weapon :steel_scimitar do
+ attack_bonuses stab: 3, slash: 15
+ other_bonuses melee_strength: 14
+end
+
+create_weapon /(black|white) scimitar/ do
+ attack_bonuses stab: 4, slash: 19
+ other_bonuses melee_strength: 14
+end
+
+create_weapon :mithril_scimitar do
+ attack_bonuses stab: 5, slash: 21
+ other_bonuses melee_strength: 20
+end
+
+create_weapon :adamant_scimitar do
+ attack_bonuses stab: 6, slash: 29
+ other_bonuses melee_strength: 28
+end
+
+create_weapon :rune_scimitar do
+ attack_bonuses stab: 7, slash: 45
+ other_bonuses melee_strength: 44
+end
+
+create_weapon :dragon_scimitar do
+ attack_bonuses :stab => 8, slash: 67
+ other_bonuses melee_strength: 66
+end
diff --git a/data/plugins/combat/weapons/swords.rb b/data/plugins/combat/weapons/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/two_handed_swords.rb
new file mode 100644
index 00000000..a70efb1a
--- /dev/null
+++ b/data/plugins/combat/weapons/two_handed_swords.rb
@@ -0,0 +1,52 @@
+TWO_HANDED_SWORD_WIDGET_ID = 82
+TWO_HANDED_SWORD_SPECIAL_ID = 12
+
+create_weapon_class :two_handed_sword, widget: TWO_HANDED_SWORD_WIDGET_ID do
+ default_speed 7
+ special_bar TWO_HANDED_SWORD_SPECIAL_ID
+
+ animations stand: 7047, walk: 7046, run: 7039, idle_turn: 7044, turn_around: 7044, turn_left: 7043, turn_right: 7044
+
+ attack_bonuses stab: -4, magic: -4
+ defence_bonuses range: -1
+
+ add_style :accurate, attack_type: :slash, animation: 7041
+ add_style :aggressive, attack_type: :crush, animation: 7041
+ add_style :alt_aggressive, attack_type: :crush, animation: 7048
+ add_style :defensive, attack_type: :slash, animation: 7049
+end
+
+create_weapon :iron_2h_sword do
+ attack_bonuses slash: 13, crush: 10
+ other_bonuses melee_strength: 14
+end
+
+create_weapon :steel_2h_sword do
+ attack_bonuses slash: 21, crush: 16
+ other_bonuses melee_strength: 22
+end
+
+create_weapon /(?:black|white) 2h sword/ do
+ attack_bonuses slash: 27, crush: 21
+ other_bonuses melee_strength: 26
+end
+
+create_weapon :mithril_2h_sword do
+ attack_bonuses slash: 30, crush: 24
+ other_bonuses melee_strength: 26
+end
+
+create_weapon :adamant_2h_sword do
+ attack_bonuses slash: 43, crush: 30
+ other_bonuses melee_strength: 31
+end
+
+create_weapon :rune_2h_sword do
+ attack_bonuses slash: 69, crush: 50
+ other_bonuses melee_strength: 70
+end
+
+create_weapon :dragon_2h_sword do
+ attack_bonuses slash: 92, crush: 80
+ other_bonuses melee_strength: 70
+end
\ No newline at end of file
diff --git a/data/plugins/combat/weapons/unarmed.rb b/data/plugins/combat/weapons/unarmed.rb
new file mode 100644
index 00000000..db15a953
--- /dev/null
+++ b/data/plugins/combat/weapons/unarmed.rb
@@ -0,0 +1,11 @@
+create_weapon_class :unarmed, widget: -1 do
+ default_speed 4
+
+ add_style :accurate, animation: 422, block_animation: 424
+ add_style :aggressive, animation: 423, block_animation: 424
+ add_style :defensive, animation: 422, block_animation: 424
+end
+
+create_weapon :no_weapon, :unarmed, named: true do
+ # Todo factor out empty blocks
+end
\ No newline at end of file
diff --git a/data/plugins/combat/widgets/special_bar.rb b/data/plugins/combat/widgets/special_bar.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/data/plugins/combat/wilderness.rb b/data/plugins/combat/wilderness.rb
deleted file mode 100644
index e62989e4..00000000
--- a/data/plugins/combat/wilderness.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-require 'java'
-
-java_import 'org.apollo.game.model.entity.Player'
-java_import 'org.apollo.game.message.impl.SetWidgetTextMessage'
-java_import 'org.apollo.game.message.impl.OpenOverlayMessage'
-
-declare_attribute(:wilderness_level, 0, :transient)
-
-# Constants constants related to the wilderness
-module WildernessConstants
-
- # The wilderness level overlay interface id
- OVERLAY_INTERFACE_ID = 197
-
- # The wilderness level string id
- LEVEL_STRING_ID = 199
-
-end
-
-# Determines the wilderness level for the specified position
-def wilderness_level(position)
- ((position.y - 3520) / 8).ceil + 1
-end
-
-area_action :wilderness_level do
- on_entry do |player, position|
- player.wilderness_level = wilderness_level(position)
- player.interface_set.open_overlay(WildernessConstants::OVERLAY_INTERFACE_ID)
-
- id = WildernessConstants::LEVEL_STRING_ID
- player.send(SetWidgetTextMessage.new(id, "Level: #{player.wilderness_level}"))
- show_action(player, ATTACK_ACTION)
- end
-
- while_in do |player, position|
- current = player.wilderness_level
- updated = wilderness_level(position)
-
- if current != updated
- player.wilderness_level = updated
-
- id = WildernessConstants::LEVEL_STRING_ID
- player.send(SetWidgetTextMessage.new(id, "Level: #{player.wilderness_level}"))
- end
- end
-
- on_exit do |player, position|
- player.wilderness_level = 0
- player.interface_set.close
-
- player.send(OpenOverlayMessage.new(-1))
- hide_action(player, ATTACK_ACTION)
- end
-end
-
-# Monkey patch the existing player class to add method of checking whether or not a player is
-# within the wilderness
-class Player
-
- def in_wilderness
- wilderness_level > 0
- end
-
-end
-
-area name: :wilderness, coordinates: [2945, 3522, 3390, 3972, 0], actions: :wilderness_level