mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-07 16:49:12 +00:00
Add initial combat spell support
This commit is contained in:
committed by
Gary Tierney
parent
9f47fae6a9
commit
ec248a185b
@@ -9,10 +9,10 @@ class BaseAttack
|
|||||||
fail 'Attack speed must be a non-negative number' if speed < 0
|
fail 'Attack speed must be a non-negative number' if speed < 0
|
||||||
fail 'Attack range must be a non-negative number' if range < 0
|
fail 'Attack range must be a non-negative number' if range < 0
|
||||||
|
|
||||||
@speed = speed
|
@speed = speed
|
||||||
@animation = animation
|
@animation = animation
|
||||||
@graphic = graphic
|
@graphic = graphic
|
||||||
@range = range
|
@range = range
|
||||||
@requirements = requirements
|
@requirements = requirements
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ end
|
|||||||
|
|
||||||
##
|
##
|
||||||
# A simple ranged attack, which sends a projectile based on the currently equipped ammo and weapon
|
# 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.
|
# and deals damage delayed by the speed and travel distance of the projectile.
|
||||||
|
|
||||||
class RangedAttack < BaseAttack
|
class RangedAttack < BaseAttack
|
||||||
def apply(source, target)
|
def apply(source, target)
|
||||||
@@ -57,6 +57,51 @@ class RangedAttack < BaseAttack
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# A simple magic attack, which sends a projectile based on the current spell that the player is casting and deals
|
||||||
|
# damage delayed by the speed and travel distance of the projectile.
|
||||||
|
|
||||||
|
class MagicAttack < BaseAttack
|
||||||
|
|
||||||
|
##
|
||||||
|
# The maximum distance that a magic attack can be performed from.
|
||||||
|
MAX_DISTANCE = 8
|
||||||
|
|
||||||
|
##
|
||||||
|
# The speed that magic attacks can be casted at.
|
||||||
|
SPEED = 5
|
||||||
|
|
||||||
|
def initialize(spell)
|
||||||
|
super(animation: spell.animation, graphic: spell.graphic, speed: SPEED, range: MAX_DISTANCE)
|
||||||
|
|
||||||
|
@damage = spell.damage
|
||||||
|
@hit_graphic = spell.hit_graphic
|
||||||
|
@projectile = spell.projectile
|
||||||
|
@projectile_type = PROJECTILE_TYPES[spell.projectile_type]
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply(source, target)
|
||||||
|
|
||||||
|
projectile!(source, target, @projectile, @projectile_type)
|
||||||
|
|
||||||
|
distance = source.position.get_distance(target.position)
|
||||||
|
damage_delay = (@projectile_type.delay + @projectile_type.speed + distance * 5) * 0.02857
|
||||||
|
|
||||||
|
schedule_damage!(source, target, rand(@damage), damage_delay) do
|
||||||
|
|
||||||
|
unless @hit_graphic.nil?
|
||||||
|
if @hit_graphic.is_a?(Hash)
|
||||||
|
target.play_graphic(Graphic.new(@hit_graphic[:id], @hit_graphic[:delay] || 0, @hit_graphic[:height] || 0))
|
||||||
|
else
|
||||||
|
target.play_graphic(Graphic.new(@hit_graphic))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# A basic melee attack, which deals damage a tick after the attack was made.
|
# A basic melee attack, which deals damage a tick after the attack was made.
|
||||||
|
|
||||||
@@ -74,7 +119,7 @@ class AttackDSL
|
|||||||
|
|
||||||
def initialize()
|
def initialize()
|
||||||
@requirements = []
|
@requirements = []
|
||||||
@subattacks = []
|
@subattacks = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_requirement(requirement)
|
def add_requirement(requirement)
|
||||||
@@ -95,7 +140,7 @@ class AttackDSL
|
|||||||
|
|
||||||
def damage!(damage_modifier: 1, delay: 0, secondary: false)
|
def damage!(damage_modifier: 1, delay: 0, secondary: false)
|
||||||
@subattacks.push lambda { |source, target|
|
@subattacks.push lambda { |source, target|
|
||||||
do_damage! source, target, 1, delay, secondary
|
schedule_damage! source, target, 1, delay, secondary
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -118,29 +163,37 @@ end
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
##
|
def schedule_damage!(source, target, amount, delay, secondary = false, &callback)
|
||||||
# TODO: refactor
|
|
||||||
def do_damage!(source, target, amount, delay = 0, secondary = false, &_block)
|
|
||||||
schedule delay do |task|
|
schedule delay do |task|
|
||||||
task.stop && return if source.dead || target.dead
|
|
||||||
|
|
||||||
target_combat_state = target.get_combat_state
|
do_damage!(source, target, amount, secondary)
|
||||||
target_hitpoints = target.skill_set.get_skill(Skill::HITPOINTS).get_current_level
|
callback.call if block_given?
|
||||||
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
|
task.stop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def do_damage!(source, target, amount, secondary = false)
|
||||||
|
return if source.dead || target.dead
|
||||||
|
|
||||||
|
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.damage(amount, type, secondary)
|
||||||
|
|
||||||
|
auto_retaliate!(source, target)
|
||||||
|
end
|
||||||
|
|
||||||
|
def auto_retaliate!(source, target)
|
||||||
|
target_combat_state = target.get_combat_state
|
||||||
|
|
||||||
|
if target.auto_retaliate && !target_combat_state.is_attacking?
|
||||||
|
target_combat_state.target = source
|
||||||
|
target.start_action(CombatAction.new(target))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# TODO: refactor
|
# TODO: refactor
|
||||||
|
|
||||||
@@ -152,13 +205,13 @@ def do_ranged_damage!(source, target, _damage_modifier = 1, speed_modifier = 1,
|
|||||||
ammo = EquipmentUtil.equipped_ammo source
|
ammo = EquipmentUtil.equipped_ammo source
|
||||||
|
|
||||||
if projectile.nil? && projectile_type.nil?
|
if projectile.nil? && projectile_type.nil?
|
||||||
projectile = ammo.projectile
|
projectile = ammo.projectile
|
||||||
projectile_type = ammo.projectile_type
|
projectile_type = ammo.projectile_type
|
||||||
projectile_graphic = ammo.graphic
|
projectile_graphic = ammo.graphic
|
||||||
end
|
end
|
||||||
|
|
||||||
projectile! source, target, projectile, projectile_type, speed_modifier
|
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
|
schedule_damage! source, target, rand(1...50), (projectile_type.delay + projectile_type.speed + distance * 5) * 0.02857, secondary
|
||||||
|
|
||||||
unless projectile_graphic.nil?
|
unless projectile_graphic.nil?
|
||||||
source.play_graphic Graphic.new(projectile_graphic, 0, 100)
|
source.play_graphic Graphic.new(projectile_graphic, 0, 100)
|
||||||
@@ -183,13 +236,13 @@ def projectile!(source, target, projectile_id, projectile_type, speed_modifier =
|
|||||||
distance = source.position.get_distance(target.position)
|
distance = source.position.get_distance(target.position)
|
||||||
|
|
||||||
projectile_parameters = {
|
projectile_parameters = {
|
||||||
angle: -1,
|
angle: -1,
|
||||||
start_height: projectile_type.start_height,
|
start_height: projectile_type.start_height,
|
||||||
end_height: projectile_type.end_height,
|
end_height: projectile_type.end_height,
|
||||||
delay: projectile_type.delay * (2 - speed_modifier),
|
delay: projectile_type.delay * (2 - speed_modifier),
|
||||||
speed: (projectile_type.delay + projectile_type.speed + distance * 5) * speed_modifier,
|
speed: (projectile_type.delay + projectile_type.speed + distance * 5) * speed_modifier,
|
||||||
slope: projectile_type.slope,
|
slope: projectile_type.slope,
|
||||||
radius: projectile_type.radius
|
radius: projectile_type.radius
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectileModule.fire_projectile(source.position, target.position, projectile_id, projectile_parameters, target)
|
ProjectileModule.fire_projectile(source.position, target.position, projectile_id, projectile_parameters, target)
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ declare_attribute(:logout_timer, Time.now.to_i)
|
|||||||
# The <i>CombatStyle</i> offset that a <i>Mob</i> is currently using.
|
# The <i>CombatStyle</i> offset that a <i>Mob</i> is currently using.
|
||||||
declare_attribute(:combat_style, 0, :persistent)
|
declare_attribute(:combat_style, 0, :persistent)
|
||||||
|
|
||||||
|
##
|
||||||
|
# The <i>CombatSpell</i> offset that a <i>Mob</i> is currently using.
|
||||||
|
declare_attribute(:combat_spell, :none, :persistent)
|
||||||
|
|
||||||
##
|
##
|
||||||
# A flag indicating whether the special bar is flagged for the next attack.
|
# A flag indicating whether the special bar is flagged for the next attack.
|
||||||
declare_attribute(:using_special, false, :persistent)
|
declare_attribute(:using_special, false, :persistent)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
java_import 'org.apollo.game.message.impl.HintIconMessage'
|
java_import 'org.apollo.game.message.impl.HintIconMessage'
|
||||||
|
|
||||||
on :message, :npc_action do |player, message|
|
on :message, :npc_action do |player, message|
|
||||||
target = $world.npc_repository.get message.index
|
target = $world.npc_repository.get message.index
|
||||||
|
|
||||||
# unless target.attacking
|
# unless target.attacking
|
||||||
# target_combat_state = get_combat_state target
|
# target_combat_state = get_combat_state target
|
||||||
@@ -10,12 +10,28 @@ on :message, :npc_action do |player, message|
|
|||||||
# target.start_action CombatAction.new(target)
|
# target.start_action CombatAction.new(target)
|
||||||
# end
|
# end
|
||||||
|
|
||||||
player_combat_state = player.get_combat_state
|
player_combat_state = player.get_combat_state
|
||||||
player_combat_state.target = target
|
player_combat_state.target = target
|
||||||
|
|
||||||
player.send HintIconMessage.for_npc(target.index)
|
player.send HintIconMessage.for_npc(target.index)
|
||||||
player.walking_queue.clear
|
player.walking_queue.clear
|
||||||
player.start_action CombatAction.new(player) unless player_combat_state.is_attacking?
|
player.start_action CombatAction.new(player)
|
||||||
|
end
|
||||||
|
|
||||||
|
on :message, :magic_on_mob do |player, message|
|
||||||
|
target = $world.npc_repository.get(message.index)
|
||||||
|
|
||||||
|
player_combat_state = player.get_combat_state
|
||||||
|
player_combat_state.target = target
|
||||||
|
|
||||||
|
spellbook = spellbook_for(message.interface_id)
|
||||||
|
spell = spell_for(spellbook, message.spell_id)
|
||||||
|
|
||||||
|
player.walking_queue.clear
|
||||||
|
player.start_action CombatAction.new(player)
|
||||||
|
|
||||||
|
magic_attack = MagicAttack.new(spell)
|
||||||
|
player_combat_state.queue_attack(magic_attack)
|
||||||
end
|
end
|
||||||
|
|
||||||
on :message, :player_action do |player, message|
|
on :message, :player_action do |player, message|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class CombatAction < Action
|
|||||||
current_distance = mob.position.get_distance target.position
|
current_distance = mob.position.get_distance target.position
|
||||||
attack_collision_type = next_attack.range > 1 ? EntityType::PROJECTILE : EntityType::NPC
|
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)
|
in_range = current_distance <= next_attack.range# && !$world.intersects(mob.position, target.position, attack_collision_type)
|
||||||
|
|
||||||
unless in_range
|
unless in_range
|
||||||
mob.follow @combat_state.target, next_attack.range
|
mob.follow @combat_state.target, next_attack.range
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
class CombatSpell
|
||||||
|
|
||||||
|
attr_reader :button
|
||||||
|
|
||||||
|
attr_reader :spellbook
|
||||||
|
|
||||||
|
attr_reader :level
|
||||||
|
|
||||||
|
attr_reader :damage
|
||||||
|
|
||||||
|
attr_reader :runes
|
||||||
|
|
||||||
|
attr_reader :animation
|
||||||
|
|
||||||
|
attr_reader :graphic
|
||||||
|
|
||||||
|
attr_reader :hit_graphic
|
||||||
|
|
||||||
|
attr_reader :projectile
|
||||||
|
|
||||||
|
attr_reader :projectile_type
|
||||||
|
|
||||||
|
def initialize(button, spellbook, level, damage, runes, animation, graphic, hit_graphic, projectile, projectile_type)
|
||||||
|
@spellbook = spellbook
|
||||||
|
@button = button
|
||||||
|
@level = level
|
||||||
|
@damage = damage
|
||||||
|
@runes = runes
|
||||||
|
@animation = animation
|
||||||
|
@graphic = graphic
|
||||||
|
@hit_graphic = hit_graphic
|
||||||
|
@projectile = projectile
|
||||||
|
@projectile_type = projectile_type
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class CombatSpellDSL
|
||||||
|
|
||||||
|
def initialize(&block)
|
||||||
|
instance_eval(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def interface(spellbook:, button:)
|
||||||
|
@spellbook = spellbook
|
||||||
|
@button = button
|
||||||
|
end
|
||||||
|
|
||||||
|
def effects(animation: nil, graphic: nil, hit_graphic: nil)
|
||||||
|
@animation = animation
|
||||||
|
@graphic = graphic
|
||||||
|
@hit_graphic = hit_graphic
|
||||||
|
end
|
||||||
|
|
||||||
|
def projectile(projectile:, projectile_type:)
|
||||||
|
@projectile = projectile
|
||||||
|
@projectile_type = projectile_type
|
||||||
|
end
|
||||||
|
|
||||||
|
def level_requirement(level)
|
||||||
|
@level = level
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_damage(damage)
|
||||||
|
@damage = damage
|
||||||
|
end
|
||||||
|
|
||||||
|
def runes(runes = {})
|
||||||
|
@runes = runes
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_combat_spell
|
||||||
|
return CombatSpell.new(@button, @spellbook, @level, @damage, @runes, @animation, @graphic, @hit_graphic, @projectile, @projectile_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
COMBAT_SPELLS = {}
|
||||||
|
|
||||||
|
def create_combat_spell(name, &block)
|
||||||
|
fail 'Block not given' unless block_given?
|
||||||
|
|
||||||
|
combat_spell_dsl = CombatSpellDSL.new(&block)
|
||||||
|
|
||||||
|
COMBAT_SPELLS[name] = combat_spell_dsl.to_combat_spell
|
||||||
|
end
|
||||||
|
|
||||||
|
def spell_for(spellbook, button)
|
||||||
|
|
||||||
|
COMBAT_SPELLS.each do |name, spell|
|
||||||
|
return spell if spell.spellbook == spellbook && spell.button == button
|
||||||
|
end
|
||||||
|
|
||||||
|
fail "Unable to find a spell in spellbook '#{spellbook}' with button id '#{button}'"
|
||||||
|
end
|
||||||
@@ -23,6 +23,13 @@
|
|||||||
<script>equipment.rb</script>
|
<script>equipment.rb</script>
|
||||||
<script>util.rb</script>
|
<script>util.rb</script>
|
||||||
<script>projectile_type.rb</script>
|
<script>projectile_type.rb</script>
|
||||||
|
|
||||||
|
<script>spellbook.rb</script>
|
||||||
|
<script>spellbooks.rb</script>
|
||||||
|
|
||||||
|
<script>combat_spell.rb</script>
|
||||||
|
<script>spells/bolts.rb</script>
|
||||||
|
|
||||||
<script>weapon.rb</script>
|
<script>weapon.rb</script>
|
||||||
<script>weapon_class.rb</script>
|
<script>weapon_class.rb</script>
|
||||||
<script>weapons/bows.rb</script>
|
<script>weapons/bows.rb</script>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
SPELLBOOKS = {}
|
||||||
|
|
||||||
|
def create_spellbook(identifier, interface_id:)
|
||||||
|
SPELLBOOKS[interface_id] = identifier
|
||||||
|
end
|
||||||
|
|
||||||
|
def spellbook_for(interface_id)
|
||||||
|
fail "Could not find spellbook for #{interface_id}" unless SPELLBOOKS.has_key?(interface_id)
|
||||||
|
|
||||||
|
return SPELLBOOKS[interface_id]
|
||||||
|
end
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
create_spellbook :modern, interface_id: 192
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
create_combat_spell :wind_bolt do
|
||||||
|
interface spellbook: :modern, button: 10
|
||||||
|
|
||||||
|
level_requirement 17
|
||||||
|
max_damage 8
|
||||||
|
runes {}
|
||||||
|
|
||||||
|
effects animation: 1162, graphic: {id: 117, height: 100}, hit_graphic: {id: 119, delay: 100}
|
||||||
|
projectile projectile: 118, projectile_type: :bolt_spells
|
||||||
|
end
|
||||||
|
|
||||||
|
create_projectile_type :bolt_spells, start_height: 46, end_height: 36, delay: 51, speed: 12, slope: 15, radius: 86
|
||||||
Reference in New Issue
Block a user