Merge 'progress' branch.
authorparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Wed, 12 Mar 2008 14:52:26 +0000 (14:52 +0000)
committerparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Wed, 12 Mar 2008 14:52:26 +0000 (14:52 +0000)
git-svn-id: https://s.snth.net/svn/neverball/trunk@1877 78b8d119-cf0a-0410-b17c-f493084dd1d7

38 files changed:
Makefile
ball/demo.c
ball/demo.h
ball/game.c
ball/game.h
ball/hud.c
ball/level.c
ball/level.h
ball/levels.c [deleted file]
ball/levels.h [deleted file]
ball/main.c
ball/mode.c [deleted file]
ball/mode.h [deleted file]
ball/progress.c [new file with mode: 0644]
ball/progress.h [new file with mode: 0644]
ball/set.c
ball/set.h
ball/st_demo.c
ball/st_done.c
ball/st_fall_out.c
ball/st_goal.c
ball/st_help.c
ball/st_level.c
ball/st_over.c
ball/st_pause.c
ball/st_play.c
ball/st_save.c
ball/st_set.c
ball/st_start.c
ball/st_time_out.c
ball/st_title.c
ball/util.c
ball/util.h
data/gui/demo1.nbr
data/gui/demo2.nbr
share/config.c
share/config.h
share/lang.h

index c95a703..21d4ed6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -149,11 +149,10 @@ BALL_OBJS := \
        share/tilt.o        \
        share/common.o      \
        ball/hud.o          \
-       ball/mode.o         \
        ball/game.o         \
        ball/score.o        \
        ball/level.o        \
-       ball/levels.o       \
+       ball/progress.o     \
        ball/set.o          \
        ball/demo.o         \
        ball/util.o         \
index 39aceaf..d466163 100644 (file)
 #include "binary.h"
 #include "text.h"
 #include "common.h"
+#include "level.h"
 
 /*---------------------------------------------------------------------------*/
 
 #define MAGIC           0x52424EAF
-#define DEMO_VERSION    5
+#define DEMO_VERSION    6
 
 #define DATELEN 20
 
@@ -52,19 +53,17 @@ void demo_dump_info(const struct demo *d)
            "Player:       %s\n"
            "Shot:         %s\n"
            "Level:        %s\n"
-           "  Back:       %s\n"
-           "  Grad:       %s\n"
-           "  Song:       %s\n"
            "Time:         %d\n"
            "Goal:         %d\n"
+           "Goal enabled: %d\n"
            "Score:        %d\n"
            "Balls:        %d\n"
            "Total Time:   %d\n",
            d->name, d->filename,
            d->timer, d->coins, d->mode, d->status, ctime(&d->date),
            d->player,
-           d->shot, d->file, d->back, d->grad, d->song,
-           d->time, d->goal, d->score, d->balls, d->times);
+           d->shot, d->file,
+           d->time, d->goal, d->goal_e, d->score, d->balls, d->times);
 }
 
 static int demo_header_read(FILE *fp, struct demo *d)
@@ -112,6 +111,7 @@ static int demo_header_read(FILE *fp, struct demo *d)
 
         get_index(fp, &d->time);
         get_index(fp, &d->goal);
+        get_index(fp, &d->goal_e);
         get_index(fp, &d->score);
         get_index(fp, &d->balls);
         get_index(fp, &d->times);
@@ -146,6 +146,7 @@ static void demo_header_write(FILE *fp, struct demo *d)
 
     put_index(fp, &d->time);
     put_index(fp, &d->goal);
+    put_index(fp, &d->goal_e);
     put_index(fp, &d->score);
     put_index(fp, &d->balls);
     put_index(fp, &d->times);
@@ -262,9 +263,8 @@ void demo_unique(char *name)
 
 /*---------------------------------------------------------------------------*/
 
-int demo_play_init(const char *name,
-                   const struct level *level,
-                   const struct level_game *lg)
+int demo_play_init(const char *name, const struct level *level,
+                   int mode, int t, int g, int e, int s, int b, int tt)
 {
     struct demo demo;
 
@@ -273,28 +273,26 @@ int demo_play_init(const char *name,
     strncpy(demo.filename, config_user(name), MAXSTR);
     strcat(demo.filename, REPLAY_EXT);
 
-    demo.mode = lg->mode;
+    demo.mode = mode;
     demo.date = time(NULL);
 
     config_get_s(CONFIG_PLAYER, demo.player, MAXNAM);
 
     strncpy(demo.shot, level->shot, PATHMAX);
     strncpy(demo.file, level->file, PATHMAX);
-    strncpy(demo.back, level->back, PATHMAX);
-    strncpy(demo.grad, level->grad, PATHMAX);
-    strncpy(demo.song, level->song, PATHMAX);
 
-    demo.time  = lg->time;
-    demo.goal  = lg->goal;
-    demo.score = lg->score;
-    demo.balls = lg->balls;
-    demo.times = lg->times;
+    demo.time   = t;
+    demo.goal   = g;
+    demo.goal_e = e;
+    demo.score  = s;
+    demo.balls  = b;
+    demo.times  = tt;
 
     if ((demo_fp = fopen(demo.filename, FMODE_WB)))
     {
         demo_header_write(demo_fp, &demo);
         audio_music_fade_to(2.0f, level->song);
-        return game_init(level, lg->time, lg->goal);
+        return game_init(level->file, t, e);
     }
     return 0;
 }
@@ -305,7 +303,7 @@ void demo_play_step()
         input_put(demo_fp);
 }
 
-void demo_play_stat(const struct level_game *lg)
+void demo_play_stat(int status, int coins, int timer)
 {
     if (demo_fp)
     {
@@ -313,9 +311,9 @@ void demo_play_stat(const struct level_game *lg)
 
         fseek(demo_fp, 8, SEEK_SET);
 
-        put_index(demo_fp, &lg->timer);
-        put_index(demo_fp, &lg->coins);
-        put_index(demo_fp, &lg->status);
+        put_index(demo_fp, &timer);
+        put_index(demo_fp, &coins);
+        put_index(demo_fp, &status);
 
         fseek(demo_fp, pos, SEEK_SET);
     }
@@ -356,17 +354,6 @@ void demo_rename(const char *name)
 
 /*---------------------------------------------------------------------------*/
 
-static int demo_load_level(const struct demo *demo, struct level *level)
-{
-    if (level_load(demo->file, level))
-    {
-        level->time = demo->time;
-        level->goal = demo->goal;
-        return 1;
-    }
-    return 0;
-}
-
 static struct demo  demo_replay;       /* The current demo */
 static struct level demo_level_replay; /* The current level demo-ed*/
 
@@ -377,7 +364,7 @@ const struct demo *curr_demo_replay(void)
 
 static int demo_status = GAME_NONE;
 
-int demo_replay_init(const char *name, struct level_game *lg)
+int demo_replay_init(const char *name, int *g, int *m, int *b, int *s, int *tt)
 {
     demo_status = GAME_NONE;
     demo_fp     = fopen(name, FMODE_RB);
@@ -389,24 +376,31 @@ int demo_replay_init(const char *name, struct level_game *lg)
                 base_name(text_from_locale(demo_replay.filename), REPLAY_EXT),
                 PATHMAX);
 
-        if (!demo_load_level(&demo_replay, &demo_level_replay))
+        if (level_load(demo_replay.file, &demo_level_replay))
+        {
+            demo_level_replay.time = demo_replay.time;
+            demo_level_replay.goal = demo_replay.goal;
+        }
+        else
             return 0;
 
-        if (lg)
-        {
-            lg->mode  = demo_replay.mode;
-            lg->score = demo_replay.score;
-            lg->times = demo_replay.times;
-            lg->time  = demo_replay.time;
-            lg->goal  = demo_replay.goal;
+        if (g)  *g  = demo_replay.goal;
+        if (m)  *m  = demo_replay.mode;
+        if (b)  *b  = demo_replay.balls;
+        if (s)  *s  = demo_replay.score;
+        if (tt) *tt = demo_replay.times;
 
-            /* A normal replay demo */
+        if (g)
+        {
             audio_music_fade_to(0.5f, demo_level_replay.song);
-            return game_init(&demo_level_replay, demo_replay.time,
-                             demo_replay.goal);
+
+            return game_init(demo_level_replay.file,
+                             demo_level_replay.time,
+                             demo_replay.goal_e);
         }
-        else /* A title screen demo */
-            return game_init(&demo_level_replay, demo_replay.time, 0);
+        else
+            return game_init(demo_level_replay.file,
+                             demo_level_replay.time, 1);
     }
     return 0;
 }
index ee7ee1f..55590e1 100644 (file)
@@ -9,25 +9,26 @@
 
 struct demo
 {
-    char   name[PATHMAX];      /* demo basename */
-    char   filename[MAXSTR];  /* demo path */
-
-    int    timer;           /* elapsed time */
-    int    coins;           /* coin number */
-    int    status;          /* how the replay end */
-    int    mode;            /* game mode */
-    time_t date;            /* date of creation */
-    char   player[MAXNAM];  /* player name */
+    char   name[PATHMAX];     /* demo basename    */
+    char   filename[MAXSTR];  /* demo path        */
+
+    char   player[MAXNAM];
+    time_t date;
+
+    int    timer;
+    int    coins;
+    int    status;
+    int    mode;
+
     char   shot[PATHMAX];   /* image filename */
     char   file[PATHMAX];   /* level filename */
-    char   back[PATHMAX];   /* level bg filename */
-    char   grad[PATHMAX];   /* level gradient filename */
-    char   song[PATHMAX];   /* level song filename */
-    int    time;            /* time limit (! training mode) */
-    int    goal;            /* coin to open the goal (! training mode) */
-    int    score;           /* sum of coins (challenge mode) */
-    int    balls;           /* number of balls (challenge mode) */
-    int    times;           /* total time (challenge mode) */
+
+    int    time;            /* time limit        */
+    int    goal;            /* coin limit        */
+    int    goal_e;          /* goal enabled flag */
+    int    score;           /* total coins       */
+    int    balls;           /* number of balls   */
+    int    times;           /* total time        */
 };
 
 /*---------------------------------------------------------------------------*/
@@ -46,9 +47,9 @@ void demo_unique(char *);
 /*---------------------------------------------------------------------------*/
 
 int  demo_play_init(const char *, const struct level *,
-                    const struct level_game *);
+                    int, int, int, int, int, int, int);
 void demo_play_step(void);
-void demo_play_stat(const struct level_game *);
+void demo_play_stat(int, int, int);
 void demo_play_stop(void);
 
 int  demo_saved (void);
@@ -56,7 +57,7 @@ void demo_rename(const char *);
 
 /*---------------------------------------------------------------------------*/
 
-int  demo_replay_init(const char *, struct level_game *);
+int  demo_replay_init(const char *, int *, int *, int *, int *, int *);
 int  demo_replay_step(float);
 void demo_replay_stop(int);
 void demo_replay_dump_info(void);
index 618f6d2..879c969 100644 (file)
@@ -27,7 +27,6 @@
 #include "solid_gl.h"
 #include "config.h"
 #include "binary.h"
-#include "level.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -55,7 +54,7 @@ static float view_e[3][3];              /* Current view reference frame      */
 static float view_k;
 
 static int   coins  = 0;                /* Collected coins                   */
-static int   goal_c = 0;                /* Goal coins remaining (0 = open)   */
+static int   goal_e = 0;                /* Goal enabled flag                 */
 static float goal_k = 0;                /* Goal animation                    */
 static int   jump_e = 1;                /* Jumping enabled flag              */
 static int   jump_b = 0;                /* Jump-in-progress flag             */
@@ -312,8 +311,12 @@ static void view_init(void)
     view_e[2][2] = 1.f;
 }
 
-int game_init(const struct level *level, int t, int g)
+int game_init(const char *file_name, int t, int e)
 {
+    char *back_name = NULL, *grad_name = NULL;
+
+    int i;
+
     timer      = (float) t / 100.f;
     timer_down = (t > 0);
     coins      = 0;
@@ -321,7 +324,7 @@ int game_init(const struct level *level, int t, int g)
     if (game_state)
         game_free();
 
-    if (!sol_load_gl(&file, config_data(level->file),
+    if (!sol_load_gl(&file, config_data(file_name),
                      config_get_d(CONFIG_TEXTURES),
                      config_get_d(CONFIG_SHADOW)))
         return (game_state = 0);
@@ -338,19 +341,28 @@ int game_init(const struct level *level, int t, int g)
     jump_e = 1;
     jump_b = 0;
 
-    goal_c = g;
-    goal_k = (g == 0) ? 1.0f : 0.0f;
+    goal_e = e ? 1    : 0;
+    goal_k = e ? 1.0f : 0.0f;
 
     /* Initialise the level, background, particles, fade, and view. */
 
     fade_k =  1.0f;
     fade_d = -2.0f;
 
+    for (i = 0; i < file.dc; i++)
+    {
+        char *k = file.av + file.dv[i].ai;
+        char *v = file.av + file.dv[i].aj;
+
+        if (strcmp(k, "back") == 0) back_name = v;
+        if (strcmp(k, "grad") == 0) grad_name = v;
+    }
+
     part_reset(GOAL_HEIGHT);
     view_init();
-    back_init(level->grad, config_get_d(CONFIG_GEOMETRY));
+    back_init(grad_name, config_get_d(CONFIG_GEOMETRY));
 
-    sol_load_gl(&back, config_data(level->back),
+    sol_load_gl(&back, config_data(back_name),
                 config_get_d(CONFIG_TEXTURES), 0);
 
     /* Initialize ball size tracking... */
@@ -384,11 +396,6 @@ int curr_coins(void)
     return coins;
 }
 
-int curr_goal(void)
-{
-    return goal_c;
-}
-
 /*---------------------------------------------------------------------------*/
 
 static void game_draw_balls(const struct s_file *fp,
@@ -488,7 +495,7 @@ static void game_draw_items(const struct s_file *fp, float t)
 
 static void game_draw_goals(const struct s_file *fp, const float *M, float t)
 {
-    if (goal_c == 0)
+    if (goal_e)
     {
         int zi;
 
@@ -966,7 +973,7 @@ static void game_update_view(float dt)
 
 static void game_update_time(float dt, int b)
 {
-    if (goal_c == 0 && goal_k < 1.0f)
+    if (goal_e && goal_k < 1.0f)
         goal_k += dt;
 
    /* The ticking clock. */
@@ -997,32 +1004,17 @@ static int game_update_state(int bt)
 
     if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS)))
     {
-        const char *sound = AUD_COIN;
-
         item_color(hp, c);
         part_burst(p, c);
 
         grow_init(fp, hp->t);
 
         if (hp->t == ITEM_COIN)
-        {
             coins += hp->n;
 
-            /* Check for goal open. */
-
-            if (goal_c > 0)
-            {
-                goal_c -= hp->n;
-                if (goal_c <= 0)
-                {
-                    sound = AUD_SWITCH;
-                    goal_c = 0;
-                }
-            }
-        }
-        audio_play(sound, 1.f);
+        audio_play(AUD_COIN, 1.f);
 
-        /* Reset item type. */
+        /* Discard item. */
 
         hp->t = ITEM_NONE;
     }
@@ -1047,7 +1039,7 @@ static int game_update_state(int bt)
 
     /* Test for a goal. */
 
-    if (bt && goal_c == 0 && (zp = sol_goal_test(fp, p, 0)))
+    if (bt && goal_e && (zp = sol_goal_test(fp, p, 0)))
     {
         audio_play(AUD_GOAL, 1.0f);
         return GAME_GOAL;
@@ -1138,6 +1130,19 @@ int game_step(const float g[3], float dt, int bt)
 
 /*---------------------------------------------------------------------------*/
 
+void game_set_goal(void)
+{
+    audio_play(AUD_SWITCH, 1.0f);
+    goal_e = 1;
+}
+
+void game_clr_goal(void)
+{
+    goal_e = 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
 void game_set_x(int k)
 {
     input_set_x(-ANGLE_BOUND * k / JOY_MAX);
@@ -1276,3 +1281,17 @@ void game_fade(float d)
 }
 
 /*---------------------------------------------------------------------------*/
+
+const char *status_to_str(int s)
+{
+    switch (s)
+    {
+    case GAME_NONE:    return _("Aborted");
+    case GAME_TIME:    return _("Time-out");
+    case GAME_GOAL:    return _("Success");
+    case GAME_FALL:    return _("Fall-out");
+    default:           return _("Unknown");
+    }
+}
+
+/*---------------------------------------------------------------------------*/
index 4a4727c..408bf98 100644 (file)
@@ -3,8 +3,7 @@
 
 #include <stdio.h>
 
-#include "level.h"
-#include "mode.h"
+#include "lang.h"
 
 /*---------------------------------------------------------------------------*/
 
 
 /*---------------------------------------------------------------------------*/
 
-int   game_init(const struct level *, int, int);
+int   game_init(const char *, int, int);
 void  game_free(void);
 
 int   curr_clock(void);
 int   curr_coins(void);
-int   curr_goal(void);
 
 void  game_draw(int, float);
 int   game_step(const float[3], float, int);
 
+void  game_set_goal(void);
+void  game_clr_goal(void);
+
 void  game_set_ang(int, int);
 void  game_set_pos(int, int);
 void  game_set_x  (int);
@@ -70,4 +71,13 @@ int input_get(FILE *);
 
 /*---------------------------------------------------------------------------*/
 
+#define GAME_NONE 0
+#define GAME_TIME 1
+#define GAME_GOAL 2
+#define GAME_FALL 3
+
+const char *status_to_str(int);
+
+/*---------------------------------------------------------------------------*/
+
 #endif
index d218db7..c339d4a 100644 (file)
@@ -20,7 +20,7 @@
 #include "hud.h"
 #include "gui.h"
 #include "game.h"
-#include "levels.h"
+#include "progress.h"
 #include "config.h"
 #include "audio.h"
 
 
 static int Lhud_id;
 static int Rhud_id;
-static int Rhud2_id;
 static int time_id;
 
 static int coin_id;
-static int coin2_id;
 static int ball_id;
 static int scor_id;
 static int goal_id;
@@ -66,13 +64,6 @@ void hud_init(void)
         gui_layout(Rhud_id, +1, -1);
     }
 
-    if ((Rhud2_id = gui_hstack(0)))
-    {
-        gui_label(Rhud2_id, _("Coins"), GUI_SML, 0, gui_wht, gui_wht);
-        coin2_id = gui_count(Rhud2_id, 100,   GUI_SML, GUI_NW);
-        gui_layout(Rhud2_id, +1, -1);
-    }
-
     if ((Lhud_id = gui_hstack(0)))
     {
         if ((id = gui_vstack(Lhud_id)))
@@ -112,16 +103,10 @@ void hud_free(void)
 
 void hud_paint(void)
 {
-    int mode = curr_lg()->mode;
-
-    if (mode == MODE_CHALLENGE)
+    if (curr_mode() == MODE_CHALLENGE)
         gui_paint(Lhud_id);
 
-    if (mode == MODE_PRACTICE)
-        gui_paint(Rhud2_id);
-    else
-        gui_paint(Rhud_id);
-
+    gui_paint(Rhud_id);
     gui_paint(time_id);
 
     if (config_get_d(CONFIG_FPS))
@@ -133,14 +118,11 @@ void hud_paint(void)
 
 void hud_update(int pulse)
 {
-    const struct level_game *lg = curr_lg();
-
     int clock = curr_clock();
     int coins = curr_coins();
     int goal  = curr_goal();
-    int balls = lg->balls;
-    int score = lg->score;
-    int mode  = lg->mode;
+    int balls = curr_balls();
+    int score = curr_score();
 
     int c_id;
     int last;
@@ -160,7 +142,7 @@ void hud_update(int pulse)
     {
         gui_set_clock(time_id, clock);
 
-        if (last > clock && pulse && mode != MODE_PRACTICE)
+        if (last > clock && pulse)
         {
             if (clock <= 1000 && (last / 100) > (clock / 100))
             {
@@ -177,7 +159,7 @@ void hud_update(int pulse)
 
     /* balls and score + select coin widget */
 
-    switch (mode)
+    switch (curr_mode())
     {
     case MODE_CHALLENGE:
         if (gui_value(ball_id) != balls) gui_set_count(ball_id, balls);
@@ -186,12 +168,8 @@ void hud_update(int pulse)
         c_id = coin_id;
         break;
 
-    case MODE_NORMAL:
-        c_id = coin_id;
-        break;
-
     default:
-        c_id = coin2_id;
+        c_id = coin_id;
         break;
     }
 
index 7ecb296..ae724d3 100644 (file)
 #include <string.h>
 #include <math.h>
 #include <errno.h>
+#include <assert.h>
 
+#include "solid.h"
 #include "config.h"
-#include "demo.h"
-#include "text.h"
 #include "level.h"
-#include "mode.h"
 #include "set.h"
-#include "solid.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -39,12 +37,8 @@ static void scan_dict(struct level *l, const struct s_file *fp)
 
         if (strcmp(k, "message") == 0)
             strncpy(l->message, v, MAXSTR);
-        else if (strcmp(k, "back") == 0)
-            strncpy(l->back, v, PATHMAX);
         else if (strcmp(k, "song") == 0)
             strncpy(l->song, v, PATHMAX);
-        else if (strcmp(k, "grad") == 0)
-            strncpy(l->grad, v, PATHMAX);
         else if (strcmp(k, "shot") == 0)
             strncpy(l->shot, v, PATHMAX);
         else if (strcmp(k, "goal") == 0)
@@ -149,8 +143,6 @@ void level_dump(const struct level *l)
            "goal hs:         %d %d %d\n"
            "coin hs:         %d %d %d\n"
            "message:         %s\n"
-           "background:      %s\n"
-           "gradient:        %s\n"
            "screenshot:      %s\n"
            "song:            %s\n",
            l->file,
@@ -168,144 +160,125 @@ void level_dump(const struct level *l)
            l->score.most_coins.coins[1],
            l->score.most_coins.coins[2],
            l->message,
-           l->back,
-           l->grad,
            l->shot,
            l->song);
 }
 
 /*---------------------------------------------------------------------------*/
 
-static unsigned int do_level_init = 1;
-
-int level_replay(const char *filename)
+int  level_exists(int i)
 {
-    return demo_replay_init(filename, curr_lg());
+    return set_level_exists(curr_set(), i);
 }
 
-int level_play(const struct level *l, int m)
+void level_open(int i)
 {
-    struct level_game *lg = curr_lg();
-
-    if (do_level_init)
-    {
-        memset(lg, 0, sizeof (struct level_game));
-
-        lg->mode  = m;
-        lg->level = l;
-        lg->balls = 3;
-    }
-
-    lg->goal = (lg->mode == MODE_PRACTICE) ? 0 : lg->level->goal;
-    lg->time = (lg->mode == MODE_PRACTICE) ? 0 : lg->level->time;
-
-    /* Clear other fields. */
-
-    lg->status = GAME_NONE;
-    lg->coins = 0;
-    lg->timer = lg->time;
-    lg->coin_rank = lg->goal_rank = lg->time_rank =
-        lg->score_rank = lg->times_rank = 3;
-
-    lg->win = lg->dead = lg->unlock = 0;
-    lg->next_level = NULL;
-    lg->bonus = 0;
-
-    return demo_play_init(USER_REPLAY_FILE, lg->level, lg);
+    if (level_exists(i))
+        get_level(i)->is_locked = 0;
 }
 
-void level_stat(int status, int clock, int coins)
+int  level_opened(int i)
 {
-    struct level_game *lg = curr_lg();
-
-    int mode = lg->mode;
-    int timer = (mode == MODE_PRACTICE) ? clock : lg->time - clock;
-
-    char player[MAXNAM];
-
-    config_get_s(CONFIG_PLAYER, player, MAXNAM);
-
-    lg->status = status;
-    lg->coins = coins;
-    lg->timer = timer;
-
-    if (mode == MODE_CHALLENGE)
-    {
-        /* sum time */
-        lg->times += timer;
+    return level_exists(i) && !get_level(i)->is_locked;
+}
 
-        /* sum coins an earn extra balls */
-        if (status == GAME_GOAL || lg->level->is_bonus)
-        {
-            lg->balls += count_extra_balls(lg->score, coins);
-            lg->score += coins;
-        }
+void level_complete(int i)
+{
+    if (level_exists(i))
+        get_level(i)->is_completed = 1;
+}
 
-        /* lose ball and game */
-        else
-        {
-            lg->dead = (lg->balls <= 0);
-            lg->balls--;
-        }
-    }
+int  level_completed(int i)
+{
+    return level_exists(i) && get_level(i)->is_completed;
+}
 
-    set_finish_level(lg, player);
+int  level_time (int i)
+{
+    assert(level_exists(i));
+    return get_level(i)->time;
+}
 
-    demo_play_stat(lg);
+int  level_goal (int i)
+{
+    assert(level_exists(i));
+    return get_level(i)->goal;
 }
 
-void level_stop(void)
+int  level_bonus(int i)
 {
-    demo_play_stop();
-    do_level_init = 1;
+    return level_exists(i) && get_level(i)->is_bonus;
 }
 
-int level_next(void)
+const char *level_shot(int i)
 {
-    struct level_game *lg = curr_lg();
+    return level_exists(i) ? get_level(i)->shot : NULL;
+}
 
-    level_stop();
-    lg->level = lg->next_level;
-    do_level_init = 0;
+const char *level_file(int i)
+{
+    return level_exists(i) ? get_level(i)->file : NULL;
+}
 
-    return level_play(lg->level, lg->mode);
+const char *level_repr(int i)
+{
+    return level_exists(i) ? get_level(i)->repr : NULL;
 }
 
-int level_same(void)
+const char *level_msg(int i)
 {
-    level_stop();
-    do_level_init = 0;
-    return level_play(curr_lg()->level, curr_lg()->mode);
+    if (level_exists(i) && strlen(get_level(i)->message) > 0)
+        return _(get_level(i)->message);
+
+    return NULL;
 }
 
 /*---------------------------------------------------------------------------*/
 
-int count_extra_balls(int old_score, int coins)
+int level_score_update(int level,
+                       int timer,
+                       int coins,
+                       int *time_rank,
+                       int *goal_rank,
+                       int *coin_rank)
 {
-    return ((old_score % 100) + coins) / 100;
-}
+    struct level *l = get_level(level);
+    char player[MAXSTR] = "";
 
-void level_update_player_name(void)
-{
-    char player[MAXNAM];
+    config_get_s(CONFIG_PLAYER, player, MAXSTR);
 
-    config_get_s(CONFIG_PLAYER, player, MAXNAM);
+    if (time_rank)
+        *time_rank = score_time_insert(&l->score.best_times,
+                                       player, timer, coins);
 
-    score_change_name(curr_lg(), player);
-}
+    if (goal_rank)
+        *goal_rank = score_time_insert(&l->score.unlock_goal,
+                                       player, timer, coins);
 
-/*---------------------------------------------------------------------------*/
+    if (coin_rank)
+        *coin_rank = score_coin_insert(&l->score.most_coins,
+                                       player, timer, coins);
 
-const char *status_to_str(int m)
+    if ((time_rank && *time_rank < 3) ||
+        (goal_rank && *goal_rank < 3) ||
+        (coin_rank && *coin_rank < 3))
+        return 1;
+    else
+        return 0;
+}
+
+void level_rename_player(int level,
+                         int time_rank,
+                         int goal_rank,
+                         int coin_rank,
+                         const char *player)
 {
-    switch (m)
-    {
-    case GAME_NONE:    return _("Aborted");
-    case GAME_TIME:    return _("Time-out");
-    case GAME_GOAL:    return _("Success");
-    case GAME_FALL:    return _("Fall-out");
-    default:           return _("Unknown");
-    }
+    struct level *l = get_level(level);
+
+    strncpy(l->score.best_times.player [time_rank], player, MAXNAM);
+    strncpy(l->score.unlock_goal.player[goal_rank], player, MAXNAM);
+    strncpy(l->score.most_coins.player [coin_rank], player, MAXNAM);
 }
 
 /*---------------------------------------------------------------------------*/
+
index 92f971a..c5d9b5e 100644 (file)
@@ -3,15 +3,15 @@
 
 #include "base_config.h"
 #include "score.h"
-#include "levels.h"
+#include "progress.h"
 
 /*---------------------------------------------------------------------------*/
 
 struct level
 {
+    /* TODO: turn into an internal structure. */
+
     char file[PATHMAX];
-    char back[PATHMAX];
-    char grad[PATHMAX];
     char shot[PATHMAX];
     char song[PATHMAX];
 
@@ -48,26 +48,30 @@ struct level
 int  level_load(const char *, struct level *);
 void level_dump(const struct level *);
 
-int  level_replay(const char *);
-int  level_play(const struct level *, int);
-void level_stat(int, int, int);
-void level_stop(void);
-int  level_next(void);
-int  level_same(void);
-
 /*---------------------------------------------------------------------------*/
 
-int count_extra_balls(int, int);
+int  level_exists(int);
+
+void level_open  (int);
+int  level_opened(int);
 
-void level_update_player_name(void);
+void level_complete (int);
+int  level_completed(int);
+
+int  level_time(int);
+int  level_goal(int);
+int  level_bonus(int);
+
+const char *level_shot(int);
+const char *level_file(int);
+const char *level_repr(int);
+const char *level_msg (int);
 
 /*---------------------------------------------------------------------------*/
 
-#define GAME_NONE 0     /* No event (or aborted) */
-#define GAME_TIME 1     /* Time's up */
-#define GAME_GOAL 2     /* Goal reached */
-#define GAME_FALL 3     /* Fall out */
+int  level_score_update (int, int, int, int *, int *, int *);
+void level_rename_player(int, int, int, int, const char *);
 
-const char *status_to_str(int);
+/*---------------------------------------------------------------------------*/
 
 #endif
diff --git a/ball/levels.c b/ball/levels.c
deleted file mode 100644 (file)
index ce40b1f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2003 Robert Kooima
- *
- * NEVERBALL is  free software; you can redistribute  it and/or modify
- * it under the  terms of the GNU General  Public License as published
- * by the Free  Software Foundation; either version 2  of the License,
- * or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
- * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
- * General Public License for more details.
- */
-
-#include "levels.h"
-
-/*---------------------------------------------------------------------------*/
-
-/* Currently playing level. */
-static struct level_game current_level_game;
-
-struct level_game *curr_lg(void)
-{
-    return &current_level_game;
-}
-
-/*---------------------------------------------------------------------------*/
diff --git a/ball/levels.h b/ball/levels.h
deleted file mode 100644 (file)
index 6b0efbd..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef LEVELS_H
-#define LEVELS_H
-
-/*---------------------------------------------------------------------------*/
-
-struct level_game
-{
-    int mode;          /* game mode */
-    const struct level *level; /* the level played */
-
-    int goal;          /* coins needed */
-    int time;          /* time limit */
-
-    /* MODE_CHALLENGE only */
-    int score;         /* coin total */
-    int balls;         /* live count */
-    int times;         /* time total */
-
-    int   bonus;       /* is a bonus level message needed? */
-    char *bonus_repr;  /* representation of the unlocked bonus level */
-
-    /* Once a level is finished */
-    int status;        /* status ending */
-    int coins;         /* coins collected */
-    int timer;         /* time elapsed */
-
-    /* rank = 3  => unclassed */
-    int coin_rank;     /* rank in the level high-scores */
-    int goal_rank;     /* rank in the level high-scores */
-    int time_rank;     /* rank in the level high-scores */
-    int score_rank;    /* rank in the set high-scores */
-    int times_rank;    /* rank in the set high-scores */
-
-    /* What about the game and the set? */
-    int dead;          /* Is the game over and lost? */
-    int win;           /* Is the game over and win? */
-    int unlock;        /* Is the next level newly unlocked */
-    const struct level *next_level; /* next level (NULL no next level) */
-};
-
-struct level_game *curr_lg(void);
-
-/*---------------------------------------------------------------------------*/
-
-#endif
index dd5dea9..e75c303 100644 (file)
@@ -24,7 +24,7 @@
 #include "image.h"
 #include "audio.h"
 #include "demo.h"
-#include "levels.h"
+#include "progress.h"
 #include "game.h"
 #include "gui.h"
 #include "set.h"
@@ -377,7 +377,7 @@ int main(int argc, char *argv[])
 
     if (display_info)
     {
-        if (!level_replay(demo_path))
+        if (!progress_replay(demo_path))
         {
             fprintf(stderr, L_("Replay file '%s': %s\n"), demo_path,
                     errno ?  strerror(errno) : L_("Not a replay file"));
@@ -433,9 +433,8 @@ int main(int argc, char *argv[])
 
     /* Initialise demo playback. */
 
-    if (replay_demo)
+    if (replay_demo && progress_replay(demo_path))
     {
-        level_replay(demo_path);
         demo_play_goto(1);
         goto_state(&st_demo_play);
     }
diff --git a/ball/mode.c b/ball/mode.c
deleted file mode 100644 (file)
index fe8382c..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2007 Robert Kooima
- *
- * NEVERBALL is  free software; you can redistribute  it and/or modify
- * it under the  terms of the GNU General  Public License as published
- * by the Free  Software Foundation; either version 2  of the License,
- * or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
- * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
- * General Public License for more details.
- */
-
-#include "mode.h"
-#include "lang.h"
-
-/*---------------------------------------------------------------------------*/
-
-static int mode = MODE_NORMAL;
-
-/*---------------------------------------------------------------------------*/
-
-int curr_mode(void)
-{
-    return mode;
-}
-
-void mode_set(int new_mode)
-{
-    mode = new_mode;
-}
-
-const char *mode_to_str(int m, int l)
-{
-    switch (m)
-    {
-    case MODE_CHALLENGE: return l ? _("Challenge Mode") : _("Challenge");
-    case MODE_NORMAL:    return l ? _("Normal Mode")    : _("Normal");
-    case MODE_PRACTICE:  return l ? _("Practice Mode")  : _("Practice");
-    default:             return l ? _("Unknown Mode")   : _("Unknown");
-    }
-}
-
-/*---------------------------------------------------------------------------*/
diff --git a/ball/mode.h b/ball/mode.h
deleted file mode 100644 (file)
index bfc0bd5..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef MODE_H
-#define MODE_H
-
-/*---------------------------------------------------------------------------*/
-
-#define MODE_CHALLENGE  1
-#define MODE_NORMAL     2
-#define MODE_PRACTICE   3
-
-#define MODE_COUNT 3
-
-void mode_set(int);
-int  curr_mode(void);
-
-const char *mode_to_str(int, int);
-
-/*---------------------------------------------------------------------------*/
-
-#endif
diff --git a/ball/progress.c b/ball/progress.c
new file mode 100644 (file)
index 0000000..cccff48
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2003 Robert Kooima
+ *
+ * NEVERBALL is  free software; you can redistribute  it and/or modify
+ * it under the  terms of the GNU General  Public License as published
+ * by the Free  Software Foundation; either version 2  of the License,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
+ * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
+ * General Public License for more details.
+ */
+
+#include "progress.h"
+#include "config.h"
+#include "game.h"
+#include "demo.h"
+#include "level.h"
+#include "set.h"
+#include "lang.h"
+#include "score.h"
+
+/*---------------------------------------------------------------------------*/
+
+struct progress
+{
+    int balls;
+    int score;
+    int times;
+};
+
+static int mode = MODE_NORMAL;
+
+static int level =  0;
+static int next  = -1;
+static int done  =  0;
+
+static int bonus =  0;
+
+static struct progress curr;
+static struct progress prev;
+
+/* Set stats. */
+
+static int score_rank = 3;
+static int times_rank = 3;
+
+/* Level stats. */
+
+static int status = GAME_NONE;
+
+static int coins = 0;
+static int timer = 0;
+
+static int goal   = 0; /* Current goal value. */
+static int goal_i = 0; /* Initial goal value. */
+
+static int time_rank = 3;
+static int goal_rank = 3;
+static int coin_rank = 3;
+
+/*---------------------------------------------------------------------------*/
+
+void progress_init(int m)
+{
+    mode  = m;
+    bonus = 0;
+
+    curr.balls = 2;
+    curr.score = 0;
+    curr.times = 0;
+
+    prev = curr;
+
+    score_rank = times_rank = 3;
+
+    done  = 0;
+}
+
+int  progress_play(int i)
+{
+    if (level_opened(i) || config_cheat())
+    {
+        level = i;
+
+        next   = -1;
+        status = GAME_NONE;
+        coins  = 0;
+        timer  = 0;
+        goal   = goal_i = level_goal(level);
+
+        prev = curr;
+
+        time_rank = goal_rank = coin_rank = 3;
+
+        if (demo_play_init(USER_REPLAY_FILE, get_level(level), mode,
+                           level_time(level), level_goal(level),
+                           (mode != MODE_CHALLENGE && level_completed(level) &&
+                            config_get_d(CONFIG_LOCK_GOALS) == 0) || goal == 0,
+                           curr.score, curr.balls, curr.times))
+        {
+            return 1;
+        }
+        else
+        {
+            demo_play_stop();
+            return 0;
+        }
+    }
+    return 0;
+}
+
+void progress_step(void)
+{
+    if (goal > 0)
+    {
+        goal = goal_i - curr_coins();
+
+        if (goal <= 0)
+        {
+            game_set_goal();
+            goal = 0;
+        }
+    }
+}
+
+void progress_stat(int s)
+{
+    int i, dirty = 0;
+
+    status = s;
+
+    coins = curr_coins();
+    timer = level_time(level) - curr_clock();
+
+    switch (status)
+    {
+    case GAME_GOAL:
+
+        for (i = curr.score + 1; i <= curr.score + coins; i++)
+            if (progress_reward_ball(i))
+                curr.balls++;
+
+        curr.score += coins;
+        curr.times += timer;
+
+        dirty = level_score_update(level, timer, coins,
+                                   &time_rank,
+                                   goal == 0 ? &goal_rank : NULL,
+                                   &coin_rank);
+
+        if (!level_completed(level))
+        {
+            level_complete(level);
+            dirty = 1;
+        }
+
+        /* Compute next level. */
+
+        if (mode == MODE_CHALLENGE)
+        {
+            for (next = level + 1; level_bonus(next); next++)
+                if (!level_opened(next))
+                {
+                    level_open(next);
+                    dirty = 1;
+                    bonus++;
+                }
+        }
+        else
+        {
+            for (next = level + 1;
+                 level_bonus(next) && !level_opened(next);
+                 next++)
+                /* Do nothing. */;
+        }
+
+        /* Complete the set or open next level. */
+
+        if (!level_exists(next))
+        {
+            if (mode == MODE_CHALLENGE)
+            {
+                dirty = set_score_update(curr.times, curr.score,
+                                         &score_rank, &times_rank);
+                done  = 1;
+            }
+        }
+        else
+        {
+            level_open(next);
+            dirty = 1;
+        }
+
+        break;
+
+    case GAME_FALL:
+        /* Fall through. */
+
+    case GAME_TIME:
+        for (next = level + 1;
+             level_exists(next) && !level_opened(next);
+             next++)
+            /* Do nothing. */;
+
+        curr.balls--;
+        break;
+    }
+
+    if (dirty)
+        set_store_hs();
+
+    demo_play_stat(status, coins, timer);
+}
+
+void progress_stop(void)
+{
+    demo_play_stop();
+}
+
+void progress_exit(int s)
+{
+    progress_stat(s);
+    progress_stop();
+}
+
+int  progress_replay(const char *filename)
+{
+    if (demo_replay_init(filename, &goal, &mode,
+                         &curr.balls,
+                         &curr.score,
+                         &curr.times))
+    {
+        goal_i = goal;
+        return 1;
+    }
+    else
+        return 0;
+}
+
+int  progress_next_avail(void)
+{
+    if (mode == MODE_CHALLENGE)
+        return status == GAME_GOAL && level_exists(next);
+    else
+        return level_opened(next);
+}
+
+int  progress_same_avail(void)
+{
+    switch (status)
+    {
+    case GAME_NONE:
+        return mode != MODE_CHALLENGE;
+
+    default:
+        if (mode == MODE_CHALLENGE)
+            return !progress_dead();
+        else
+            return 1;
+    }
+}
+
+int  progress_next(void)
+{
+    progress_stop();
+    return progress_play(next);
+}
+
+int  progress_same(void)
+{
+    progress_stop();
+    curr = status == GAME_GOAL ? prev : curr;
+    return progress_play(level);
+}
+
+int  progress_dead(void)
+{
+    return mode == MODE_CHALLENGE ? curr.balls < 0 : 0;
+}
+
+int  progress_done(void)
+{
+    return done;
+}
+
+int  progress_lvl_high(void)
+{
+    return time_rank < 3 || goal_rank < 3 || coin_rank < 3;
+}
+
+int  progress_set_high(void)
+{
+    return score_rank < 3 || times_rank < 3;
+}
+
+void progress_rename(int set_only)
+{
+    char player[MAXNAM] = "";
+
+    config_get_s(CONFIG_PLAYER, player, sizeof (player));
+
+    if (set_only)
+    {
+        set_rename_player(score_rank, times_rank, player);
+    }
+    else
+    {
+        level_rename_player(level, time_rank, goal_rank, coin_rank, player);
+
+        if (progress_done())
+            set_rename_player(score_rank, times_rank, player);
+    }
+
+    set_store_hs();
+}
+
+int  progress_reward_ball(int s)
+{
+    return s > 0 && s % 100 == 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+int curr_level(void) { return level;      }
+int curr_balls(void) { return curr.balls; }
+int curr_score(void) { return curr.score; }
+int curr_mode (void) { return mode;       }
+int curr_bonus(void) { return bonus;      }
+int curr_goal (void) { return goal;       }
+
+int progress_time_rank(void) { return time_rank; }
+int progress_goal_rank(void) { return goal_rank; }
+int progress_coin_rank(void) { return coin_rank; }
+
+int progress_times_rank(void) { return times_rank; }
+int progress_score_rank(void) { return score_rank; }
+
+/*---------------------------------------------------------------------------*/
+
+const char *mode_to_str(int m, int l)
+{
+    switch (m)
+    {
+    case MODE_CHALLENGE: return l ? _("Challenge Mode") : _("Challenge");
+    case MODE_NORMAL:    return l ? _("Normal Mode")    : _("Normal");
+    default:             return l ? _("Unknown Mode")   : _("Unknown");
+    }
+}
+
+/*---------------------------------------------------------------------------*/
diff --git a/ball/progress.h b/ball/progress.h
new file mode 100644 (file)
index 0000000..c1558c5
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef PROGRESS_H
+#define PROGRESS_H
+
+/*---------------------------------------------------------------------------*/
+
+void progress_init(int);
+
+int  progress_play(int level);
+void progress_step(void);
+void progress_stat(int status);
+void progress_stop(void);
+void progress_exit(int status);
+
+int  progress_next_avail(void);
+int  progress_next(void);
+int  progress_same_avail(void);
+int  progress_same(void);
+
+void progress_rename(int);
+
+int  progress_replay(const char *);
+
+int  progress_dead(void);
+int  progress_done(void);
+
+int  progress_lvl_high(void);
+int  progress_set_high(void);
+
+int  curr_level(void);
+int  curr_balls(void);
+int  curr_score(void);
+int  curr_mode (void);
+int  curr_bonus(void);
+int  curr_goal (void);
+
+int  progress_time_rank(void);
+int  progress_goal_rank(void);
+int  progress_coin_rank(void);
+
+int  progress_times_rank(void);
+int  progress_score_rank(void);
+
+int  progress_reward_ball(int);
+
+/*---------------------------------------------------------------------------*/
+
+#define MODE_CHALLENGE 1
+#define MODE_NORMAL    2
+
+#define MODE_COUNT 2
+
+const char *mode_to_str(int, int);
+
+/*---------------------------------------------------------------------------*/
+
+#endif
index 36953e4..2897e72 100644 (file)
@@ -38,8 +38,8 @@ struct set
 
     char user_scores[PATHMAX]; /* User high-score file       */
 
-    struct score time_score;   /* Challenge score            */
     struct score coin_score;   /* Challenge score            */
+    struct score time_score;   /* Challenge score            */
 
     /* Level info */
 
@@ -62,11 +62,10 @@ static void put_score(FILE *fp, const struct score *s)
     int j;
 
     for (j = 0; j < NSCORE; j++)
-       fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
+        fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
 }
 
-/* Store the score of the set. */
-static void set_store_hs(void)
+void set_store_hs(void)
 {
     const struct set *s = &set_v[set];
     FILE *fout;
@@ -166,8 +165,8 @@ static void set_load_hs(void)
         {
             l = &level_v[i];
             res = get_score(fin, &l->score.best_times) &&
-                  get_score(fin, &l->score.unlock_goal) &&
-                  get_score(fin, &l->score.most_coins);
+                get_score(fin, &l->score.unlock_goal) &&
+                get_score(fin, &l->score.most_coins);
         }
 
         fclose(fin);
@@ -385,189 +384,38 @@ int curr_set(void)
     return set;
 }
 
-const struct level *get_level(int i)
+struct level *get_level(int i)
 {
     return (i >= 0 && i < set_v[set].count) ? &level_v[i] : NULL;
 }
 
 /*---------------------------------------------------------------------------*/
 
-/* Update the level score rank according to coins and timer. */
-static int level_score_update(struct level_game *lg, const char *player)
-{
-    int timer = lg->timer;
-    int coins = lg->coins;
-    struct level *l = &level_v[lg->level->number];
-
-    lg->time_rank = score_time_insert(&l->score.best_times,
-                                      player, timer, coins);
-
-    if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
-        lg->goal_rank = score_time_insert(&l->score.unlock_goal,
-                                          player, timer, coins);
-    else
-        lg->goal_rank = 3;
-
-    lg->coin_rank = score_coin_insert(&l->score.most_coins,
-                                      player, timer, coins);
-
-    return (lg->time_rank < 3 || lg->goal_rank < 3 || lg->coin_rank < 3);
-}
-
-/* Update the set score rank according to score and times. */
-static int set_score_update(struct level_game *lg, const char *player)
+int set_score_update(int timer, int coins, int *score_rank, int *times_rank)
 {
-    int timer = lg->times;
-    int coins = lg->score;
     struct set *s = &set_v[set];
+    char player[MAXSTR] = "";
 
-    lg->score_rank = score_time_insert(&s->time_score, player, timer, coins);
-    lg->times_rank = score_time_insert(&s->coin_score, player, timer, coins);
-
-    return (lg->score_rank < 3 || lg->times_rank < 3);
-}
-
-/* Update the player name for set and level high-score. */
-void score_change_name(struct level_game *lg, const char *player)
-{
-    struct set   *s = &set_v[set];
-    struct level *l = &level_v[lg->level->number];
-
-    strncpy(l->score.best_times.player [lg->time_rank], player, MAXNAM);
-    strncpy(l->score.unlock_goal.player[lg->goal_rank], player, MAXNAM);
-    strncpy(l->score.most_coins.player [lg->coin_rank], player, MAXNAM);
-
-    strncpy(s->coin_score.player[lg->score_rank], player, MAXNAM);
-    strncpy(s->time_score.player[lg->times_rank], player, MAXNAM);
+    config_get_s(CONFIG_PLAYER, player, MAXSTR);
 
-    set_store_hs();
-}
-
-static struct level *next_level(int i)
-{
-    return set_level_exists(set, i + 1) ? &level_v[i + 1] : NULL;
-}
+    if (score_rank)
+        *score_rank = score_coin_insert(&s->coin_score, player, timer, coins);
 
-static struct level *next_normal_level(int i)
-{
-    for (i++; i < set_v[set].count; i++)
-        if (!level_v[i].is_bonus)
-            return &level_v[i];
+    if (times_rank)
+        *times_rank = score_time_insert(&s->time_score, player, timer, coins);
 
-    return NULL;
+    if ((score_rank && *score_rank < 3) || (times_rank && *times_rank < 3))
+        return 1;
+    else
+        return 0;
 }
 
-/*---------------------------------------------------------------------------*/
-
-void set_finish_level(struct level_game *lg, const char *player)
+void set_rename_player(int score_rank, int times_rank, const char *player)
 {
     struct set *s = &set_v[set];
-    int ln = lg->level->number;      /* Current level number       */
-    struct level *cl = &level_v[ln]; /* Current level              */
-    struct level *nl = NULL;         /* Next level                 */
-    int dirty = 0;                   /* Should the score be saved? */
-
-    assert(s == cl->set);
-
-    /* if no set, no next level */
-    if (s == NULL)
-    {
-        /* if no set, return */
-        lg->next_level = NULL;
-        return;
-    }
-
-    /* On level completed */
-    if (lg->status == GAME_GOAL)
-    {
-        /* Update level scores */
-        dirty = level_score_update(lg, player);
-
-        /* Complete the level */
-        if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
-        {
-            /* Complete the level */
-            if (!cl->is_completed)
-            {
-                cl->is_completed = 1;
-                dirty = 1;
-            }
-        }
-    }
-
-    /* On goal reached */
-    if (lg->status == GAME_GOAL)
-    {
-        /* Identify the following level */
-
-        nl = next_level(ln);
-
-        if (nl != NULL)
-        {
-            /* Skip bonuses if unlocked in any mode */
-
-            if (nl->is_bonus)
-            {
-                if (lg->mode == MODE_CHALLENGE && nl->is_locked)
-                {
-                    nl->is_locked = 0;
-
-                    lg->bonus = 1;
-                    lg->bonus_repr = nl->repr;
-                }
-
-                nl = next_normal_level(nl->number);
-
-                if (nl == NULL && lg->mode == MODE_CHALLENGE)
-                {
-                    lg->win = 1;
-                }
-            }
-        }
-        else if (lg->mode == MODE_CHALLENGE)
-            lg->win = 1;
-    }
-    else if (cl->is_bonus || lg->mode != MODE_CHALLENGE)
-    {
-        /* On fail, identify the next level (only in bonus for challenge) */
-        nl = next_normal_level(ln);
-        /* Next level may be unavailable */
-        if (!cl->is_bonus && nl != NULL && nl->is_locked)
-            nl = NULL;
-        /* Fail a bonus level but win the set! */
-        else if (nl == NULL && lg->mode == MODE_CHALLENGE)
-            lg->win = 1;
-    }
-
-    /* Win ! */
-    if (lg->win)
-    {
-        /* update set score */
-        set_score_update(lg, player);
-        /* unlock all levels */
-        set_cheat();
-        dirty = 1;
-    }
-
-    /* unlock the next level if needed */
-    if (nl != NULL && nl->is_locked)
-    {
-        if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
-        {
-            lg->unlock = 1;
-            nl->is_locked = 0;
-            dirty = 1;
-        }
-        else
-            nl = NULL;
-    }
-
-    /* got the next level */
-    lg->next_level = nl;
 
-    /* Update file */
-    if (dirty)
-        set_store_hs();
+    strncpy(s->coin_score.player[score_rank], player, MAXNAM);
+    strncpy(s->time_score.player[times_rank], player, MAXNAM);
 }
 
 /*---------------------------------------------------------------------------*/
@@ -588,7 +436,7 @@ void level_snap(int i)
 
     /* Initialize the game for a snapshot. */
 
-    if (game_init(&level_v[i], 0, 0))
+    if (game_init(level_v[i].file, 0, 1))
     {
         /* Render the level and grab the screen. */
 
index d06676a..e813780 100644 (file)
@@ -27,14 +27,15 @@ const char         *set_shot(int);
 const struct score *set_time_score(int);
 const struct score *set_coin_score(int);
 
+int  set_score_update (int, int, int *, int *);
+void set_rename_player(int, int, const char *);
+
+void set_store_hs(void);
 
 /*---------------------------------------------------------------------------*/
 
 int set_level_exists(int, int);
-const struct level *get_level(int);
-
-void set_finish_level(struct level_game *, const char *);
-void score_change_name(struct level_game *, const char *);
+struct level *get_level(int);
 
 void level_snap(int);
 void set_cheat(void);
index b615c2a..7f60069 100644 (file)
@@ -19,7 +19,7 @@
 #include "set.h"
 #include "game.h"
 #include "demo.h"
-#include "levels.h"
+#include "progress.h"
 #include "audio.h"
 #include "solid.h"
 #include "config.h"
@@ -66,12 +66,13 @@ static int demo_action(int i)
         break;
 
     default:
-        if (level_replay(demo_get(i)->filename))
+        if (progress_replay(demo_get(i)->filename))
         {
             last_viewed = i;
             demo_play_goto(0);
             return goto_state(&st_demo_play);
         }
+        break;
     }
     return 1;
 }
@@ -375,6 +376,8 @@ static void demo_play_timer(int id, float dt)
         demo_paused = 0;
         goto_state(&st_demo_end);
     }
+    else
+        progress_step();
 }
 
 static int demo_play_keybd(int c, int d)
@@ -433,7 +436,7 @@ static int demo_end_action(int i)
         return 0;
     case DEMO_REPLAY:
         demo_replay_stop(0);
-        level_replay(curr_demo_replay()->filename);
+        progress_replay(curr_demo_replay()->filename);
         return goto_state(&st_demo_play);
     case DEMO_CONTINUE:
         return goto_state(&st_demo_play);
index fc5f945..a98901a 100644 (file)
@@ -19,7 +19,7 @@
 #include "game.h"
 #include "util.h"
 #include "demo.h"
-#include "levels.h"
+#include "progress.h"
 #include "audio.h"
 #include "config.h"
 #include "st_shared.h"
 /*---------------------------------------------------------------------------*/
 
 #define DONE_OK   1
-#define DONE_NAME 2
 
 /* Bread crumbs. */
 
 static int new_name;
+static int resume;
 
 static int done_action(int i)
 {
@@ -46,9 +46,16 @@ static int done_action(int i)
     case DONE_OK:
         return goto_state(&st_start);
 
-    case DONE_NAME:
+    case GUI_NAME:
         new_name = 1;
         return goto_name(&st_done, &st_done, 0);
+
+    case GUI_MOST_COINS:
+    case GUI_BEST_TIMES:
+    case GUI_UNLOCK_GOAL:
+        gui_score_set(i);
+        resume = 1;
+        return goto_state(&st_done);
     }
     return 1;
 }
@@ -60,11 +67,11 @@ static int done_enter(void)
 
     int id, jd;
 
-    int high = (curr_lg()->times_rank < 3) || (curr_lg()->score_rank < 3);
+    int high = progress_set_high();
 
     if (new_name)
     {
-        level_update_player_name();
+        progress_rename(1);
         new_name = 0;
     }
 
@@ -79,34 +86,37 @@ static int done_enter(void)
 
         gui_space(id);
 
-        if ((jd = gui_harray(id)))
-        {
-            gui_most_coins(jd, 1);
-            gui_best_times(jd, 1);
-        }
+        if ((jd = gui_hstack(id)))
+            gui_score_board(jd, 1, high);
 
         gui_space(id);
 
-        if ((jd = gui_harray(id)))
-        {
-            /* FIXME, I'm ugly. */
-
-            if (high)
-               gui_state(jd, _("Change Player Name"), GUI_SML, DONE_NAME, 0);
+        gui_start(id, _("Select Level"), GUI_SML, DONE_OK, 0);
 
-            gui_start(jd, _("OK"), GUI_SML, DONE_OK, 0);
-        }
+        if (!resume)
+            gui_pulse(gid, 1.2f);
 
         gui_layout(id, 0, 0);
-        gui_pulse(gid, 1.2f);
     }
 
-    set_best_times(set_time_score(curr_set()), curr_lg()->times_rank, 0);
-    set_most_coins(set_coin_score(curr_set()), curr_lg()->score_rank);
+    set_score_board(set_coin_score(curr_set()), progress_score_rank(),
+                    set_time_score(curr_set()), progress_times_rank(),
+                    set_time_score(curr_set()), progress_times_rank());
+
+    /* Reset hack. */
+    resume = 0;
 
     return id;
 }
 
+static int done_keybd(int c, int d)
+{
+    if (d && config_tst_d(CONFIG_KEY_SCORE_NEXT, c))
+        return done_action(gui_score_next(gui_score_get()));
+
+    return 1;
+}
+
 static int done_buttn(int b, int d)
 {
     if (d)
@@ -130,7 +140,7 @@ struct state st_done = {
     shared_stick,
     shared_angle,
     shared_click,
-    NULL,
+    done_keybd,
     done_buttn,
     1, 0
 };
index 1c55b78..d715261 100644 (file)
@@ -15,7 +15,7 @@
 #include "gui.h"
 #include "game.h"
 #include "util.h"
-#include "levels.h"
+#include "progress.h"
 #include "audio.h"
 #include "config.h"
 #include "demo.h"
@@ -35,7 +35,7 @@
 #define FALL_OUT_BACK 4
 #define FALL_OUT_OVER 5
 
-static int be_back_soon;
+static int resume;
 
 static int fall_out_action(int i)
 {
@@ -47,22 +47,24 @@ static int fall_out_action(int i)
         /* Fall through. */
 
     case FALL_OUT_OVER:
-        level_stop();
+        progress_stop();
         return goto_state(&st_over);
 
     case FALL_OUT_SAVE:
-        be_back_soon = 1;
+        resume = 1;
 
-        level_stop();
+        progress_stop();
         return goto_save(&st_fall_out, &st_fall_out);
 
     case FALL_OUT_NEXT:
-        level_next();
-        return goto_state(&st_level);
+        if (progress_next())
+            return goto_state(&st_level);
+        break;
 
     case FALL_OUT_SAME:
-        level_same();
-        return goto_state(&st_level);
+        if (progress_same())
+            return goto_state(&st_level);
+        break;
     }
 
     return 1;
@@ -72,10 +74,8 @@ static int fall_out_enter(void)
 {
     int id, jd, kd;
 
-    const struct level_game *lg = curr_lg();
-
     /* Reset hack. */
-    be_back_soon = 0;
+    resume = 0;
 
     if ((id = gui_vstack(0)))
     {
@@ -85,29 +85,17 @@ static int fall_out_enter(void)
 
         if ((jd = gui_harray(id)))
         {
-            int next_id = 0, retry_id = 0;
-
-            next_id = gui_maybe(jd, _("Next Level"), FALL_OUT_NEXT,
-                                lg->next_level != NULL);
-
-            if (lg->dead)
-            {
-                gui_start(jd, _("Game Over"), GUI_SML, FALL_OUT_OVER, 0);
-            }
-            else
-            {
-                retry_id = gui_state(jd, _("Retry Level"), GUI_SML,
-                                     FALL_OUT_SAME, 0);
-            }
+            if (progress_dead())
+                gui_start(jd, _("Exit"), GUI_SML, FALL_OUT_OVER, 0);
 
-            gui_maybe(jd, _("Save Replay"), FALL_OUT_SAVE, demo_saved());
+            if (progress_next_avail())
+                gui_start(jd, _("Next Level"),  GUI_SML, FALL_OUT_NEXT, 0);
 
-            /* Default is next if the next level is newly unlocked. */
+            if (progress_same_avail())
+                gui_start(jd, _("Retry Level"), GUI_SML, FALL_OUT_SAME, 0);
 
-            if (next_id && lg->unlock)
-                gui_focus(next_id);
-            else if (retry_id)
-                gui_focus(retry_id);
+            if (demo_saved())
+                gui_state(jd, _("Save Replay"), GUI_SML, FALL_OUT_SAVE, 0);
         }
 
         gui_space(id);
@@ -141,7 +129,7 @@ static int fall_out_keybd(int c, int d)
 {
     if (d)
     {
-        if (config_tst_d(CONFIG_KEY_RESTART, c) && !curr_lg()->dead)
+        if (config_tst_d(CONFIG_KEY_RESTART, c) && progress_same_avail())
             return fall_out_action(FALL_OUT_SAME);
     }
     return 1;
@@ -162,7 +150,7 @@ static int fall_out_buttn(int b, int d)
 static void fall_out_leave(int id)
 {
     /* HACK:  don't run animation if only "visiting" a state. */
-    st_fall_out.timer = be_back_soon ? shared_timer : fall_out_timer;
+    st_fall_out.timer = resume ? shared_timer : fall_out_timer;
 
     gui_delete(id);
 }
index 96f5f98..adc4316 100644 (file)
@@ -17,7 +17,7 @@
 #include "gui.h"
 #include "game.h"
 #include "util.h"
-#include "levels.h"
+#include "progress.h"
 #include "audio.h"
 #include "config.h"
 #include "demo.h"
@@ -38,8 +38,7 @@
 #define GOAL_SAVE 3
 #define GOAL_BACK 4
 #define GOAL_DONE 5
-#define GOAL_NAME 6
-#define GOAL_OVER 7
+#define GOAL_OVER 6
 
 static int balls_id;
 static int coins_id;
@@ -48,7 +47,7 @@ static int score_id;
 /* Bread crumbs. */
 
 static int new_name;
-static int be_back_soon;
+static int resume;
 
 static int goal_action(int i)
 {
@@ -60,33 +59,42 @@ static int goal_action(int i)
         /* Fall through. */
 
     case GOAL_OVER:
-        level_stop();
+        progress_stop();
         return goto_state(&st_over);
 
     case GOAL_SAVE:
-        be_back_soon = 1;
+        resume = 1;
 
-        level_stop();
+        progress_stop();
         return goto_save(&st_goal, &st_goal);
 
-    case GOAL_NAME:
+    case GUI_NAME:
         new_name = 1;
-        be_back_soon = 1;
+        resume = 1;
 
-        level_stop();
+        progress_stop();
         return goto_name(&st_goal, &st_goal, 0);
 
     case GOAL_DONE:
-        level_stop();
+        progress_stop();
         return goto_state(&st_done);
 
+    case GUI_MOST_COINS:
+    case GUI_BEST_TIMES:
+    case GUI_UNLOCK_GOAL:
+        gui_score_set(i);
+        resume = 1;
+        return goto_state(&st_goal);
+
     case GOAL_NEXT:
-        level_next();
-        return goto_state(&st_level);
+        if (progress_next())
+            return goto_state(&st_level);
+        break;
 
     case GOAL_SAME:
-        level_same();
-        return goto_state(&st_level);
+        if (progress_same())
+            return goto_state(&st_level);
+        break;
     }
 
     return 1;
@@ -96,23 +104,16 @@ static int goal_enter(void)
 {
     const char *s1 = _("New Record");
     const char *s2 = _("GOAL");
-    const char *s3 = _("Congratulations!");
 
     int id, jd, kd;
 
-    struct level_game *lg = curr_lg();
-    const struct level *l = lg->level;
-
-    int high;
-
-    high = (lg->time_rank < 3) || (lg->goal_rank < 3) || (lg->coin_rank < 3);
+    const struct level *l = get_level(curr_level());
 
-    /* Reset hack. */
-    be_back_soon = 0;
+    int high = progress_lvl_high();
 
     if (new_name)
     {
-        level_update_player_name();
+        progress_rename(0);
         new_name = 0;
     }
 
@@ -120,20 +121,6 @@ static int goal_enter(void)
     {
         int gid;
 
-        if (lg->mode == MODE_CHALLENGE && lg->bonus)
-        {
-            char buf[MAXSTR];
-
-            sprintf(buf, _("You have unlocked bonus level %s!"),
-                    lg->bonus_repr);
-
-            gid = gui_label(id, s3,  GUI_MED, GUI_ALL, gui_grn, gui_red);
-            gid = gui_label(id, buf, GUI_SML, GUI_ALL, gui_blu, gui_grn);
-
-            lg->bonus = 0;
-            lg->bonus_repr = NULL;
-        }
-
         if (high)
             gid = gui_label(id, s1, GUI_MED, GUI_ALL, gui_grn, gui_grn);
         else
@@ -141,99 +128,107 @@ static int goal_enter(void)
 
         gui_space(id);
 
-        if (lg->mode == MODE_CHALLENGE)
+        if (curr_mode() == MODE_CHALLENGE)
         {
-            int coins = lg->coins;
-            int score = lg->score - coins;
-            int balls = lg->balls - count_extra_balls(score, coins);
+            int coins, score, balls;
+            char msg[MAXSTR] = "";
+            int i;
+
+            /* Reverse-engineer initial score and balls. */
+
+            if (resume)
+            {
+                coins = 0;
+                score = curr_score();
+                balls = curr_balls();
+            }
+            else
+            {
+                coins = curr_coins();
+                score = curr_score() - coins;
+                balls = curr_balls();
+
+                for (i = curr_score(); i > score; i--)
+                    if (progress_reward_ball(i))
+                        balls--;
+            }
+
+            sprintf(msg, ngettext("%d new bonus level",
+                                  "%d new bonus levels",
+                                  curr_bonus()), curr_bonus());
 
             if ((jd = gui_hstack(id)))
             {
+
                 if ((kd = gui_harray(jd)))
                 {
-                    balls_id = gui_count(kd, 100, GUI_MED, GUI_RGT);
-                    gui_label(kd, _("Balls"), GUI_SML, GUI_LFT, gui_wht, gui_wht);
+                    balls_id = gui_count(kd, 100, GUI_MED, GUI_NE);
+                    gui_label(kd, _("Balls"), GUI_SML, 0, gui_wht, gui_wht);
                 }
                 if ((kd = gui_harray(jd)))
                 {
-                    score_id = gui_count(kd, 1000, GUI_MED, GUI_RGT);
-                    gui_label(kd, _("Score"), GUI_SML, GUI_LFT, gui_wht, gui_wht);
+                    score_id = gui_count(kd, 1000, GUI_MED, 0);
+                    gui_label(kd, _("Score"), GUI_SML, 0, gui_wht, gui_wht);
                 }
                 if ((kd = gui_harray(jd)))
                 {
-                    coins_id = gui_count(kd, 100, GUI_MED, GUI_RGT);
-                    gui_label(kd, _("Coins"), GUI_SML, GUI_LFT, gui_wht, gui_wht);
+                    coins_id = gui_count(kd, 100, GUI_MED, 0);
+                    gui_label(kd, _("Coins"), GUI_SML, GUI_NW, gui_wht, gui_wht);
                 }
 
                 gui_set_count(balls_id, balls);
                 gui_set_count(score_id, score);
                 gui_set_count(coins_id, coins);
+
             }
+
+            gui_label(id, msg, GUI_SML, GUI_BOT, 0, 0);
+
+            gui_space(id);
         }
         else
         {
             balls_id = score_id = coins_id = 0;
         }
 
-        gui_space(id);
-
-        if ((jd = gui_harray(id)))
-        {
-            gui_most_coins(jd, 1);
-            gui_best_times(jd, 1);
-        }
+        if ((jd = gui_hstack(id)))
+            gui_score_board(jd, 1, high);
 
         gui_space(id);
 
         if ((jd = gui_harray(id)))
         {
-            int next_id = 0, retry_id = 0;
-
-            if (lg->win)
+            if (progress_done())
                 gui_start(jd, _("Finish"), GUI_SML, GOAL_DONE, 0);
-            else
-                next_id = gui_maybe(jd, _("Next Level"),  GOAL_NEXT,
-                                    lg->next_level != NULL);
-
-            if (lg->dead)
-                gui_start(jd, _("Game Over"), GUI_SML, GOAL_OVER, 0);
-            else
-            {
-                retry_id = gui_maybe(jd, _("Retry Level"), GOAL_SAME,
-                                     lg->mode != MODE_CHALLENGE);
-            }
 
-            gui_maybe(jd, _("Save Replay"), GOAL_SAVE, demo_saved());
+            if (progress_next_avail())
+                gui_start(jd, _("Next Level"),  GUI_SML, GOAL_NEXT, 0);
 
-            /* Default is next if the next level is newly unlocked. */
+            if (progress_same_avail())
+                gui_start(jd, _("Retry Level"), GUI_SML, GOAL_SAME, 0);
 
-            if (next_id && lg->unlock)
-                gui_focus(next_id);
-            else if (lg->mode != MODE_CHALLENGE)
-                gui_focus(retry_id);
+            if (demo_saved())
+                gui_state(jd, _("Save Replay"), GUI_SML, GOAL_SAVE, 0);
         }
 
-        /* FIXME, I'm ugly. */
-
-        if (high)
-            gui_state(id, _("Change Player Name"),  GUI_SML, GOAL_NAME, 0);
+        if (!resume)
+            gui_pulse(gid, 1.2f);
 
-        gui_pulse(gid, 1.2f);
         gui_layout(id, 0, 0);
 
     }
 
-    set_most_coins(&l->score.most_coins, lg->coin_rank);
-
-    if (lg->mode == MODE_CHALLENGE || lg->mode == MODE_NORMAL)
-        set_best_times(&l->score.unlock_goal, lg->goal_rank, 1);
-    else
-        set_best_times(&l->score.best_times, lg->time_rank, 0);
+    set_score_board(&l->score.most_coins,  progress_coin_rank(),
+                    &l->score.best_times,  progress_time_rank(),
+                    &l->score.unlock_goal, progress_goal_rank());
 
     audio_music_fade_out(2.0f);
 
     config_clr_grab();
 
+    /* Reset hack. */
+    resume = 0;
+
     return id;
 }
 
@@ -265,7 +260,7 @@ static void goal_timer(int id, float dt)
             gui_set_count(score_id, score + 1);
             gui_pulse(score_id, 1.1f);
 
-            if ((score + 1) % 100 == 0)
+            if (progress_reward_ball(score + 1))
             {
                 gui_set_count(balls_id, balls + 1);
                 gui_pulse(balls_id, 2.0f);
@@ -278,6 +273,14 @@ static void goal_timer(int id, float dt)
     gui_timer(id, dt);
 }
 
+static int goal_keybd(int c, int d)
+{
+    if (d && config_tst_d(CONFIG_KEY_SCORE_NEXT, c))
+        return goal_action(gui_score_next(gui_score_get()));
+
+    return 1;
+}
+
 static int goal_buttn(int b, int d)
 {
     if (d)
@@ -293,7 +296,7 @@ static int goal_buttn(int b, int d)
 static void goal_leave(int id)
 {
     /* HACK:  don't run animation if only "visiting" a state. */
-    st_goal.timer = be_back_soon ? shared_timer : goal_timer;
+    st_goal.timer = resume ? shared_timer : goal_timer;
 
     gui_delete(id);
 }
@@ -309,7 +312,7 @@ struct state st_goal = {
     shared_stick,
     shared_angle,
     shared_click,
-    NULL,
+    goal_keybd,
     goal_buttn,
     1, 0
 };
index a158e79..e623429 100644 (file)
@@ -48,12 +48,14 @@ static int help_action(int t)
         break;
 
     case HELP_DEMO_1:
-        if (demo_replay_init(config_data("gui/demo1.nbr"), NULL))
+        if (demo_replay_init(config_data("gui/demo1.nbr"),
+                             NULL, NULL, NULL, NULL, NULL))
             return goto_state(&st_help_demo);
         break;
 
     case HELP_DEMO_2:
-        if (demo_replay_init(config_data("gui/demo2.nbr"), NULL))
+        if (demo_replay_init(config_data("gui/demo2.nbr"),
+                             NULL, NULL, NULL, NULL, NULL))
             return goto_state(&st_help_demo);
         break;
 
@@ -240,14 +242,6 @@ static int help_modes(int id)
 
             gui_space(kd);
 
-            gui_label(kd, _("Practice Mode"), GUI_SML, GUI_TOP, 0, 0);
-            gui_multi(kd,
-                      _("Play without time limit or coin constraint.\\"
-                        "Levels cannot be unlocked in this mode."),
-                      GUI_SML, GUI_BOT, gui_wht, gui_wht);
-
-            gui_space(kd);
-
             gui_label(kd, _("Challenge Mode"), GUI_SML, GUI_TOP, 0, 0);
             gui_multi(kd,
                       _("Start playing from the first level of the set.\\"
index f408dbd..8e70c78 100644 (file)
@@ -17,7 +17,7 @@
 #include "gui.h"
 #include "game.h"
 #include "set.h"
-#include "levels.h"
+#include "progress.h"
 #include "audio.h"
 #include "config.h"
 #include "st_shared.h"
@@ -33,7 +33,6 @@ static int level_enter(void)
 {
     int id, jd, kd, ld;
     const char *ln;
-    const struct level_game *lg = curr_lg();
     int b;
     const float *textcol1, *textcol2;
 
@@ -41,8 +40,9 @@ static int level_enter(void)
     {
         if ((jd = gui_hstack(id)))
         {
-            ln = lg->level->repr;
-            b = lg->level->is_bonus;
+            ln = level_repr (curr_level());
+            b  = level_bonus(curr_level());
+
             textcol1 = b ? gui_wht : 0;
             textcol2 = b ? gui_grn : 0;
 
@@ -73,7 +73,7 @@ static int level_enter(void)
                     }
                 }
 
-                gui_label(kd, mode_to_str(lg->mode, 1), GUI_SML, GUI_BOT,
+                gui_label(kd, mode_to_str(curr_mode(), 1), GUI_SML, GUI_BOT,
                           gui_wht, gui_wht);
 
             }
@@ -81,9 +81,9 @@ static int level_enter(void)
         }
         gui_space(id);
 
-        if (strlen(lg->level->message) != 0)
-            gui_multi(id, _(lg->level->message), GUI_SML, GUI_ALL, gui_wht,
-                      gui_wht);
+        gui_multi(id, level_msg(curr_level()),
+                  GUI_SML, GUI_ALL,
+                  gui_wht, gui_wht);
 
         gui_layout(id, 0, 0);
     }
@@ -120,8 +120,7 @@ static int level_buttn(int b, int d)
         }
         if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b))
         {
-            level_stat(GAME_NONE, curr_clock(), curr_coins());
-            level_stop();
+            progress_stop();
             return goto_state(&st_over);
         }
     }
index c455042..4a141b4 100644 (file)
@@ -15,7 +15,7 @@
 #include "gui.h"
 #include "set.h"
 #include "game.h"
-#include "levels.h"
+#include "progress.h"
 #include "audio.h"
 #include "config.h"
 #include "demo.h"
@@ -30,7 +30,7 @@ static int over_enter(void)
 {
     int id;
 
-    if (curr_lg()->mode != MODE_CHALLENGE)
+    if (curr_mode() != MODE_CHALLENGE)
         return 0;
 
     if ((id = gui_label(0, _("GAME OVER"), GUI_LRG, GUI_ALL, gui_gry, gui_red)))
@@ -49,7 +49,7 @@ static int over_enter(void)
 
 static void over_timer(int id, float dt)
 {
-    if (curr_lg()->mode != MODE_CHALLENGE || time_state() > 3.f)
+    if (curr_mode() != MODE_CHALLENGE || time_state() > 3.f)
         goto_state(&st_start);
 
     gui_timer(id, dt);
index 51b5b2a..77fca2f 100644 (file)
@@ -15,7 +15,7 @@
 #include "gui.h"
 #include "config.h"
 #include "game.h"
-#include "levels.h"
+#include "progress.h"
 #include "level.h"
 #include "audio.h"
 #include "hud.h"
@@ -65,15 +65,17 @@ static int pause_action(int i)
         return goto_state(st_continue);
 
     case PAUSE_RESTART:
-        level_same();
-        clear_pause();
-        SDL_PauseAudio(0);
-        config_set_grab(1);
-        return goto_state(&st_play_ready);
+        if (progress_same())
+        {
+            clear_pause();
+            SDL_PauseAudio(0);
+            config_set_grab(1);
+            return goto_state(&st_play_ready);
+        }
+        break;
 
     case PAUSE_EXIT:
-        level_stat(GAME_NONE, curr_clock(), curr_coins());
-        level_stop();
+        progress_exit(GAME_NONE);
         clear_pause();
         SDL_PauseAudio(0);
         audio_music_stop();
@@ -104,13 +106,8 @@ static int pause_enter(void)
         {
             gui_state(jd, _("Quit"), GUI_SML, PAUSE_EXIT, 0);
 
-            if (curr_lg()->mode != MODE_CHALLENGE)
+            if (progress_same_avail())
                 gui_state(jd, _("Restart"), GUI_SML, PAUSE_RESTART, 0);
-            else
-            {
-                int ld = gui_state(jd, _("Restart"), GUI_SML, 0, 0);
-                gui_set_color(ld, gui_gry, gui_gry);
-            }
 
             gui_start(jd, _("Continue"), GUI_SML, PAUSE_CONTINUE, 1);
         }
@@ -143,8 +140,7 @@ static int pause_keybd(int c, int d)
         if (config_tst_d(CONFIG_KEY_PAUSE, c))
             return pause_action(PAUSE_CONTINUE);
 
-        if (config_tst_d(CONFIG_KEY_RESTART, c)
-            && curr_lg()->mode != MODE_CHALLENGE)
+        if (config_tst_d(CONFIG_KEY_RESTART, c) && progress_same_avail())
             return pause_action(PAUSE_RESTART);
     }
     return 1;
index 5eb59ce..e3d8feb 100644 (file)
@@ -16,7 +16,7 @@
 #include "hud.h"
 #include "game.h"
 #include "demo.h"
-#include "levels.h"
+#include "progress.h"
 #include "audio.h"
 #include "config.h"
 #include "st_shared.h"
@@ -36,8 +36,7 @@ static int pause_or_exit(void)
 {
     if (SDL_GetModState() & KMOD_SHIFT)
     {
-        level_stat(GAME_NONE, curr_clock(), curr_coins());
-        level_stop();
+        progress_exit(GAME_NONE);
         config_clr_grab();
 
         return goto_state(&st_over);
@@ -228,24 +227,25 @@ static void play_loop_timer(int id, float dt)
     switch (game_step(g, dt, 1))
     {
     case GAME_GOAL:
-        level_stat(GAME_GOAL, curr_clock(), curr_coins());
+        progress_stat(GAME_GOAL);
         gui_stuck();
         goto_state(&st_goal);
         break;
 
     case GAME_FALL:
-        level_stat(GAME_FALL, curr_clock(), curr_coins());
+        progress_stat(GAME_FALL);
         gui_stuck();
         goto_state(&st_fall_out);
         break;
 
     case GAME_TIME:
-        level_stat(GAME_TIME, curr_clock(), curr_coins());
+        progress_stat(GAME_TIME);
         gui_stuck();
         goto_state(&st_time_out);
         break;
 
     default:
+        progress_step();
         break;
     }
 }
@@ -294,10 +294,10 @@ static int play_loop_keybd(int c, int d)
             hud_view_pulse(2);
         }
         if (config_tst_d(CONFIG_KEY_RESTART, c) &&
-            curr_lg()->mode != MODE_CHALLENGE)
+            progress_same_avail())
         {
-            level_same();
-            goto_state(&st_play_ready);
+            if (progress_same())
+                goto_state(&st_play_ready);
         }
         if (config_tst_d(CONFIG_KEY_PAUSE, c))
             goto_pause();
@@ -318,7 +318,7 @@ static int play_loop_keybd(int c, int d)
 
     if (d && c == SDLK_c && config_cheat())
     {
-        level_stat(GAME_GOAL, curr_clock(), curr_coins());
+        progress_stat(GAME_GOAL);
         return goto_state(&st_goal);
     }
     return 1;
index ea4abe3..39813d7 100644 (file)
@@ -21,7 +21,7 @@
 #include "audio.h"
 #include "config.h"
 #include "demo.h"
-#include "levels.h"
+#include "progress.h"
 #include "text.h"
 
 #include "st_shared.h"
index d3bdc68..b2fb56a 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "gui.h"
 #include "set.h"
-#include "levels.h"
+#include "progress.h"
 #include "game.h"
 #include "audio.h"
 #include "config.h"
index ec5b582..daa52a1 100644 (file)
@@ -16,7 +16,7 @@
 #include "set.h"
 #include "util.h"
 #include "game.h"
-#include "levels.h"
+#include "progress.h"
 #include "audio.h"
 #include "config.h"
 #include "st_shared.h"
 
 /*---------------------------------------------------------------------------*/
 
-#define START_BACK      -1
-#define START_PRACTICE  -2
-#define START_NORMAL    -3
-#define START_CHALLENGE -4
+#define START_BACK        -1
+#define START_CHALLENGE   -2
+#define START_OPEN_GOALS  -3
+#define START_LOCK_GOALS  -4
 
 static int shot_id;
-static int status_id;
 
 /*---------------------------------------------------------------------------*/
 
@@ -43,101 +42,59 @@ static int status_id;
 
 static void gui_level(int id, int i)
 {
-    const GLfloat *fore, *back;
-    const struct level *l;
+    const GLfloat *fore = 0, *back = 0;
 
     int jd;
 
-    if (!set_level_exists(curr_set(), i))
+    if (!level_exists(i))
     {
-        gui_space(id);
+        gui_label(id, " ", GUI_SML, GUI_ALL, gui_blk, gui_blk);
         return;
     }
 
-    l = get_level(i);
-
-    if (!l->is_locked)
+    if (level_opened(i))
     {
-        fore = l->is_bonus     ? gui_grn : gui_wht;
-        back = l->is_completed ? fore    : gui_yel;
+        fore = level_bonus(i)     ? gui_grn : gui_wht;
+        back = level_completed(i) ? fore    : gui_yel;
     }
-    else
-        fore = back = gui_gry;
 
-    jd = gui_label(id, l->repr, GUI_SML, GUI_ALL, back, fore);
+    jd = gui_label(id, level_repr(i), GUI_SML, GUI_ALL, back, fore);
 
-    gui_active(jd, i, 0);
+    if (level_opened(i) || config_cheat())
+        gui_active(jd, i, 0);
 }
 
 static void start_over_level(int i)
 {
-    const struct level *l = get_level(i);
-    if (!l->is_locked || config_cheat())
+    if (level_opened(i) || config_cheat())
     {
-        gui_set_image(shot_id, l->shot);
-
-        set_most_coins(&l->score.most_coins, -1);
+        gui_set_image(shot_id, level_shot(i));
 
-        if (curr_mode() == MODE_PRACTICE)
-        {
-            set_best_times(&l->score.best_times, -1, 0);
-            if (l->is_bonus)
-                gui_set_label(status_id,
-                              _("Play this bonus level in practice mode"));
-            else
-                gui_set_label(status_id,
-                              _("Play this level in practice mode"));
-        }
-        else
-        {
-            set_best_times(&l->score.unlock_goal, -1, 1);
-            if (l->is_bonus)
-                gui_set_label(status_id,
-                              _("Play this bonus level in normal mode"));
-            else
-                gui_set_label(status_id, _("Play this level in normal mode"));
-        }
-        if (config_cheat())
-        {
-            gui_set_label(status_id, l->file);
-        }
-        return;
+        set_score_board(&get_level(i)->score.most_coins,  -1,
+                        &get_level(i)->score.best_times,  -1,
+                        &get_level(i)->score.unlock_goal, -1);
     }
-    else if (l->is_bonus)
-        gui_set_label(status_id,
-                      _("Play in challenge mode to unlock extra bonus levels"));
-    else
-        gui_set_label(status_id,
-                      _("Finish previous levels to unlock this level"));
 }
 
-static void start_over(int id)
+static void start_over(int id, int pulse)
 {
     int i;
 
-    gui_pulse(id, 1.2f);
     if (id == 0)
         return;
 
-    i = gui_token(id);
+    if (pulse)
+        gui_pulse(id, 1.2f);
 
+    i = gui_token(id);
 
-    switch (i)
+    if (i == START_CHALLENGE || i == START_BACK)
     {
-    case START_CHALLENGE:
         gui_set_image(shot_id, set_shot(curr_set()));
-        set_most_coins(set_coin_score(curr_set()), -1);
-        set_best_times(set_time_score(curr_set()), -1, 0);
-        gui_set_label(status_id, _("Challenge all levels from the first one"));
-        break;
-
-    case START_NORMAL:
-        gui_set_label(status_id, _("Collect coins and unlock next level"));
-        break;
 
-    case START_PRACTICE:
-        gui_set_label(status_id, _("Train yourself without time nor coin"));
-        break;
+        set_score_board(set_coin_score(curr_set()), -1,
+                        set_time_score(curr_set()), -1,
+                        set_time_score(curr_set()), -1);
     }
 
     if (i >= 0)
@@ -148,51 +105,37 @@ static void start_over(int id)
 
 static int start_action(int i)
 {
-    int mode = curr_mode();
-
     audio_play(AUD_MENU, 1.0f);
 
     switch (i)
     {
     case START_BACK:
         return goto_state(&st_set);
-    case START_NORMAL:
-        mode_set(MODE_NORMAL);
-        return goto_state(&st_start);
-    case START_PRACTICE:
-        mode_set(MODE_PRACTICE);
+
+    case START_CHALLENGE:
+        progress_init(MODE_CHALLENGE);
+        return config_cheat() ? 1 : start_action(0);
+
+    case GUI_MOST_COINS:
+    case GUI_BEST_TIMES:
+    case GUI_UNLOCK_GOAL:
+        gui_score_set(i);
         return goto_state(&st_start);
-    }
 
-    if (i == START_CHALLENGE)
-    {
-        /* On cheat, start challenge mode where you want */
-        if (config_cheat())
-        {
-            mode_set(MODE_CHALLENGE);
-            return goto_state(&st_start);
-        }
-        i = 0;
-        mode = MODE_CHALLENGE;
-    }
+    case START_OPEN_GOALS:
+        config_set_d(CONFIG_LOCK_GOALS, 0);
+        return goto_state(&st_start);
 
-    if (i >= 0)
-    {
-        const struct level *l = get_level(i);
+    case START_LOCK_GOALS:
+        config_set_d(CONFIG_LOCK_GOALS, 1);
+        return goto_state(&st_start);
 
-        if (!l->is_locked || config_cheat())
-        {
-            if (level_play(l, mode))
-            {
-                return goto_state(&st_level);
-            }
-            else
-            {
-                level_stop();
-                return 1;
-            }
-        }
+    default:
+        if (progress_play(i))
+            return goto_state(&st_level);
+        break;
     }
+
     return 1;
 }
 
@@ -200,18 +143,11 @@ static int start_enter(void)
 {
     int w = config_get_d(CONFIG_WIDTH);
     int h = config_get_d(CONFIG_HEIGHT);
-    int m = curr_mode();
     int i, j;
 
     int id, jd, kd, ld;
 
-    /* Deactivate cheat */
-
-    if (m == MODE_CHALLENGE && !config_cheat())
-    {
-        mode_set(MODE_NORMAL);
-        m = MODE_NORMAL;
-    }
+    progress_init(MODE_NORMAL);
 
     if ((id = gui_vstack(0)))
     {
@@ -224,45 +160,57 @@ static int start_enter(void)
             gui_start(jd, _("Back"),  GUI_SML, START_BACK, 0);
         }
 
-
         if ((jd = gui_harray(id)))
         {
             shot_id = gui_image(jd, set_shot(curr_set()), 7 * w / 16, 7 * h / 16);
 
             if ((kd = gui_varray(jd)))
             {
-                if ((ld = gui_harray(kd)))
-                {
-                    gui_state(ld, _("Practice"), GUI_SML, START_PRACTICE,
-                              m == MODE_PRACTICE);
-                    gui_state(ld, _("Normal"),   GUI_SML, START_NORMAL,
-                              m == MODE_NORMAL);
-                }
                 for (i = 0; i < 5; i++)
                     if ((ld = gui_harray(kd)))
                         for (j = 4; j >= 0; j--)
                             gui_level(ld, i * 5 + j);
 
                 gui_state(kd, _("Challenge"), GUI_SML, START_CHALLENGE,
-                          m == MODE_CHALLENGE);
+                          curr_mode() == MODE_CHALLENGE);
             }
         }
         gui_space(id);
 
-        if ((jd = gui_harray(id)))
-        {
-            gui_most_coins(jd, 0);
-            gui_best_times(jd, 0);
-        }
+        if ((jd = gui_hstack(id)))
+            gui_score_board(jd, 0, 0);
+
         gui_space(id);
 
-        status_id = gui_label(id, _("Choose a level to play"), GUI_SML, GUI_ALL,
-                              gui_yel, gui_wht);
+        if ((jd = gui_hstack(id)))
+        {
+            gui_filler(jd);
+
+            if ((kd = gui_harray(jd)))
+            {
+                /* TODO, replace the whitespace hack with something sane. */
+
+                gui_state(kd,
+                          /* TRANSLATORS: adjust the amount of whitespace here
+                           * as necessary for the buttons to look good. */
+                          _("   No   "), GUI_SML, START_OPEN_GOALS,
+                          config_get_d(CONFIG_LOCK_GOALS) == 0);
+
+                gui_state(kd, _("Yes"), GUI_SML, START_LOCK_GOALS,
+                          config_get_d(CONFIG_LOCK_GOALS) == 1);
+            }
+
+            gui_space(jd);
+
+            gui_label(jd, _("Lock Goals of Completed Levels?"),
+                      GUI_SML, GUI_ALL, 0, 0);
+
+            gui_filler(jd);
+        }
 
         gui_layout(id, 0, 0);
 
-        set_most_coins(NULL, -1);
-        set_best_times(NULL, -1, m != MODE_PRACTICE);
+        set_score_board(NULL, -1, NULL, -1, NULL, -1);
     }
 
     audio_music_fade_to(0.5f, "bgm/inter.ogg");
@@ -272,7 +220,7 @@ static int start_enter(void)
 
 static void start_point(int id, int x, int y, int dx, int dy)
 {
-    start_over(gui_point(id, x, y));
+    start_over(gui_point(id, x, y), 1);
 }
 
 static void start_stick(int id, int a, int v)
@@ -280,26 +228,48 @@ static void start_stick(int id, int a, int v)
     int x = (config_tst_d(CONFIG_JOYSTICK_AXIS_X, a)) ? v : 0;
     int y = (config_tst_d(CONFIG_JOYSTICK_AXIS_Y, a)) ? v : 0;
 
-    start_over(gui_stick(id, x, y));
+    start_over(gui_stick(id, x, y), 1);
 }
 
 static int start_keybd(int c, int d)
 {
-    if (d && c == SDLK_c && config_cheat())
+    if (d)
     {
-        set_cheat();
-        return goto_state(&st_start);
-    }
+        if (c == SDLK_c && config_cheat())
+        {
+            set_cheat();
+            return goto_state(&st_start);
+        }
+        else if (c == SDLK_F12)
+        {
+            int i;
 
-    if (d && c == SDLK_F12)
-    {
-        int i;
+            /* Iterate over all levels, taking a screenshot of each. */
+
+            for (i = 0; i < MAXLVL; i++)
+                if (level_exists(i))
+                    level_snap(i);
+        }
+        else if (config_tst_d(CONFIG_KEY_SCORE_NEXT, c))
+        {
+            int active = gui_click();
 
-        /* Iterate over all levels, taking a screenshot of each. */
+            if (start_action(gui_score_next(gui_score_get())))
+            {
+                /* HACK ALERT
+                 *
+                 * This assumes that 'active' is a valid widget ID even after
+                 * the above start_action has recreated the entire widget
+                 * hierarchy.  Maybe it is.  Maybe it isn't.
+                 */
+                gui_focus(active);
+                start_over(active, 0);
 
-        for (i = 0; i < MAXLVL; i++)
-            if (set_level_exists(curr_set(), i))
-                level_snap(i);
+                return 1;
+            }
+            else
+                return 0;
+        }
     }
 
     return 1;
index bce3713..7c22ec9 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "game.h"
 #include "util.h"
-#include "levels.h"
+#include "progress.h"
 #include "demo.h"
 #include "audio.h"
 #include "gui.h"
@@ -45,20 +45,22 @@ static int time_out_action(int i)
         /* Fall through. */
 
     case TIME_OUT_OVER:
-        level_stop();
+        progress_stop();
         return goto_state(&st_over);
 
     case TIME_OUT_SAVE:
-        level_stop();
+        progress_stop();
         return goto_save(&st_time_out, &st_time_out);
 
     case TIME_OUT_NEXT:
-        level_next();
-        return goto_state(&st_level);
+        if (progress_next())
+            return goto_state(&st_level);
+        break;
 
     case TIME_OUT_SAME:
-        level_same();
-        return goto_state(&st_level);
+        if (progress_same())
+            return goto_state(&st_level);
+        break;
     }
 
     return 1;
@@ -68,8 +70,6 @@ static int time_out_enter(void)
 {
     int id, jd, kd;
 
-    const struct level_game *lg = curr_lg();
-
     if ((id = gui_vstack(0)))
     {
         kd = gui_label(id, _("Time's Up!"), GUI_LRG, GUI_ALL, gui_gry, gui_red);
@@ -78,27 +78,17 @@ static int time_out_enter(void)
 
         if ((jd = gui_harray(id)))
         {
-            int next_id = 0, retry_id = 0;
-
-            next_id = gui_maybe(jd, _("Next Level"),  TIME_OUT_NEXT,
-                                lg->next_level != NULL);
-
-            if (lg->dead)
-                gui_start(jd, _("Game Over"), GUI_SML, TIME_OUT_OVER, 0);
-            else
-            {
-                retry_id = gui_state(jd, _("Retry Level"), GUI_SML,
-                                     TIME_OUT_SAME, 0);
-            }
+            if (progress_dead())
+                gui_start(jd, _("Exit"), GUI_SML, TIME_OUT_OVER, 0);
 
-            gui_maybe(jd, _("Save Replay"), TIME_OUT_SAVE, demo_saved());
+            if (progress_next_avail())
+                gui_start(jd, _("Next Level"), GUI_SML, TIME_OUT_NEXT, 0);
 
-            /* Default is next if the next level is newly unlocked. */
+            if (progress_same_avail())
+                gui_start(jd, _("Retry Level"), GUI_SML, TIME_OUT_SAME, 0);
 
-            if (next_id && lg->unlock)
-                gui_focus(next_id);
-            else if (retry_id)
-                gui_focus(retry_id);
+            if (demo_saved())
+                gui_state(jd, _("Save Replay"), GUI_SML, TIME_OUT_SAVE, 0);
         }
         gui_space(id);
 
@@ -118,7 +108,7 @@ static int time_out_keybd(int c, int d)
 {
     if (d)
     {
-        if (config_tst_d(CONFIG_KEY_RESTART, c) && !curr_lg()->dead)
+        if (config_tst_d(CONFIG_KEY_RESTART, c) && progress_same_avail())
             return time_out_action(TIME_OUT_SAME);
     }
     return 1;
index 6e624ae..cb581b2 100644 (file)
@@ -110,8 +110,6 @@ static int title_action(int i)
     return 1;
 }
 
-static struct level title_level;
-
 static int title_enter(void)
 {
     int id, jd, kd;
@@ -153,8 +151,8 @@ static int title_enter(void)
     audio_music_fade_to(0.5f, "bgm/title.ogg");
 
     /* Initialize the title level for display. */
-    level_load("map-medium/title.sol", &title_level);
-    game_init(&title_level, 0, 0);
+
+    game_init("map-medium/title.sol", 0, 1);
 
     real_time = 0.0f;
     mode = 0;
@@ -197,7 +195,7 @@ static void title_timer(int id, float dt)
         {
             if ((demo = demo_pick()))
             {
-                demo_replay_init(demo, NULL);
+                demo_replay_init(demo, NULL, NULL, NULL, NULL, NULL);
                 game_set_fly(0.0f);
                 real_time = 0.0f;
                 mode = 2;
@@ -226,7 +224,8 @@ static void title_timer(int id, float dt)
 
         if (real_time > 1.0f)
         {
-            game_init(&title_level, 0, 0);
+            game_init("map-medium/title.sol", 0, 1);
+
             real_time = 0.0f;
             mode = 0;
         }
index d439438..e88528d 100644 (file)
@@ -38,7 +38,7 @@ static int coin_extra_row;
 
 /* Build a Most Coins top three list with default values. */
 
-void gui_most_coins(int id, int e)
+static void gui_most_coins(int id, int e)
 {
     const char *s = "1234567";
 
@@ -103,12 +103,12 @@ void gui_most_coins(int id, int e)
     }
 }
 
-/* Set the Most Coins top three list values for level i. */
+/* Set the Most Coins top three list values. */
 
-void set_most_coins(const struct score *s, int hilight)
+static void set_most_coins(const struct score *s, int hilight)
 {
-    int j, spe;
     const char *name;
+    int j;
 
     if (s == NULL)
     {
@@ -124,19 +124,19 @@ void set_most_coins(const struct score *s, int hilight)
         for (j = 0; j < NSCORE + coin_extra_row; j++)
         {
             name = s->player[j];
-            spe = is_special_name(name);
 
-            if (spe)
-                gui_set_color(coin_name[j], 0, 0);
-            else if (j != hilight)
-                gui_set_color(coin_name[j], gui_yel, gui_wht);
-            else if (j>= NSCORE)
-                gui_set_color(coin_name[j], gui_red, gui_red);
+            if (j == hilight)
+            {
+                if (j < NSCORE)
+                    gui_set_color(coin_name[j], gui_grn, gui_grn);
+                else
+                    gui_set_color(coin_name[j], gui_red, gui_red);
+            }
             else
-                gui_set_color(coin_name[j], gui_grn, gui_grn);
+                gui_set_color(coin_name[j], gui_yel, gui_wht);
 
             gui_set_count(coin_coin[j], s->coins[j]);
-            gui_set_label(coin_name[j], spe ? _(name) : name);
+            gui_set_label(coin_name[j], is_special_name(name) ? _(name) : name);
             gui_set_clock(coin_time[j], s->timer[j]);
         }
     }
@@ -154,7 +154,7 @@ static int time_extra_row;
 
 /* Build a Best Times top three list with default values. */
 
-void gui_best_times(int id, int e)
+static void gui_best_times(int id, int e)
 {
     const char *s = "1234567";
 
@@ -219,12 +219,12 @@ void gui_best_times(int id, int e)
     }
 }
 
-/* Set the Best Times top three list values for level i. */
+/* Set the Best Times top three list values. */
 
-void set_best_times(const struct score *s, int hilight, int goal)
+static void set_best_times(const struct score *s, int hilight, int goal)
 {
-    int j, spe;
     const char *name;
+    int j;
 
     gui_set_label(time_label, goal ? _("Unlock Goal") : _("Best Times"));
 
@@ -242,19 +242,19 @@ void set_best_times(const struct score *s, int hilight, int goal)
         for (j = 0; j < NSCORE + time_extra_row; j++)
         {
             name = s->player[j];
-            spe = is_special_name(name);
 
-            if (spe)
-                gui_set_color(time_name[j], 0, 0);
-            else if (j != hilight)
-                gui_set_color(time_name[j], gui_yel, gui_wht);
-            else if (j>= NSCORE)
-                gui_set_color(time_name[j], gui_red, gui_red);
+            if (j == hilight)
+            {
+                if (j < NSCORE)
+                    gui_set_color(time_name[j], gui_grn, gui_grn);
+                else
+                    gui_set_color(time_name[j], gui_red, gui_red);
+            }
             else
-                gui_set_color(time_name[j], gui_grn, gui_grn);
+                gui_set_color(time_name[j], gui_yel, gui_wht);
 
             gui_set_clock(time_time[j], s->timer[j]);
-            gui_set_label(time_name[j], spe ? _(name) : name);
+            gui_set_label(time_name[j], is_special_name(name) ? _(name) : name);
             gui_set_count(time_coin[j], s->coins[j]);
         }
     }
@@ -262,6 +262,112 @@ void set_best_times(const struct score *s, int hilight, int goal)
 
 /*---------------------------------------------------------------------------*/
 
+static int score_type = GUI_MOST_COINS;
+
+void gui_score_board(int id, int e, int h)
+{
+    int jd, kd, ld;
+
+    gui_filler(id);
+
+    if ((jd = gui_hstack(id)))
+    {
+        gui_filler(jd);
+
+        if ((kd = gui_vstack(jd)))
+        {
+            gui_filler(kd);
+
+            gui_state(kd, _("Most Coins"),  GUI_SML, GUI_MOST_COINS,
+                      score_type == GUI_MOST_COINS);
+            gui_state(kd, _("Best Times"),  GUI_SML, GUI_BEST_TIMES,
+                      score_type == GUI_BEST_TIMES);
+            gui_state(kd, _("Unlock Goal"), GUI_SML, GUI_UNLOCK_GOAL,
+                      score_type == GUI_UNLOCK_GOAL);
+
+            if (h)
+            {
+                gui_space(kd);
+
+                if ((ld = gui_hstack(kd)))
+                {
+                    gui_filler(ld);
+                    gui_state(ld, _("Change Name"), GUI_SML, GUI_NAME, 0);
+                    gui_filler(ld);
+                }
+            }
+
+            gui_filler(kd);
+        }
+
+        gui_filler(jd);
+    }
+
+    gui_filler(id);
+
+    switch (score_type)
+    {
+    case GUI_MOST_COINS:
+        gui_most_coins(id, e);
+        break;
+
+    case GUI_BEST_TIMES:
+        gui_best_times(id, e);
+        break;
+
+    case GUI_UNLOCK_GOAL:
+        gui_best_times(id, e);
+        break;
+    }
+
+    gui_filler(id);
+}
+
+void set_score_board(const struct score *smc, int hmc,
+                     const struct score *sbt, int hbt,
+                     const struct score *sug, int hug)
+{
+    switch (score_type)
+    {
+    case GUI_MOST_COINS:
+        set_most_coins(smc, hmc);
+        break;
+
+    case GUI_BEST_TIMES:
+        set_best_times(sbt, hbt, 0);
+        break;
+
+    case GUI_UNLOCK_GOAL:
+        set_best_times(sug, hug, 1);
+        break;
+    }
+}
+
+void gui_score_set(int t)
+{
+    score_type = t;
+}
+
+int  gui_score_get(void)
+{
+    return score_type;
+}
+
+int  gui_score_next(int t)
+{
+    switch (t)
+    {
+    case GUI_MOST_COINS:  return GUI_BEST_TIMES;
+    case GUI_BEST_TIMES:  return GUI_UNLOCK_GOAL;
+    case GUI_UNLOCK_GOAL: return GUI_MOST_COINS;
+
+    default:
+        return GUI_MOST_COINS;
+    }
+}
+
+/*---------------------------------------------------------------------------*/
+
 static int lock = 1;
 static int keyd[127];
 
index aa1529e..969e03c 100644 (file)
 #define GUI_BS   -104
 #define GUI_CL   -105
 
-void gui_most_coins(int, int);
-void set_most_coins(const struct score *, int);
-void gui_best_times(int, int);
-void set_best_times(const struct score *, int, int);
+#define GUI_MOST_COINS  -106
+#define GUI_BEST_TIMES  -107
+#define GUI_UNLOCK_GOAL -108
+
+#define GUI_NAME -109
+
+void gui_score_set(int);
+int  gui_score_get(void);
+int  gui_score_next(int);
+
+void gui_score_board(int, int, int);
+void set_score_board(const struct score *, int,
+                     const struct score *, int,
+                     const struct score *, int);
 
 void gui_keyboard(int);
 void gui_keyboard_lock(void);
index 980aee1..015b8ce 100644 (file)
Binary files a/data/gui/demo1.nbr and b/data/gui/demo1.nbr differ
index 769e934..00f8294 100644 (file)
Binary files a/data/gui/demo2.nbr and b/data/gui/demo2.nbr differ
index bd37109..2615c25 100644 (file)
@@ -124,7 +124,9 @@ void config_init(void)
     config_set_d(CONFIG_KEY_RIGHT,            DEFAULT_KEY_RIGHT);
     config_set_d(CONFIG_KEY_PAUSE,            DEFAULT_KEY_PAUSE);
     config_set_d(CONFIG_KEY_RESTART,          DEFAULT_KEY_RESTART);
+    config_set_d(CONFIG_KEY_SCORE_NEXT,       DEFAULT_KEY_SCORE_NEXT);
     config_set_d(CONFIG_SCREENSHOT,           DEFAULT_SCREENSHOT);
+    config_set_d(CONFIG_LOCK_GOALS,           DEFAULT_LOCK_GOALS);
 }
 
 void config_load(void)
@@ -244,6 +246,9 @@ void config_load(void)
                 else if (strcmp(key, "key_restart")  == 0)
                     config_key(val, CONFIG_KEY_RESTART, DEFAULT_KEY_RESTART);
 
+                else if (strcmp(key, "key_score_next") == 0)
+                    config_key(val, CONFIG_KEY_SCORE_NEXT, DEFAULT_KEY_SCORE_NEXT);
+
                 else if (strcmp(key, "player") == 0)
                     config_set_s(CONFIG_PLAYER, val);
                 else if (strcmp(key, "ball_file") == 0)
@@ -259,6 +264,8 @@ void config_load(void)
                     config_set_d(CONFIG_UNIFORM, atoi(val));
                 else if (strcmp(key, "screenshot") == 0)
                     config_set_d(CONFIG_SCREENSHOT, atoi(val));
+                else if (strcmp(key, "lock_goals") == 0)
+                    config_set_d(CONFIG_LOCK_GOALS, atoi(val));
             }
 
         fclose(fp);
@@ -377,6 +384,9 @@ void config_save(void)
         fprintf(fp, "key_restart          %s\n",
                 SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_RESTART]));
 
+        fprintf(fp, "key_score_next       %s\n",
+                SDL_GetKeyName((SDLKey) option_d[CONFIG_KEY_SCORE_NEXT]));
+
         if (strlen(option_s[CONFIG_PLAYER]) > 0)
             fprintf(fp, "player       %s\n", option_s[CONFIG_PLAYER]);
         if (strlen(option_s[CONFIG_BALL]) > 0)
@@ -384,12 +394,10 @@ void config_save(void)
         if (strlen(option_s[CONFIG_WIIMOTE_ADDR]) > 0)
             fprintf(fp, "wiimote_addr %s\n", option_s[CONFIG_WIIMOTE_ADDR]);
 
-        fprintf(fp, "stats                %d\n",
-                option_d[CONFIG_STATS]);
-        fprintf(fp, "uniform              %d\n",
-                option_d[CONFIG_UNIFORM]);
-        fprintf(fp, "screenshot           %d\n",
-            option_d[CONFIG_SCREENSHOT]);
+        fprintf(fp, "stats                %d\n", option_d[CONFIG_STATS]);
+        fprintf(fp, "uniform              %d\n", option_d[CONFIG_UNIFORM]);
+        fprintf(fp, "screenshot           %d\n", option_d[CONFIG_SCREENSHOT]);
+        fprintf(fp, "lock_goals           %d\n", option_d[CONFIG_LOCK_GOALS]);
 
         if (config_cheat())
             fprintf(fp, "cheat                %d\n", option_d[CONFIG_CHEAT]);
index 3cb5af3..80789bc 100644 (file)
@@ -85,10 +85,12 @@ enum {
     CONFIG_KEY_RIGHT,
     CONFIG_KEY_PAUSE,
     CONFIG_KEY_RESTART,
+    CONFIG_KEY_SCORE_NEXT,
     CONFIG_CHEAT,
     CONFIG_STATS,
     CONFIG_UNIFORM,
     CONFIG_SCREENSHOT,
+    CONFIG_LOCK_GOALS,
 
     CONFIG_OPTION_D_COUNT
 };
@@ -161,9 +163,11 @@ enum {
 #define DEFAULT_KEY_RIGHT            SDLK_RIGHT
 #define DEFAULT_KEY_PAUSE            SDLK_SPACE
 #define DEFAULT_KEY_RESTART          SDLK_r
+#define DEFAULT_KEY_SCORE_NEXT       SDLK_TAB
 #define DEFAULT_STATS                0
 #define DEFAULT_UNIFORM              0
 #define DEFAULT_SCREENSHOT           0
+#define DEFAULT_LOCK_GOALS           0
 
 /*---------------------------------------------------------------------------*/
 
index 6ab8410..6ee5f64 100644 (file)
@@ -26,6 +26,8 @@
 #define _(String)   (String)
 #define L_(String)  (String)
 
+#define ngettext(msgid, msgid_plural, n) ((n) == 1 ? (msgid) : (msgid_plural))
+
 #endif /* ENABLE_NLS */
 
 /* No-op, useful for marking up strings for extraction-only. */