8
8
range_360 ,
9
9
engine_filters ,
10
10
directions ,
11
+ get_centered_pos_in_px ,
12
+ promised_wait ,
11
13
} from "./utils" ;
12
14
import { ControllableChar } from "./ControllableChar" ;
13
15
import { interaction_patterns } from "./game_events/GameEventManager" ;
@@ -90,23 +92,30 @@ export class NPC extends ControllableChar {
90
92
private _after_psynergy_cast_events : {
91
93
[ psynergy_key : string ] : GameEvent [ ] ;
92
94
} ;
93
- private _custom_movement : {
95
+ private _custom_movements : {
94
96
position ?: {
95
- x : number ,
96
- y : number ,
97
- is_px ?: boolean ,
98
- incremental ?: boolean
99
- }
100
- dashing ?: boolean ,
101
- look_direction ?: directions ,
102
- wait ?: number ,
97
+ x : number ;
98
+ y : number ;
99
+ is_px ?: boolean ;
100
+ incremental ?: boolean ;
101
+ dashing ?: boolean ;
102
+ extra_speed ?: number ;
103
+ } ;
104
+ look_direction ?: string ;
105
+ wait ?: number ;
103
106
animation ?: {
104
- animation : string ,
105
- action : string ,
106
- frame_rate : number ,
107
- reset_before_start ?: boolean
108
- }
107
+ animation : string ;
108
+ action : string ;
109
+ frame_rate : number ;
110
+ reset_before_start ?: boolean ;
111
+ stop_char_on_finish ?: boolean ;
112
+ } ;
109
113
} [ ] ;
114
+ private _next_custom_move_available : boolean ;
115
+ private _custom_move_index : number ;
116
+ private _custom_move_moving : boolean ;
117
+ private _loop_custom_move : boolean ;
118
+ private _custom_move_destination : { x : number ; y : number } ;
110
119
111
120
/** If true, this NPC will move freely while a game event is happening. */
112
121
public move_freely_in_event : boolean ;
@@ -164,7 +173,8 @@ export class NPC extends ControllableChar {
164
173
after_psynergy_cast_events ,
165
174
force_char_stop_in_event ,
166
175
force_idle_action_in_event ,
167
- custom_movement
176
+ custom_movement ,
177
+ loop_custom_move
168
178
) {
169
179
super (
170
180
game ,
@@ -189,6 +199,10 @@ export class NPC extends ControllableChar {
189
199
movement_type = this . data . storage . get ( this . storage_keys . movement_type ) ;
190
200
}
191
201
this . movement_type = movement_type ?? npc_movement_types . IDLE ;
202
+ this . _custom_movements = custom_movement ?? [ ] ;
203
+ if ( this . movement_type === npc_movement_types . CUSTOM && ! this . _custom_movements . length ) {
204
+ this . movement_type = npc_movement_types . IDLE ;
205
+ }
192
206
if ( this . storage_keys . move_freely_in_event !== undefined ) {
193
207
move_freely_in_event = this . data . storage . get ( this . storage_keys . move_freely_in_event ) ;
194
208
}
@@ -240,7 +254,11 @@ export class NPC extends ControllableChar {
240
254
this . _step_max_variation = step_max_variation ;
241
255
this . _map_index = map_index ;
242
256
this . _allow_interaction_when_inactive = allow_interaction_when_inactive ?? false ;
243
- this . _custom_movement = custom_movement ?? [ ] ;
257
+ this . _next_custom_move_available = true ;
258
+ this . _custom_move_index = 0 ;
259
+ this . _custom_move_moving = false ;
260
+ this . _custom_move_destination = { x : 0 , y : 0 } ;
261
+ this . _loop_custom_move = loop_custom_move ?? true ;
244
262
}
245
263
246
264
/** The list of GameEvents related to this NPC. */
@@ -518,9 +536,101 @@ export class NPC extends ControllableChar {
518
536
519
537
/**
520
538
* Do all the necessary steps to execute a list of custom moves.
539
+ * Types of custom moves: move to position, face direction, wait and animation.
521
540
*/
522
541
private execute_custom_moves ( ) {
523
-
542
+ if ( ! this . _next_custom_move_available ) {
543
+ if ( this . _custom_move_moving ) {
544
+ if ( ! this . set_speed_factors ( ) ) {
545
+ this . stop_char ( true ) ;
546
+ } else {
547
+ this . choose_direction_by_speed ( ) ;
548
+ this . set_direction ( ) ;
549
+ this . choose_action_based_on_char_state ( ) ;
550
+ this . calculate_speed ( ) ;
551
+ this . apply_speed ( ) ;
552
+ this . play_current_action ( true ) ;
553
+ if (
554
+ get_sqr_distance (
555
+ this . x ,
556
+ this . _custom_move_destination . x ,
557
+ this . y ,
558
+ this . _custom_move_destination . y
559
+ ) < NPC . STOP_MINIMAL_DISTANCE_SQR
560
+ ) {
561
+ this . stop_char ( true ) ;
562
+ this . increase_extra_speed (
563
+ - this . _custom_movements [ this . _custom_move_index ] . position . extra_speed ?? 0
564
+ ) ;
565
+ this . _next_custom_move_available = true ;
566
+ ++ this . _custom_move_index ;
567
+ this . _custom_move_moving = false ;
568
+ }
569
+ }
570
+ }
571
+ return ;
572
+ }
573
+ if ( this . _custom_move_index === this . _custom_movements . length ) {
574
+ if ( ! this . _loop_custom_move ) {
575
+ return ;
576
+ }
577
+ this . _custom_move_index = 0 ;
578
+ }
579
+ this . _next_custom_move_available = false ;
580
+ const move = this . _custom_movements [ this . _custom_move_index ] ;
581
+ if ( move . position ) {
582
+ let x = move . position . is_px
583
+ ? move . position . x
584
+ : get_centered_pos_in_px ( move . position . x , this . data . map . tile_width ) ;
585
+ if ( move . position . incremental ) {
586
+ x += this . x ;
587
+ }
588
+ let y = move . position . is_px
589
+ ? move . position . y
590
+ : get_centered_pos_in_px ( move . position . y , this . data . map . tile_height ) ;
591
+ if ( move . position . incremental ) {
592
+ y += this . y ;
593
+ }
594
+ this . _custom_move_destination . x = x ;
595
+ this . _custom_move_destination . y = y ;
596
+ const speed_vector = new Phaser . Point ( x - this . x , y - this . y ) . normalize ( ) ;
597
+ this . force_diagonal_speed . x = speed_vector . x ;
598
+ this . force_diagonal_speed . y = speed_vector . y ;
599
+ this . _angle_direction = Math . atan2 ( speed_vector . y , speed_vector . x ) ;
600
+ this . _custom_move_moving = true ;
601
+ this . dashing = move . position . dashing ?? false ;
602
+ this . increase_extra_speed ( move . position . extra_speed ?? 0 ) ;
603
+ } else if ( move . look_direction ) {
604
+ const direction = directions [ move . look_direction ] ;
605
+ this . face_direction ( direction ) . then ( ( ) => {
606
+ this . _next_custom_move_available = true ;
607
+ ++ this . _custom_move_index ;
608
+ } ) ;
609
+ } else if ( move . animation ) {
610
+ if ( move . animation . animation in directions ) {
611
+ this . set_direction ( directions [ move . animation . animation ] ) ;
612
+ }
613
+ const anim = this . play (
614
+ move . animation . action ,
615
+ move . animation . animation ,
616
+ true ,
617
+ move . animation . frame_rate ,
618
+ false ,
619
+ move . animation . reset_before_start
620
+ ) ;
621
+ anim . onComplete . addOnce ( ( ) => {
622
+ if ( move . animation . stop_char_on_finish ) {
623
+ this . stop_char ( true ) ;
624
+ }
625
+ this . _next_custom_move_available = true ;
626
+ ++ this . _custom_move_index ;
627
+ } ) ;
628
+ } else if ( move . wait ) {
629
+ promised_wait ( this . game , move . wait , ( ) => {
630
+ this . _next_custom_move_available = true ;
631
+ ++ this . _custom_move_index ;
632
+ } ) ;
633
+ }
524
634
}
525
635
526
636
/**
@@ -560,6 +670,7 @@ export class NPC extends ControllableChar {
560
670
561
671
/**
562
672
* Sets the x-y speed values that this NPC is going to be using to move.
673
+ * It's important to have NPC._angle_direction set for correct checking.
563
674
* @returns Returns true if the speed values were set. Going towards a collision direction is not acceptable, then returns false.
564
675
*/
565
676
private set_speed_factors ( ) {
0 commit comments