@@ -15,6 +15,7 @@ use crate::{
1515 } ,
1616 fruit:: Fruit ,
1717 ghosts:: { GhostGroup , GhostGroupUpdateContext } ,
18+ high_scores:: HighScoreTable ,
1819 mazedata:: MazeSpec ,
1920 modes:: GhostMode ,
2021 nodes:: NodeGroup ,
@@ -123,6 +124,7 @@ struct GameplayState {
123124 level : u32 ,
124125 lives : u32 ,
125126 score : u32 ,
127+ high_score : u32 ,
126128 pacman_pause_remaining : f32 ,
127129 fruit_thresholds_spawned : [ bool ; 2 ] ,
128130 personal_dot_counters : [ usize ; 4 ] ,
@@ -179,6 +181,7 @@ struct TitleScreenState {
179181 prompt_blink_timer : f32 ,
180182 prompt_visible : bool ,
181183 title_image : Arc < RenderedImage > ,
184+ high_score_image : Arc < RenderedImage > ,
182185 company_image : Arc < RenderedImage > ,
183186 bonus_image : Arc < RenderedImage > ,
184187 push_start_image : Arc < RenderedImage > ,
@@ -216,6 +219,8 @@ struct AttractNicknameRow {
216219struct AppState {
217220 title_screen : TitleScreenState ,
218221 gameplay : Option < GameplayState > ,
222+ high_scores : HighScoreTable ,
223+ persist_high_scores : bool ,
219224 events : Vec < GameEvent > ,
220225}
221226
@@ -227,6 +232,13 @@ impl Game {
227232 }
228233 }
229234
235+ pub fn load ( ) -> Self {
236+ Self {
237+ state : AppState :: load ( ) ,
238+ quit_requested : false ,
239+ }
240+ }
241+
230242 /// Updates with input.
231243 pub fn update_with_input ( & mut self , dt : f32 , input : UpdateInput ) {
232244 let q_pressed = input
@@ -403,11 +415,26 @@ impl GameplayState {
403415 const READY_TIME : f32 = 3.0 ;
404416
405417 fn new ( ) -> Self {
406- Self :: start_level ( 1 , 5 , 0 , Vec :: new ( ) )
418+ Self :: with_high_score ( 0 )
419+ }
420+
421+ fn with_high_score ( high_score : u32 ) -> Self {
422+ Self :: start_level_with_high_score ( 1 , 5 , 0 , Vec :: new ( ) , high_score)
407423 }
408424
425+ #[ cfg( test) ]
409426 /// Starts level.
410427 fn start_level ( level : u32 , lives : u32 , score : u32 , fruit_captured : Vec < usize > ) -> Self {
428+ Self :: start_level_with_high_score ( level, lives, score, fruit_captured, 0 )
429+ }
430+
431+ fn start_level_with_high_score (
432+ level : u32 ,
433+ lives : u32 ,
434+ score : u32 ,
435+ fruit_captured : Vec < usize > ,
436+ high_score : u32 ,
437+ ) -> Self {
411438 let maze_spec = MazeSpec :: arcade ( ) ;
412439 let ( nodes, pacman, pellets, ghosts) = build_gameplay_level ( maze_spec, level) ;
413440 let maze_sprites = MazeSprites :: from_layout ( maze_spec. layout ) ;
@@ -416,6 +443,7 @@ impl GameplayState {
416443
417444 let mut text_group = TextGroup :: new ( ) ;
418445 text_group. update_score ( score) ;
446+ text_group. update_high_score ( high_score. max ( score) ) ;
419447 text_group. update_level ( level) ;
420448 text_group. show_status ( StatusText :: Ready ) ;
421449
@@ -429,6 +457,7 @@ impl GameplayState {
429457 level,
430458 lives,
431459 score,
460+ high_score : high_score. max ( score) ,
432461 pacman_pause_remaining : 0.0 ,
433462 fruit_thresholds_spawned : [ false ; 2 ] ,
434463 personal_dot_counters : [ 0 ; 4 ] ,
@@ -976,11 +1005,24 @@ impl GameplayState {
9761005 fn update_score ( & mut self , points : u32 ) {
9771006 self . score += points;
9781007 self . text_group . update_score ( self . score ) ;
1008+ self . sync_high_score ( ) ;
9791009 }
9801010
9811011 /// Updates score headless.
9821012 fn update_score_headless ( & mut self , points : u32 ) {
9831013 self . score += points;
1014+ self . sync_high_score ( ) ;
1015+ }
1016+
1017+ fn sync_high_score ( & mut self ) {
1018+ if self . score > self . high_score {
1019+ self . high_score = self . score ;
1020+ self . text_group . update_high_score ( self . high_score ) ;
1021+ }
1022+ }
1023+
1024+ fn high_score ( & self ) -> u32 {
1025+ self . high_score . max ( self . score )
9841026 }
9851027
9861028 /// Checks pellet events.
@@ -1242,11 +1284,12 @@ impl GameplayState {
12421284 GameplayAction :: ResetLevel => self . reset_level ( ) ,
12431285 GameplayAction :: NextLevel => {
12441286 let flags = self . secret_mode_flags ( ) ;
1245- * self = Self :: start_level (
1287+ * self = Self :: start_level_with_high_score (
12461288 self . level + 1 ,
12471289 self . lives ,
12481290 self . score ,
12491291 self . fruit_captured . clone ( ) ,
1292+ self . high_score ,
12501293 ) ;
12511294 self . apply_secret_mode_flags ( flags) ;
12521295 }
@@ -1262,11 +1305,12 @@ impl GameplayState {
12621305 GameplayAction :: ResetLevel => self . reset_level_headless ( ) ,
12631306 GameplayAction :: NextLevel => {
12641307 let flags = self . secret_mode_flags ( ) ;
1265- * self = Self :: start_level (
1308+ * self = Self :: start_level_with_high_score (
12661309 self . level + 1 ,
12671310 self . lives ,
12681311 self . score ,
12691312 self . fruit_captured . clone ( ) ,
1313+ self . high_score ,
12701314 ) ;
12711315 self . apply_secret_mode_flags ( flags) ;
12721316 self . pause . set_paused ( false ) ;
@@ -1540,13 +1584,23 @@ impl TitleAttractScene {
15401584}
15411585
15421586impl TitleScreenState {
1587+ #[ cfg( test) ]
15431588 fn new ( ) -> Self {
1589+ Self :: with_high_score ( 0 )
1590+ }
1591+
1592+ fn with_high_score ( high_score : u32 ) -> Self {
15441593 Self {
15451594 scene : TitleAttractScene :: Title ,
15461595 scene_timer : 0.0 ,
15471596 prompt_blink_timer : 0.0 ,
15481597 prompt_visible : true ,
15491598 title_image : rasterize_text_image ( "PACMAN" , YELLOW , 64.0 ) ,
1599+ high_score_image : rasterize_text_image (
1600+ & format ! ( "HIGH SCORE {high_score:08}" ) ,
1601+ WHITE ,
1602+ 16.0 ,
1603+ ) ,
15501604 company_image : rasterize_text_image ( "1980 MIDWAY MFG CO" , WHITE , 16.0 ) ,
15511605 bonus_image : rasterize_text_image ( "BONUS PACMAN FOR 10000 PTS" , WHITE , 16.0 ) ,
15521606 push_start_image : rasterize_text_image ( "PUSH START BUTTON" , YELLOW , 16.0 ) ,
@@ -1680,6 +1734,7 @@ impl TitleScreenState {
16801734 /// Appends title scene.
16811735 fn append_title_scene ( & self , frame : & mut FrameData ) {
16821736 append_centered_text ( frame, & self . title_image , 68.0 ) ;
1737+ append_centered_text ( frame, & self . high_score_image , 136.0 ) ;
16831738 append_centered_text ( frame, & self . company_image , 168.0 ) ;
16841739 append_centered_text ( frame, & self . bonus_image , 204.0 ) ;
16851740 if self . prompt_visible {
@@ -1825,9 +1880,19 @@ fn append_actor_sprite(frame: &mut FrameData, image: Arc<RenderedImage>, center:
18251880
18261881impl AppState {
18271882 fn new ( ) -> Self {
1883+ Self :: with_high_scores ( HighScoreTable :: default ( ) , false )
1884+ }
1885+
1886+ fn load ( ) -> Self {
1887+ Self :: with_high_scores ( HighScoreTable :: load_default ( ) , true )
1888+ }
1889+
1890+ fn with_high_scores ( high_scores : HighScoreTable , persist_high_scores : bool ) -> Self {
18281891 Self {
1829- title_screen : TitleScreenState :: new ( ) ,
1892+ title_screen : TitleScreenState :: with_high_score ( high_scores . top_score ( ) ) ,
18301893 gameplay : None ,
1894+ high_scores,
1895+ persist_high_scores,
18311896 events : vec ! [ GameEvent :: TitleScreenEntered ] ,
18321897 }
18331898 }
@@ -1840,11 +1905,15 @@ impl AppState {
18401905 input. pause_requested ,
18411906 & input. typed_chars ,
18421907 ) ;
1908+ let high_score_changed = self . high_scores . record ( gameplay. high_score ( ) ) ;
1909+ if high_score_changed && self . persist_high_scores {
1910+ let _ = self . high_scores . save_default ( ) ;
1911+ }
18431912 self . events . extend ( gameplay. drain_events ( ) ) ;
18441913
18451914 if gameplay. return_to_title_requested {
18461915 self . gameplay = None ;
1847- self . title_screen = TitleScreenState :: new ( ) ;
1916+ self . title_screen = TitleScreenState :: with_high_score ( self . high_scores . top_score ( ) ) ;
18481917 self . events . push ( GameEvent :: TitleScreenEntered ) ;
18491918 }
18501919 return ;
@@ -1863,7 +1932,7 @@ impl AppState {
18631932 }
18641933
18651934 if should_start {
1866- self . gameplay = Some ( GameplayState :: new ( ) ) ;
1935+ self . gameplay = Some ( GameplayState :: with_high_score ( self . high_scores . top_score ( ) ) ) ;
18671936 self . events . push ( GameEvent :: GameStarted ) ;
18681937 }
18691938 }
0 commit comments