@@ -11,15 +11,14 @@ use crate::incident::trigger_incident;
11
11
use crate :: log;
12
12
use crate :: map:: Terrain :: Unexplored ;
13
13
use crate :: movement:: {
14
- has_movable_units, move_units_destinations, stop_current_move, take_move_state, CurrentMove ,
15
- MoveState ,
14
+ has_movable_units, move_units_destinations, take_move_state, CurrentMove , MoveState ,
16
15
} ;
17
16
use crate :: playing_actions:: PlayingAction ;
18
17
use crate :: recruit:: on_recruit;
19
18
use crate :: resource:: check_for_waste;
20
19
use crate :: resource_pile:: ResourcePile ;
21
20
use crate :: status_phase:: play_status_phase;
22
- use crate :: undo:: { redo , undo , DisembarkUndoContext , UndoContext } ;
21
+ use crate :: undo:: { clean_patch , redo , to_serde_value , undo } ;
23
22
use crate :: unit:: MovementAction :: { Move , Stop } ;
24
23
use crate :: unit:: { get_current_move, MovementAction } ;
25
24
use crate :: wonder:: draw_wonder_card;
@@ -78,36 +77,51 @@ impl Action {
78
77
/// # Panics
79
78
///
80
79
/// Panics if the action is illegal
81
- pub fn execute_action ( game : & mut Game , action : Action , player_index : usize ) {
80
+ #[ must_use]
81
+ pub fn execute_action ( mut game : Game , action : Action , player_index : usize ) -> Game {
82
+ let add_undo = !matches ! ( & action, Action :: Undo ) ;
83
+ let old = to_serde_value ( & game) ;
84
+ let old_player = game. active_player ( ) ;
85
+ game = execute_without_undo ( game, action, player_index) ;
86
+ let new = to_serde_value ( & game) ;
87
+ let new_player = game. active_player ( ) ;
88
+ let patch = json_patch:: diff ( & new, & old) ;
89
+ if old_player != new_player {
90
+ game. lock_undo ( ) ; // don't undo player change
91
+ } else if add_undo && game. can_undo ( ) {
92
+ game. action_log [ game. action_log_index - 1 ] . undo = clean_patch ( patch. 0 ) ;
93
+ }
94
+ game
95
+ }
96
+
97
+ fn execute_without_undo ( mut game : Game , action : Action , player_index : usize ) -> Game {
82
98
assert ! ( player_index == game. active_player( ) , "Illegal action" ) ;
83
99
if let Action :: Undo = action {
84
100
assert ! (
85
101
game. can_undo( ) ,
86
102
"actions revealing new information can't be undone"
87
103
) ;
88
- undo ( game, player_index) ;
89
- return ;
104
+ return undo ( game) ;
90
105
}
91
106
92
107
if matches ! ( action, Action :: Redo ) {
93
- assert ! ( game. can_redo( ) , "no action can be redone" ) ;
94
- redo ( game, player_index) ;
95
- return ;
108
+ assert ! ( game. can_redo( ) , "action can't be redone" ) ;
109
+ redo ( & mut game, player_index) ;
110
+ return game ;
96
111
}
97
112
98
- add_log_item_from_action ( game, & action) ;
99
- add_action_log_item ( game, action. clone ( ) ) ;
113
+ add_log_item_from_action ( & mut game, & action) ;
114
+ add_action_log_item ( & mut game, action. clone ( ) ) ;
100
115
101
116
if let Some ( s) = game. current_event_handler_mut ( ) {
102
117
s. response = action. custom_phase_event ( ) ;
103
118
let details = game. current_event ( ) . event_type . clone ( ) ;
104
- execute_custom_phase_action ( game, player_index, & details) ;
119
+ execute_custom_phase_action ( & mut game, player_index, & details) ;
105
120
} else {
106
- execute_regular_action ( game, action, player_index) ;
121
+ execute_regular_action ( & mut game, action, player_index) ;
107
122
}
108
- check_for_waste ( game) ;
109
-
110
- game. action_log [ game. action_log_index - 1 ] . undo = std:: mem:: take ( & mut game. undo_context_stack ) ;
123
+ check_for_waste ( & mut game) ;
124
+ game
111
125
}
112
126
113
127
fn add_action_log_item ( game : & mut Game , item : Action ) {
@@ -138,7 +152,6 @@ pub(crate) fn execute_custom_phase_action(
138
152
start_combat ( game) ;
139
153
}
140
154
CombatRoundEnd ( r) => {
141
- game. lock_undo ( ) ;
142
155
if let Some ( c) = combat_round_end ( game, r) {
143
156
combat_loop ( game, c) ;
144
157
}
@@ -202,28 +215,14 @@ pub(crate) fn execute_movement_action(
202
215
action : MovementAction ,
203
216
player_index : usize ,
204
217
) {
205
- let Some ( GameState :: Movement ( saved_state) ) = game. state_stack . last ( ) . cloned ( ) else {
206
- panic ! ( "game should be in a movement state" ) ;
207
- } ;
208
- let ( starting_position, disembarked_units) = match action {
218
+ match action {
209
219
Move ( m) => {
210
220
let player = & game. players [ player_index] ;
211
221
let starting_position = player
212
222
. get_unit ( * m. units . first ( ) . expect (
213
223
"instead of providing no units to move a stop movement actions should be done" ,
214
224
) )
215
225
. position ;
216
- let disembarked_units = m
217
- . units
218
- . iter ( )
219
- . filter_map ( |unit| {
220
- let unit = player. get_unit ( * unit) ;
221
- unit. carrier_id . map ( |carrier_id| DisembarkUndoContext {
222
- unit_id : unit. id ,
223
- carrier_id,
224
- } )
225
- } )
226
- . collect ( ) ;
227
226
match move_units_destinations (
228
227
player,
229
228
game,
@@ -283,26 +282,18 @@ pub(crate) fn execute_movement_action(
283
282
starting_position,
284
283
m. destination ,
285
284
) ;
286
- stop_current_move ( game) ;
287
- return ; // can't undo this action
285
+ return ;
288
286
}
289
287
290
288
if move_with_possible_combat ( game, player_index, starting_position, & m) {
291
289
return ;
292
290
}
293
-
294
- ( Some ( starting_position) , disembarked_units)
295
291
}
296
292
Stop => {
297
293
game. pop_state ( ) ;
298
294
return ;
299
295
}
300
296
} ;
301
- game. push_undo_context ( UndoContext :: Movement {
302
- starting_position,
303
- move_state : saved_state,
304
- disembarked_units,
305
- } ) ;
306
297
307
298
let state = take_move_state ( game) ;
308
299
let all_moves_used =
0 commit comments