Module: Natural20::Weapons

Included in:
AttackAction, Firebolt, InventoryUI, MagicMissile, Navigation
Defined in:
lib/natural_20/utils/weapons.rb

Overview

reusable utility methods for weapon calculations

Instance Method Summary collapse

Instance Method Details

#compute_advantages_and_disadvantages(battle, source, target, weapon, source_pos: nil) ⇒ Array

Compute all advantages and disadvantages

Parameters:

Options Hash (weapon):

  • type (String)

Returns:

  • (Array)


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/natural_20/utils/weapons.rb', line 22

def compute_advantages_and_disadvantages(battle, source, target, weapon, source_pos: nil)
  weapon = battle.session.load_weapon(weapon) if weapon.is_a?(String) || weapon.is_a?(Symbol)
  advantage = []
  disadvantage = []

  disadvantage << :prone if source.prone?
  disadvantage << :squeezed if source.squeezed?
  disadvantage << :target_dodge if target.dodge?(battle)
  disadvantage << :armor_proficiency unless source.proficient_with_equipped_armor?
  advantage << :squeezed if target.squeezed?
  advantage << :being_helped if battle.help_with?(target)
  disadvantage << :target_long_range if battle.map && weapon && battle.map.distance(source, target,
                                                                                    entity_1_pos: source_pos) > weapon[:range]

  if weapon && weapon[:type] == 'ranged_attack' && battle.map
    disadvantage << :ranged_with_enemy_in_melee if battle.enemy_in_melee_range?(source, source_pos: source_pos)
    disadvantage << :target_is_prone_range if target.prone?
  end

  if source.class_feature?('pack_tactics') && battle.ally_within_enemey_melee_range?(source, target,
                                                                                     source_pos: source_pos)
    advantage << :pack_tactics
  end

  if weapon && weapon[:properties]&.include?('heavy') && source.size == :small
    disadvantage << :small_creature_using_heavy
  end
  advantage << :target_is_prone if weapon && weapon[:type] == 'melee_attack' && target.prone?

  advantage << :unseen_attacker if battle.map && !battle.can_see?(target, source, entity_2_pos: source_pos)
  disadvantage << :invisible_attacker if battle.map && !battle.can_see?(source, target, entity_1_pos: source_pos)
  [advantage, disadvantage]
end

#damage_modifier(entity, weapon, second_hand: false) ⇒ String

Calculates weapon damage roll

Parameters:

  • entity (Natural20::Entity)
  • weapon (Hash)

    weapon descriptor

  • second_hand (Boolean) (defaults to: false)

    Second hand to be used for two weapon fighting

Returns:

  • (String)


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/natural_20/utils/weapons.rb', line 61

def damage_modifier(entity, weapon, second_hand: false)
  damage_mod = entity.attack_ability_mod(weapon)

  damage_mod = [damage_mod, 0].min if second_hand && !entity.class_feature?('two_weapon_fighting')

  # compute damage roll using versatile weapon property
  damage_roll = if weapon[:properties]&.include?('versatile') && entity.used_hand_slots <= 1.0
                  weapon[:damage_2]
                else
                  weapon[:damage]
                end

  # duelist class feature
  if entity.class_feature?('dueling') && weapon[:type] == 'melee_attack' && entity.used_hand_slots(weapon_only: true) <= 1.0
    damage_mod += 2
  end

  "#{damage_roll}#{damage_mod >= 0 ? "+#{damage_mod}" : damage_mod}"
end

#target_advantage_condition(battle, source, target, weapon, source_pos: nil) ⇒ Object

Check all the factors that affect advantage/disadvantage in attack rolls



4
5
6
7
8
9
10
11
12
13
14
# File 'lib/natural_20/utils/weapons.rb', line 4

def target_advantage_condition(battle, source, target, weapon, source_pos: nil)
  advantages, disadvantages = compute_advantages_and_disadvantages(battle, source, target, weapon,
                                                                   source_pos: source_pos)

  return [0, [advantages, disadvantages]] if advantages.empty? && disadvantages.empty?
  return [0, [advantages, disadvantages]] if !advantages.empty? && !disadvantages.empty?

  return [1, [advantages, disadvantages]] unless advantages.empty?

  [-1, [advantages, disadvantages]]
end