Deny playing bonus levels in any mode, including challenge mode. Todo: inform the...
[neverball] / ball / level.c
index b324655..983cde2 100644 (file)
 #include <math.h>
 #include <errno.h>
 
+#include "config.h"
+#include "demo.h"
+#include "text.h"
 #include "level.h"
+#include "mode.h"
+#include "set.h"
 #include "solid.h"
 
 /*---------------------------------------------------------------------------*/
 
-void score_init_hs(struct score *s, int timer, int coins)
+static void scan_dict(struct level *l, const struct s_file *fp)
 {
     int i;
 
-    strcpy(s->player[0], "Hard");
-    strcpy(s->player[1], "Medium");
-    strcpy(s->player[2], "Easy");
-    strcpy(s->player[3], "");
-
-    for (i = 0; i < NSCORE + 1; i++)
-    {
-        s->timer[i] = timer;
-        s->coins[i] = coins;
-    }
-}
-
-/*---------------------------------------------------------------------------*/
-
-static int level_scan_metadata(struct level *l, char *av)
-{
-#define CASE(x) (strcmp((x), c) == 0)
-    char *c    = av;
-    char *stop = av + strlen(av);
-    char *v, *e;
-
-    while (c < stop)
+    for (i = 0; i < fp->dc; i++)
     {
-        /* look for the start of the value */
-        v = strchr(c, '=');
-        if (v == NULL)
-            return 0;
-        *v = '\0';
-        v++;
-
-        /* look the end of the value */
-        e = strchr(v, '\n');
-        if (e == NULL)
-            return 0;
-        *e = '\0';
-        e++;
-
-        /* test metadata */
-        if (CASE("message"))
-            strcpy(l->message, v);
-        else if (CASE("back"))
-            strcpy(l->back, v);
-        else if (CASE("song"))
-            strcpy(l->song, v);
-        else if (CASE("grad"))
-            strcpy(l->grad, v);
-        else if (CASE("shot"))
-            strcpy(l->shot, v);
-        else if (CASE("goal"))
+        char *k = fp->av + fp->dv[i].ai;
+        char *v = fp->av + fp->dv[i].aj;
+
+        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)
         {
             l->goal = atoi(v);
             l->score.most_coins.coins[2] = l->goal;
         }
-        else if (CASE("time"))
+        else if (strcmp(k, "time") == 0)
         {
             l->time = atoi(v);
             l->score.best_times.timer[2] = l->time;
             l->score.unlock_goal.timer[2] = l->time;
         }
-        else if (CASE("time_hs"))
+        else if (strcmp(k, "time_hs") == 0)
             sscanf(v, "%d %d",
                    &l->score.best_times.timer[0],
                    &l->score.best_times.timer[1]);
-        else if (CASE("goal_hs"))
+        else if (strcmp(k, "goal_hs") == 0)
             sscanf(v, "%d %d",
                    &l->score.unlock_goal.timer[0],
                    &l->score.unlock_goal.timer[1]);
-        else if (CASE("coin_hs"))
+        else if (strcmp(k, "coin_hs") == 0)
             sscanf(v, "%d %d",
                    &l->score.most_coins.coins[0],
                    &l->score.most_coins.coins[1]);
-        else if (CASE("version"))
-            strcpy(l->version, v);
-        else if (CASE("author"))
-            strcpy(l->author, v);
-        else if (CASE("special"))
-            l->is_bonus = atoi(v);
-
-        c = e;
+        else if (strcmp(k, "version") == 0)
+            strncpy(l->version, v, MAXSTR);
+        else if (strcmp(k, "author") == 0)
+            strncpy(l->author, v, MAXSTR);
+        else if (strcmp(k, "bonus") == 0)
+            l->is_bonus = atoi(v) ? 1 : 0;
     }
-    return 1;
 }
 
-/* Load the sol file 'filename' and fill the 'level' structure.  Return 1 on
- * success, 0 on error. */
-
 int level_load(const char *filename, struct level *level)
 {
     struct s_file sol;
@@ -123,15 +89,21 @@ int level_load(const char *filename, struct level *level)
     memset(level, 0, sizeof (struct level));
     memset(&sol,  0, sizeof (sol));
 
-    /* Try to load the sol file */
+#define format \
+    L_("Error while loading level file '%s': %s\n")
+#define default_error \
+    L_("Not a valid level file")
+
     if (!sol_load_only_head(&sol, config_data(filename)))
     {
-        fprintf(stderr,
-                _("Error while loading level file '%s': %s\n"), filename,
-                errno ? strerror(errno) : _("Not a valid level file"));
+        const char *error = errno ? strerror(errno) : default_error;
+        fprintf(stderr, format, filename, error);
         return 0;
     }
 
+#undef format
+#undef default_error
+
     strcpy(level->file, filename);
 
     /* Init hs with default values */
@@ -146,9 +118,8 @@ int level_load(const char *filename, struct level *level)
             money += sol.hv[i].n;
     level->score.most_coins.coins[0] = money;
 
-    /* Scan sol metadata */
-    if (sol.ac > 0)
-        level_scan_metadata(level, sol.av);
+    if (sol.dc > 0)
+        scan_dict(level, &sol);
 
     /* Compute initial hs default values */
 
@@ -167,9 +138,7 @@ int level_load(const char *filename, struct level *level)
     return 1;
 }
 
-/*---------------------------------------------------------------------------*/
-
-void level_dump_info(const struct level *l)
+void level_dump(const struct level *l)
 {
     printf("filename:        %s\n"
            "version:         %s\n"
@@ -181,7 +150,7 @@ void level_dump_info(const struct level *l)
            "coin hs:         %d %d %d\n"
            "message:         %s\n"
            "background:      %s\n"
-           "gradiant:        %s\n"
+           "gradient:        %s\n"
            "screenshot:      %s\n"
            "song:            %s\n",
            l->file,
@@ -207,27 +176,131 @@ void level_dump_info(const struct level *l)
 
 /*---------------------------------------------------------------------------*/
 
-const char *mode_to_str(int m)
+static unsigned int do_level_init = 1;
+
+int level_replay(const char *filename)
 {
-    switch (m)
+    return demo_replay_init(filename, curr_lg());
+}
+
+int level_play(const struct level *l, int m)
+{
+    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;
+
+    return demo_play_init(USER_REPLAY_FILE, lg->level, lg);
+}
+
+void level_stat(int status, int clock, int coins)
+{
+    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)
     {
-    case MODE_CHALLENGE: return _("Challenge");
-    case MODE_NORMAL:    return _("Normal");
-    case MODE_PRACTICE:  return _("Practice");
-    case MODE_SINGLE:    return _("Single");
-    default:             return _("Unknown");
+        /* sum time */
+        lg->times += timer;
+
+        /* 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;
+        }
+
+        /* lose ball and game */
+        else
+        {
+            lg->dead = (lg->balls <= 0);
+            lg->balls--;
+        }
     }
+
+    set_finish_level(lg, player);
+
+    demo_play_stat(lg);
+}
+
+void level_stop(void)
+{
+    demo_play_stop();
+    do_level_init = 1;
+}
+
+int level_next(void)
+{
+    struct level_game *lg = curr_lg();
+
+    level_stop();
+    lg->level = lg->next_level;
+    do_level_init = 0;
+
+    return level_play(lg->level, lg->mode);
+}
+
+int level_same(void)
+{
+    level_stop();
+    do_level_init = 0;
+    return level_play(curr_lg()->level, curr_lg()->mode);
+}
+
+/*---------------------------------------------------------------------------*/
+
+int count_extra_balls(int old_score, int coins)
+{
+    return ((old_score % 100) + coins) / 100;
+}
+
+void level_update_player_name(void)
+{
+    char player[MAXNAM];
+
+    config_get_s(CONFIG_PLAYER, player, MAXNAM);
+
+    score_change_name(curr_lg(), player);
 }
 
 /*---------------------------------------------------------------------------*/
 
-const char *state_to_str(int m)
+const char *status_to_str(int m)
 {
     switch (m)
     {
     case GAME_NONE:    return _("Aborted");
     case GAME_TIME:    return _("Time-out");
-    case GAME_SPEC:
     case GAME_GOAL:    return _("Success");
     case GAME_FALL:    return _("Fall-out");
     default:           return _("Unknown");