Class: MoveAction

Inherits:
Natural20::Action show all
Extended by:
Natural20::ActionDamage
Includes:
Natural20::MovementHelper
Defined in:
lib/natural_20/actions/move_action.rb

Overview

typed: true

Instance Attribute Summary collapse

Attributes inherited from Natural20::Action

#action_type, #errors, #result, #session, #source

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Natural20::ActionDamage

damage_event

Methods included from Natural20::MovementHelper

#compute_actual_moves, #requires_squeeze?, #retrieve_opportunity_attacks, #valid_move_path?

Methods inherited from Natural20::Action

#initialize, #label, #name, #to_s, #validate

Constructor Details

This class inherits a constructor from Natural20::Action

Instance Attribute Details

#as_bonus_actionObject

Returns the value of attribute as_bonus_action.



6
7
8
# File 'lib/natural_20/actions/move_action.rb', line 6

def as_bonus_action
  @as_bonus_action
end

#as_dashObject

Returns the value of attribute as_dash.



6
7
8
# File 'lib/natural_20/actions/move_action.rb', line 6

def as_dash
  @as_dash
end

#jump_indexObject

Returns the value of attribute jump_index.



6
7
8
# File 'lib/natural_20/actions/move_action.rb', line 6

def jump_index
  @jump_index
end

#move_pathObject

Returns the value of attribute move_path.



6
7
8
# File 'lib/natural_20/actions/move_action.rb', line 6

def move_path
  @move_path
end

Class Method Details

.apply!(battle, item) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/natural_20/actions/move_action.rb', line 147

def self.apply!(battle, item)
    case (item[:type])
    when :state
      item[:params].each do |k, v|
        item[:source].send(:"#{k}=", v)
      end
    when :acrobatics, :athletics
      if item[:success]
        Natural20::EventManager.received_event(source: item[:source], event: item[:type], success: true,
                                               roll: item[:roll])
      else
        Natural20::EventManager.received_event(source: item[:source], event: item[:type], success: false,
                                               roll: item[:roll])
        item[:source].prone!
      end
    when :drop_grapple
      item[:target].escape_grapple_from!(@source)
      Natural20::EventManager.received_event(event: :drop_grapple,
                                             target: item[:target], source: @source,
                                             source_roll: item[:source_roll],
                                             target_roll: item[:target_roll])

    when :move
      item[:map].move_to!(item[:source], *item[:position], battle)
      if item[:as_dash] && item[:as_bonus_action]
        battle.entity_state_for(item[:source])[:bonus_action] -= 1
      elsif item[:as_dash]
        battle.entity_state_for(item[:source])[:action] -= 1
      elsif battle
        battle.entity_state_for(item[:source])[:movement] -= item[:move_cost] * battle.map.feet_per_grid
      end

      Natural20::EventManager.received_event({ event: :move, source: item[:source], position: item[:position], path: item[:path],
                                               feet_per_grid: battle.map&.feet_per_grid,
                                               as_dash: item[:as_dash], as_bonus: item[:as_bonus_action] })
    end
end

.build(session, source) ⇒ Object



32
33
34
35
# File 'lib/natural_20/actions/move_action.rb', line 32

def self.build(session, source)
  action = MoveAction.new(session, source, :move)
  action.build_map
end

.can?(entity, battle) ⇒ Boolean



8
9
10
# File 'lib/natural_20/actions/move_action.rb', line 8

def self.can?(entity, battle)
  battle.nil? || entity.available_movement(battle).positive?
end

Instance Method Details

#build_mapObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/natural_20/actions/move_action.rb', line 12

def build_map
  OpenStruct.new({
                   action: self,
                   param: [
                     {
                       type: :movement
                     }
                   ],
                   next: lambda { |path_and_jump_index|
                     path, jump_index = path_and_jump_index
                     self.move_path = path
                     self.jump_index = jump_index
                     OpenStruct.new({
                                      param: nil,
                                      next: -> { self }
                                    })
                   }
                 })
end

#check_opportunity_attacks(entity, move_list, battle, grappled: false) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/natural_20/actions/move_action.rb', line 133

def check_opportunity_attacks(entity, move_list, battle, grappled: false)
  if battle

    retrieve_opportunity_attacks(entity, move_list, battle).each do |enemy_opporunity|
      original_location = move_list[0...enemy_opporunity[:path]]
      attack_location = original_location.last
      battle.trigger_opportunity_attack(enemy_opporunity[:source], entity, *attack_location)

      return original_location if !grappled && !entity.conscious?
    end
  end
  move_list
end

#resolve(_session, map, opts = {}) ⇒ Object

Options Hash (opts):



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/natural_20/actions/move_action.rb', line 39

def resolve(_session, map, opts = {})
  raise 'no path specified' if (move_path.nil? || move_path.empty?) && opts[:move_path].nil?

  @result = []
  # check for melee opportunity attacks
  battle = opts[:battle]

  current_moves = move_path.presence || opts[:move_path]
  jumps = jump_index || []

  actual_moves = []
  additional_effects = []

  movement_budget = if as_dash
                      (@source.speed / 5).floor
                    else
                      (@source.available_movement(battle) / 5).floor
                    end

  movement = compute_actual_moves(@source, current_moves, map, battle, movement_budget, manual_jump: jumps)
  actual_moves = movement.movement

  actual_moves.pop while actual_moves.last && !map.placeable?(@source, *actual_moves.last, battle)

  actual_moves = check_opportunity_attacks(@source, actual_moves, battle)

  # check if movement requires athletics checks
  actual_moves = check_movement_athletics(actual_moves, movement.athletics_check_locations, battle, map)

  # check if movement requires dex checks, e.g. jumping and landing on difficult terrain
  actual_moves = check_movement_acrobatics(actual_moves, movement.acrobatics_check_locations, battle)

  # calculate for area based triggers
  cutoff = false

  safe_moves = []
  actual_moves.each_with_index do |move, _index|
    is_flying_or_jumping = movement.jump_locations.include?(move)
    trigger_results = map.area_trigger!(@source, move, is_flying_or_jumping)
    if trigger_results.empty?
      safe_moves << move
    else
      safe_moves << move
      additional_effects += trigger_results
      break
    end
  end

  movement = compute_actual_moves(@source, safe_moves, map, battle, movement_budget, manual_jump: jumps)

  # compute grappled entity movement
  if @source.grappling?
    grappled_movement = movement.movement.dup
    grappled_movement.pop

    @source.grappling_targets.each do |grappling_target|
      start_pos = map.entity_or_object_pos(grappling_target)
      grappled_entity_movement = [start_pos] + grappled_movement

      additional_effects << {
        source: grappling_target,
        map: map,
        battle: battle,
        type: :move,
        path: grappled_entity_movement,
        as_dash: as_dash,
        as_bonus_action: as_bonus_action,
        move_cost: 0,
        position: grappled_entity_movement.last
      }

      grappled_movement.pop
    end
  end

  @result << {
    source: @source,
    map: map,
    battle: battle,
    as_dash: as_dash,
    as_bonus_action: as_bonus_action,
    type: :move,
    path: movement.movement,
    move_cost: movement_budget - movement.budget,
    position: movement.movement.last
  }
  @result += additional_effects

  self
end