natural_20 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +13 -0
- data/README.md +1 -0
- data/bin/nat20 +2 -1
- data/bin/nat20.cmd +0 -0
- data/char_classes/wizard.yml +89 -0
- data/characters/high_elf_mage.yml +27 -0
- data/fixtures/battle_sim_objects.yml +2 -2
- data/fixtures/high_elf_mage.yml +28 -0
- data/fixtures/large_map.yml +63 -0
- data/game.yml +2 -2
- data/items/equipment.yml +30 -0
- data/items/objects.yml +33 -29
- data/items/spells.yml +58 -0
- data/items/weapons.yml +78 -18
- data/lib/CHANGELOG.md +0 -0
- data/lib/natural_20.rb +9 -0
- data/lib/natural_20/actions/action.rb +2 -2
- data/lib/natural_20/actions/attack_action.rb +76 -67
- data/lib/natural_20/actions/concerns/action_damage.rb +3 -1
- data/lib/natural_20/actions/dash_action.rb +7 -10
- data/lib/natural_20/actions/disengage_action.rb +11 -12
- data/lib/natural_20/actions/dodge_action.rb +7 -8
- data/lib/natural_20/actions/escape_grapple_action.rb +16 -18
- data/lib/natural_20/actions/first_aid_action.rb +14 -16
- data/lib/natural_20/actions/grapple_action.rb +24 -28
- data/lib/natural_20/actions/ground_interact_action.rb +1 -3
- data/lib/natural_20/actions/help_action.rb +13 -16
- data/lib/natural_20/actions/hide_action.rb +7 -9
- data/lib/natural_20/actions/interact_action.rb +12 -14
- data/lib/natural_20/actions/look_action.rb +14 -15
- data/lib/natural_20/actions/move_action.rb +9 -9
- data/lib/natural_20/actions/multiattack_action.rb +8 -9
- data/lib/natural_20/actions/prone_action.rb +4 -6
- data/lib/natural_20/actions/short_rest_action.rb +7 -8
- data/lib/natural_20/actions/shove_action.rb +20 -24
- data/lib/natural_20/actions/spell_action.rb +89 -0
- data/lib/natural_20/actions/stand_action.rb +5 -7
- data/lib/natural_20/actions/use_item_action.rb +7 -9
- data/lib/natural_20/ai_controller/standard.rb +1 -1
- data/lib/natural_20/battle.rb +8 -3
- data/lib/natural_20/cli/action_ui.rb +180 -0
- data/lib/natural_20/cli/builder/fighter_builder.rb +1 -1
- data/lib/natural_20/cli/builder/rogue_builder.rb +10 -10
- data/lib/natural_20/cli/builder/wizard_builder.rb +77 -0
- data/lib/natural_20/cli/character_builder.rb +9 -4
- data/lib/natural_20/cli/commandline_ui.rb +55 -162
- data/lib/natural_20/cli/inventory_ui.rb +4 -0
- data/lib/natural_20/cli/map_renderer.rb +7 -1
- data/lib/natural_20/concerns/attack_helper.rb +53 -0
- data/lib/natural_20/concerns/entity.rb +170 -11
- data/lib/natural_20/concerns/fighter_actions/second_wind_action.rb +7 -9
- data/lib/natural_20/concerns/fighter_class.rb +2 -2
- data/lib/natural_20/concerns/spell_attack_helper.rb +33 -0
- data/lib/natural_20/concerns/wizard_class.rb +86 -0
- data/lib/natural_20/die_roll.rb +2 -2
- data/lib/natural_20/event_manager.rb +50 -44
- data/lib/natural_20/item_library/base_item.rb +1 -1
- data/lib/natural_20/npc.rb +4 -0
- data/lib/natural_20/player_character.rb +75 -12
- data/lib/natural_20/session.rb +14 -1
- data/lib/natural_20/spell_library/firebolt.rb +72 -0
- data/lib/natural_20/spell_library/mage_armor.rb +67 -0
- data/lib/natural_20/spell_library/mage_hand.rb +2 -0
- data/lib/natural_20/spell_library/magic_missile.rb +67 -0
- data/lib/natural_20/spell_library/shield.rb +69 -0
- data/lib/natural_20/spell_library/spell.rb +31 -0
- data/lib/natural_20/utils/weapons.rb +8 -6
- data/lib/natural_20/version.rb +1 -1
- data/locales/en.yml +44 -8
- data/maps/game_map.yml +12 -2
- metadata +22 -3
data/items/spells.yml
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
---
|
2
|
+
firebolt:
|
3
|
+
base_damage: 1d10
|
4
|
+
casting_time: 1:action
|
5
|
+
components:
|
6
|
+
- verbal
|
7
|
+
- sematic
|
8
|
+
damage_increase:
|
9
|
+
- 5
|
10
|
+
- 11
|
11
|
+
- 17
|
12
|
+
damage_type: fire
|
13
|
+
description: You hurl a mote of fire at a creature or object within range. A flammable object hit by this spell ignites if it isn't being worn or carried.
|
14
|
+
duration: instant
|
15
|
+
level: 0
|
16
|
+
name: Firebolt
|
17
|
+
range: 120
|
18
|
+
school: evocation
|
19
|
+
spell_class: Natural20::Firebolt
|
20
|
+
type: ranged_attack
|
21
|
+
mage_armor:
|
22
|
+
casting_time: 1:action
|
23
|
+
components:
|
24
|
+
- verbal
|
25
|
+
- sematic
|
26
|
+
- material
|
27
|
+
description: You touch a willing creature who isn't wearing armor, and a protective magical force surrounds it until the spell ends. The target's base AC becomes 13 + its Dexterity modifier. The spell ends if the target dons armor or if you dismiss the spell as an action.
|
28
|
+
duration: 8h
|
29
|
+
level: 1
|
30
|
+
materials:
|
31
|
+
- cured_leather
|
32
|
+
name: Mage Armor
|
33
|
+
school: abjuration
|
34
|
+
spell_class: Natural20::MageArmor
|
35
|
+
type: buff
|
36
|
+
magic_missile:
|
37
|
+
casting_time: 1:action
|
38
|
+
damage_type: force
|
39
|
+
level: 1
|
40
|
+
name: Magic Missile
|
41
|
+
range: 120
|
42
|
+
school: evocation
|
43
|
+
description: You create three glowing darts of magical force. Each dart hits a creature of your choice that you can see within range.
|
44
|
+
spell_class: Natural20::MagicMissile
|
45
|
+
type: force
|
46
|
+
higher_level: true
|
47
|
+
shield:
|
48
|
+
casting_time: 1:reaction
|
49
|
+
level: 1
|
50
|
+
name: Shield
|
51
|
+
range: 0
|
52
|
+
duration: 1R
|
53
|
+
school: abjuration
|
54
|
+
spell_class: Natural20::Shield
|
55
|
+
components:
|
56
|
+
- verbal
|
57
|
+
- sematic
|
58
|
+
type: warding
|
data/items/weapons.yml
CHANGED
@@ -39,6 +39,66 @@ dagger:
|
|
39
39
|
range_max: 120
|
40
40
|
type: melee_attack
|
41
41
|
weight: 10
|
42
|
+
quarterstaff:
|
43
|
+
cost: 2sp
|
44
|
+
damage: 1d6
|
45
|
+
damage_2: 1d8
|
46
|
+
damage_type: bludgeoning
|
47
|
+
meta:
|
48
|
+
noise_source: 5
|
49
|
+
noise_target: 5
|
50
|
+
modifiers: null
|
51
|
+
name: Quarterstaff
|
52
|
+
proficiency_type:
|
53
|
+
- simple
|
54
|
+
properties:
|
55
|
+
- versatile
|
56
|
+
range: 5
|
57
|
+
subtype: weapon
|
58
|
+
type: melee_attack
|
59
|
+
weight: 4
|
60
|
+
sling:
|
61
|
+
ammo: pellet
|
62
|
+
cost: 1sp
|
63
|
+
damage: 1d4
|
64
|
+
damage_type: bludgeoning
|
65
|
+
meta:
|
66
|
+
noise_source: 5
|
67
|
+
noise_target: 5
|
68
|
+
modifiers: null
|
69
|
+
name: Sling
|
70
|
+
proficiency_type:
|
71
|
+
- simple
|
72
|
+
properties:
|
73
|
+
- ammunition
|
74
|
+
- ranged
|
75
|
+
range: 30
|
76
|
+
range_max: 120
|
77
|
+
subtype: weapon
|
78
|
+
type: ranged_attack
|
79
|
+
weight: 0
|
80
|
+
dart:
|
81
|
+
ammo: dart
|
82
|
+
cost: 2
|
83
|
+
damage: 1d4
|
84
|
+
damage_type: piercing
|
85
|
+
meta:
|
86
|
+
noise_source: 5
|
87
|
+
noise_target: 5
|
88
|
+
modifiers: null
|
89
|
+
name: Dart
|
90
|
+
proficiency_type:
|
91
|
+
- simple
|
92
|
+
properties:
|
93
|
+
- thrown
|
94
|
+
- finesse
|
95
|
+
range: 5
|
96
|
+
subtype: weapon
|
97
|
+
thrown:
|
98
|
+
range: 20
|
99
|
+
range_max: 60
|
100
|
+
type: ranged_attack
|
101
|
+
weight: 0.25
|
42
102
|
greatclub:
|
43
103
|
cost: 2sp
|
44
104
|
damage: 1d8
|
@@ -162,24 +222,6 @@ light_hammer:
|
|
162
222
|
range_max: 60
|
163
223
|
type: melee_attack
|
164
224
|
weight: 2
|
165
|
-
warhammer:
|
166
|
-
cost: 2
|
167
|
-
damage: 1d8
|
168
|
-
damage_2: 1d10
|
169
|
-
damage_type: bludgeoning
|
170
|
-
meta:
|
171
|
-
noise_source: 5
|
172
|
-
noise_target: 5
|
173
|
-
modifiers: null
|
174
|
-
name: Warhammer
|
175
|
-
proficiency_type:
|
176
|
-
- martial
|
177
|
-
properties:
|
178
|
-
- versatile
|
179
|
-
range: 5
|
180
|
-
subtype: weapon
|
181
|
-
type: melee_attack
|
182
|
-
weight: 2
|
183
225
|
longbow:
|
184
226
|
ammo: arrows
|
185
227
|
cost: 50
|
@@ -351,4 +393,22 @@ vicious_rapier:
|
|
351
393
|
rarity: very_rare
|
352
394
|
subtype: weapon
|
353
395
|
type: melee_attack
|
396
|
+
weight: 2
|
397
|
+
warhammer:
|
398
|
+
cost: 2
|
399
|
+
damage: 1d8
|
400
|
+
damage_2: 1d10
|
401
|
+
damage_type: bludgeoning
|
402
|
+
meta:
|
403
|
+
noise_source: 5
|
404
|
+
noise_target: 5
|
405
|
+
modifiers: null
|
406
|
+
name: Warhammer
|
407
|
+
proficiency_type:
|
408
|
+
- martial
|
409
|
+
properties:
|
410
|
+
- versatile
|
411
|
+
range: 5
|
412
|
+
subtype: weapon
|
413
|
+
type: melee_attack
|
354
414
|
weight: 2
|
data/lib/CHANGELOG.md
ADDED
File without changes
|
data/lib/natural_20.rb
CHANGED
@@ -9,6 +9,7 @@ require 'natural_20/concerns/lootable'
|
|
9
9
|
require 'natural_20/concerns/evaluator/entity_state_evaluator'
|
10
10
|
require 'natural_20/concerns/entity'
|
11
11
|
require 'natural_20/item_library/object'
|
12
|
+
require 'natural_20/concerns/attack_helper'
|
12
13
|
require 'natural_20/concerns/movement_helper'
|
13
14
|
require 'natural_20/utils/cover'
|
14
15
|
require 'natural_20/utils/weapons'
|
@@ -16,6 +17,7 @@ require 'natural_20/concerns/navigation'
|
|
16
17
|
require 'natural_20/actions/action'
|
17
18
|
require 'natural_20/concerns/fighter_class'
|
18
19
|
require 'natural_20/concerns/rogue_class'
|
20
|
+
require 'natural_20/concerns/wizard_class'
|
19
21
|
require 'natural_20/actions/concerns/action_damage'
|
20
22
|
require 'natural_20/actions/look_action'
|
21
23
|
require 'natural_20/actions/attack_action'
|
@@ -37,6 +39,13 @@ require 'natural_20/actions/escape_grapple_action'
|
|
37
39
|
require 'natural_20/actions/ground_interact_action'
|
38
40
|
require 'natural_20/actions/first_aid_action'
|
39
41
|
require 'natural_20/actions/shove_action'
|
42
|
+
require 'natural_20/concerns/spell_attack_helper'
|
43
|
+
require 'natural_20/spell_library/spell'
|
44
|
+
require 'natural_20/spell_library/mage_armor'
|
45
|
+
require 'natural_20/spell_library/firebolt'
|
46
|
+
require 'natural_20/spell_library/magic_missile'
|
47
|
+
require 'natural_20/spell_library/shield'
|
48
|
+
require 'natural_20/actions/spell_action'
|
40
49
|
require 'natural_20/battle'
|
41
50
|
require 'natural_20/utils/ray_tracer'
|
42
51
|
require 'natural_20/battle_map'
|
@@ -27,14 +27,14 @@ module Natural20
|
|
27
27
|
def validate
|
28
28
|
end
|
29
29
|
|
30
|
-
def apply!(battle); end
|
30
|
+
def self.apply!(battle, item); end
|
31
31
|
|
32
32
|
def resolve(session, map, opts = {}); end
|
33
33
|
|
34
34
|
protected
|
35
35
|
|
36
36
|
def t(k, options = {})
|
37
|
-
I18n.t(k, options)
|
37
|
+
I18n.t(k, **options)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -2,7 +2,8 @@
|
|
2
2
|
class AttackAction < Natural20::Action
|
3
3
|
include Natural20::Cover
|
4
4
|
include Natural20::Weapons
|
5
|
-
include Natural20::
|
5
|
+
include Natural20::AttackHelper
|
6
|
+
extend Natural20::ActionDamage
|
6
7
|
|
7
8
|
attr_accessor :target, :using, :npc_action, :as_reaction, :thrown, :second_hand
|
8
9
|
attr_reader :advantage_mod
|
@@ -31,7 +32,7 @@ class AttackAction < Natural20::Action
|
|
31
32
|
|
32
33
|
i18n_token = thrown ? 'action.attack_action_throw' : 'action.attack_action'
|
33
34
|
|
34
|
-
t(i18n_token, name: @action_type.to_s.humanize, weapon_name: weapon[:name], mod: attack_mod,
|
35
|
+
t(i18n_token, name: @action_type.to_s.humanize, weapon_name: weapon[:name], mod: attack_mod >= 0 ? "+#{attack_mod}" : attack_mod,
|
35
36
|
dmg: damage_modifier(@source, weapon, second_hand: second_hand))
|
36
37
|
end
|
37
38
|
end
|
@@ -70,76 +71,82 @@ class AttackAction < Natural20::Action
|
|
70
71
|
end
|
71
72
|
|
72
73
|
# @param battle [Natural20::Battle]
|
73
|
-
def apply!(battle)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
item[:source].deduct_item(item[:ammo], 1) if item[:ammo]
|
95
|
-
|
96
|
-
# hanle thrown items
|
97
|
-
if item[:thrown]
|
98
|
-
if item[:source].item_count(item[:weapon]).positive?
|
99
|
-
item[:source].deduct_item(item[:weapon], 1)
|
100
|
-
else
|
101
|
-
item[:source].unequip(item[:weapon], transfer_inventory: false)
|
102
|
-
end
|
74
|
+
def self.apply!(battle, item)
|
75
|
+
if item[:flavor]
|
76
|
+
Natural20::EventManager.received_event({ event: :flavor, source: item[:source], target: item[:target],
|
77
|
+
text: item[:flavor] })
|
78
|
+
end
|
79
|
+
case (item[:type])
|
80
|
+
when :prone
|
81
|
+
item[:source].prone!
|
82
|
+
when :damage
|
83
|
+
damage_event(item, battle)
|
84
|
+
consume_resource(battle, item)
|
85
|
+
when :miss
|
86
|
+
consume_resource(battle, item)
|
87
|
+
Natural20::EventManager.received_event({ attack_roll: item[:attack_roll],
|
88
|
+
attack_name: item[:attack_name],
|
89
|
+
advantage_mod: item[:advantage_mod],
|
90
|
+
as_reaction: !!item[:as_reaction],
|
91
|
+
adv_info: item[:adv_info],
|
92
|
+
source: item[:source], target: item[:target], event: :miss })
|
93
|
+
end
|
94
|
+
end
|
103
95
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
96
|
+
# @param battle [Natural20::Battle]
|
97
|
+
# @param item [Hash]
|
98
|
+
def self.consume_resource(battle, item)
|
99
|
+
# handle ammo
|
100
|
+
item[:source].deduct_item(item[:ammo], 1) if item[:ammo]
|
101
|
+
|
102
|
+
# hanle thrown items
|
103
|
+
if item[:thrown]
|
104
|
+
if item[:source].item_count(item[:weapon]).positive?
|
105
|
+
item[:source].deduct_item(item[:weapon], 1)
|
106
|
+
else
|
107
|
+
item[:source].unequip(item[:weapon], transfer_inventory: false)
|
111
108
|
end
|
112
109
|
|
113
|
-
if
|
114
|
-
|
115
|
-
elsif item[:second_hand]
|
116
|
-
battle.consume(item[:source], :bonus_action)
|
110
|
+
if item[:type] == :damage
|
111
|
+
item[:target].add_item(item[:weapon])
|
117
112
|
else
|
118
|
-
battle.
|
113
|
+
ground_pos = item[:battle].map.entity_or_object_pos(item[:target])
|
114
|
+
ground_object = item[:battle].map.objects_at(*ground_pos).detect { |o| o.is_a?(ItemLibrary::Ground) }
|
115
|
+
ground_object&.add_item(item[:weapon])
|
119
116
|
end
|
117
|
+
end
|
120
118
|
|
121
|
-
|
119
|
+
if item[:as_reaction]
|
120
|
+
battle.consume(item[:source], :reaction)
|
121
|
+
elsif item[:second_hand]
|
122
|
+
battle.consume(item[:source], :bonus_action)
|
123
|
+
else
|
124
|
+
battle.consume(item[:source], :action)
|
125
|
+
end
|
122
126
|
|
123
|
-
|
124
|
-
weapon = session.load_weapon(item[:weapon]) if item[:weapon]
|
127
|
+
item[:source].break_stealth!(battle)
|
125
128
|
|
126
|
-
|
127
|
-
|
128
|
-
else
|
129
|
-
battle.entity_state_for(item[:source])[:two_weapon] = nil
|
130
|
-
end
|
129
|
+
# handle two-weapon fighting
|
130
|
+
weapon = battle.session.load_weapon(item[:weapon]) if item[:weapon]
|
131
131
|
|
132
|
-
|
132
|
+
if weapon && weapon[:properties]&.include?('light') && !battle.two_weapon_attack?(item[:source]) && !item[:second_hand]
|
133
|
+
battle.entity_state_for(item[:source])[:two_weapon] = item[:weapon]
|
134
|
+
elsif battle.entity_state_for(item[:source])
|
135
|
+
battle.entity_state_for(item[:source])[:two_weapon] = nil
|
136
|
+
end
|
137
|
+
|
138
|
+
# handle multiattacks
|
139
|
+
if battle.entity_state_for(item[:source])
|
133
140
|
battle.entity_state_for(item[:source])[:multiattack]&.each do |_group, attacks|
|
134
141
|
if attacks.include?(item[:attack_name])
|
135
142
|
attacks.delete(item[:attack_name])
|
136
143
|
item[:source].clear_multiattack!(battle) if attacks.empty?
|
137
144
|
end
|
138
145
|
end
|
139
|
-
|
140
|
-
# dismiss help actions
|
141
|
-
battle.dismiss_help_for(item[:target])
|
142
146
|
end
|
147
|
+
|
148
|
+
# dismiss help actions
|
149
|
+
battle.dismiss_help_for(item[:target])
|
143
150
|
end
|
144
151
|
|
145
152
|
def with_advantage?
|
@@ -172,7 +179,13 @@ class AttackAction < Natural20::Action
|
|
172
179
|
|
173
180
|
npc_action = @source.npc_actions.detect { |a| a[:name].downcase == using.downcase } if @source.npc? && using
|
174
181
|
|
175
|
-
if
|
182
|
+
if @source.npc?
|
183
|
+
|
184
|
+
if npc_action.nil?
|
185
|
+
npc_action = @source.properties[actions].detect do |action|
|
186
|
+
action[:name].downcase == using.to_s.downcase
|
187
|
+
end
|
188
|
+
end
|
176
189
|
weapon = npc_action
|
177
190
|
attack_name = npc_action[:name]
|
178
191
|
attack_mod = npc_action[:attack]
|
@@ -199,6 +212,8 @@ class AttackAction < Natural20::Action
|
|
199
212
|
|
200
213
|
# handle the lucky feat
|
201
214
|
attack_roll = attack_roll.reroll(lucky: true) if @source.class_feature?('lucky') && attack_roll.nat_1?
|
215
|
+
target_ac, _cover_ac = effective_ac(battle, target)
|
216
|
+
after_attack_roll_hook(battle, target, source, attack_roll, target_ac)
|
202
217
|
|
203
218
|
if @source.class_feature?('sneak_attack') && (weapon[:properties]&.include?('finesse') || weapon[:type] == 'ranged_attack') && (with_advantage? || battle.enemy_in_melee_range?(
|
204
219
|
target, [@source]
|
@@ -233,8 +248,8 @@ class AttackAction < Natural20::Action
|
|
233
248
|
elsif attack_roll.nat_1?
|
234
249
|
false
|
235
250
|
else
|
236
|
-
cover_ac_adjustments =
|
237
|
-
attack_roll.result >=
|
251
|
+
target_ac, cover_ac_adjustments = effective_ac(battle, target)
|
252
|
+
attack_roll.result >= target_ac
|
238
253
|
end
|
239
254
|
|
240
255
|
if hit
|
@@ -309,14 +324,6 @@ class AttackAction < Natural20::Action
|
|
309
324
|
self
|
310
325
|
end
|
311
326
|
|
312
|
-
# Computes cover armor class adjustment
|
313
|
-
# @param map [Natural20::BattleMap]
|
314
|
-
# @param target [Natural20::Entity]
|
315
|
-
# @return [Integer]
|
316
|
-
def calculate_cover_ac(map, target)
|
317
|
-
cover_calculation(map, @source, target)
|
318
|
-
end
|
319
|
-
|
320
327
|
protected
|
321
328
|
|
322
329
|
# determine eligibility for the 'Protection' fighting style
|
@@ -371,4 +378,6 @@ class TwoWeaponAttackAction < AttackAction
|
|
371
378
|
def label
|
372
379
|
"Bonus Action -> #{super}"
|
373
380
|
end
|
381
|
+
|
382
|
+
def self.apply!(battle, item); end
|
374
383
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Natural20::ActionDamage
|
2
2
|
def damage_event(item, battle)
|
3
|
-
Natural20::EventManager.received_event({ source: item[:source],
|
3
|
+
Natural20::EventManager.received_event({ source: item[:source],
|
4
|
+
attack_roll: item[:attack_roll],
|
5
|
+
target: item[:target], event: :attacked,
|
4
6
|
attack_name: item[:attack_name],
|
5
7
|
damage_type: item[:damage_type],
|
6
8
|
advantage_mod: item[:advantage_mod],
|
@@ -9,7 +9,7 @@ class DashAction < Natural20::Action
|
|
9
9
|
})
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.can?(entity, battle)
|
12
|
+
def self.can?(entity, battle, _options = {})
|
13
13
|
battle && entity.total_actions(battle).positive?
|
14
14
|
end
|
15
15
|
|
@@ -22,14 +22,11 @@ class DashAction < Natural20::Action
|
|
22
22
|
self
|
23
23
|
end
|
24
24
|
|
25
|
-
def apply!(battle)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
battle.entity_state_for(item[:source])[:movement] += item[:source].speed
|
31
|
-
end
|
32
|
-
|
25
|
+
def self.apply!(battle, item)
|
26
|
+
case item[:type]
|
27
|
+
when :dash
|
28
|
+
Natural20::EventManager.received_event({ source: item[:source], event: :dash })
|
29
|
+
battle.entity_state_for(item[:source])[:movement] += item[:source].speed
|
33
30
|
if as_bonus_action
|
34
31
|
battle.entity_state_for(item[:source])[:bonus_action] -= 1
|
35
32
|
else
|
@@ -40,7 +37,7 @@ class DashAction < Natural20::Action
|
|
40
37
|
end
|
41
38
|
|
42
39
|
class DashBonusAction < DashAction
|
43
|
-
def self.can?(entity, battle)
|
40
|
+
def self.can?(entity, battle, options = {})
|
44
41
|
battle && entity.class_feature?('cunning_action') && entity.total_bonus_actions(battle) > 0
|
45
42
|
end
|
46
43
|
end
|