Disable shadow in set snapshots.
[neverball] / ball / level.c
index 62d8cbc..be7f39b 100644 (file)
@@ -1,4 +1,4 @@
-/*   
+/*
  * Copyright (C) 2003 Robert Kooima
  *
  * NEVERBALL is  free software; you can redistribute  it and/or modify
  */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <math.h>
+#include <errno.h>
 
 #include "level.h"
-#include "glext.h"
-#include "image.h"
-#include "game.h"
-#include "geom.h"
-#include "demo.h"
-#include "hud.h"
-#include "audio.h"
-#include "config.h"
+#include "solid.h"
 
 /*---------------------------------------------------------------------------*/
 
-struct score
+void score_init_hs(struct score *s, int timer, int coins)
 {
-    char time_n[4][MAXNAM];
-    int  time_t[4];
-    int  time_c[4];
-
-    char coin_n[4][MAXNAM];
-    int  coin_t[4];
-    int  coin_c[4];
-};
-
-struct level
-{
-    char file[MAXSTR];
-    char back[MAXSTR];
-    char grad[MAXSTR];
-    char shot[MAXSTR];
-    char song[MAXSTR];
-    int  time;
-    int  goal;
-
-    GLuint text;
-};
-
-static int score;                       /* Current coin total         */
-static int coins;                       /* Current coin count         */
-static int balls;                       /* Current life count         */
-static int goal;                        /* Current goal count         */
-
-static int level;                       /* Current level number       */
-static int count;                       /* Number of levels           */
-static int limit;                       /* Last opened (locked) level */
-
-static int mode;                       /* Current play mode          */
-
-static int coins_total;
-static int times_total;
-
-static struct level level_v[MAXLVL];
-static struct score score_v[MAXLVL];
-
-static char scores_file[MAXSTR];
-
-/*---------------------------------------------------------------------------*/
-
-static void level_store_hs(const char *filename)
-{
-    FILE *fout;
-
-    if ((fout = fopen(config_user(filename), "w")))
-    {
-        int i;
-        int j;
-
-        for (i = 0; i < limit; i++)
-            for (j = 0; j < 3; j++)
-            {
-                if (strlen(score_v[i].time_n[j]) == 0)
-                    strcpy(score_v[i].time_n[j], DEFAULT_PLAYER);
-                if (strlen(score_v[i].coin_n[j]) == 0)
-                    strcpy(score_v[i].coin_n[j], DEFAULT_PLAYER);
-
-                fprintf(fout, "%d %d %s\n",
-                        score_v[i].time_t[j],
-                        score_v[i].time_c[j],
-                        score_v[i].time_n[j]);
-                fprintf(fout, "%d %d %s\n",
-                        score_v[i].coin_t[j],
-                        score_v[i].coin_c[j],
-                        score_v[i].coin_n[j]);
-            }
-            
-        fclose(fout);
-    }
-}
-
-static void level_load_hs(const char *filename)
-{
-    FILE *fin;
-
-    limit = 1;
-
-    if ((fin = fopen(config_user(filename), "r")))
+    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++)
     {
-        int i;
-
-        for (i = 0; i < count; i++)
-        {
-            if (fscanf(fin, "%d %d %s",
-                       &score_v[i].time_t[0],
-                       &score_v[i].time_c[0],
-                       score_v[i].time_n[0]) == 3 &&
-                fscanf(fin, "%d %d %s",
-                       &score_v[i].coin_t[0],
-                       &score_v[i].coin_c[0],
-                       score_v[i].coin_n[0]) == 3 &&
-                fscanf(fin, "%d %d %s",
-                       &score_v[i].time_t[1],
-                       &score_v[i].time_c[1],
-                       score_v[i].time_n[1]) == 3 &&
-                fscanf(fin, "%d %d %s",
-                       &score_v[i].coin_t[1],
-                       &score_v[i].coin_c[1],
-                       score_v[i].coin_n[1]) == 3 &&
-                fscanf(fin, "%d %d %s",
-                       &score_v[i].time_t[2],
-                       &score_v[i].time_c[2],
-                       score_v[i].time_n[2]) == 3 &&
-                fscanf(fin, "%d %d %s",
-                       &score_v[i].coin_t[2],
-                       &score_v[i].coin_c[2],
-                       score_v[i].coin_n[2]) == 3)
-                limit = i + 1;
-        }
-
-        fclose(fin);
+        s->timer[i] = timer;
+        s->coins[i] = coins;
     }
 }
 
 /*---------------------------------------------------------------------------*/
 
-static void level_init_rc(const char *filename)
+static int level_scan_metadata(struct level *l, char *av)
 {
-    FILE *fin;
-    char buf[MAXSTR];
-
-    count = 0;
-    level = 0;
-    coins = 0;
-    score = 0;
-    balls = 0;
-
-    /* Load the levels list. */
+#define CASE(x) (strcmp((x), c) == 0)
+    char *c    = av;
+    char *stop = av + strlen(av);
+    char *v, *e;
 
-    if ((fin = fopen(config_data(filename), "r")))
+    while (c < stop)
     {
-        while (count < MAXLVL && fgets(buf, MAXSTR, fin))
+        /* 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"))
         {
-            sscanf(buf, "%s %s %s %s %d %d %s",
-                    level_v[count].file,
-                    level_v[count].back,
-                    level_v[count].shot,
-                    level_v[count].grad,
-                   &level_v[count].time,
-                   &level_v[count].goal,
-                    level_v[count].song);
-            count++;
+            l->goal = atoi(v);
+            l->coin_score.coins[2] = l->goal;
         }
-        fclose(fin);
+        else if (CASE("time"))
+        {
+            l->time = atoi(v);
+            l->time_score.timer[2] = l->time;
+            l->goal_score.timer[2] = l->time;
+        }
+        else if (CASE("time_hs"))
+            sscanf(v, "%d %d",
+                   &l->time_score.timer[0],
+                   &l->time_score.timer[1]);
+        else if (CASE("goal_hs"))
+            sscanf(v, "%d %d",
+                   &l->goal_score.timer[0],
+                   &l->goal_score.timer[1]);
+        else if (CASE("coin_hs"))
+            sscanf(v, "%d %d",
+                   &l->coin_score.coins[0],
+                   &l->coin_score.coins[1]);
+        else if (CASE("levelname"))
+            strcpy(l->name, v);
+        else if (CASE("version"))
+            l->version = atoi(v);
+        else if (CASE("author"))
+            strcpy(l->author, v);
+        else if (CASE("special"))
+            l->is_bonus = atoi(v);
+        /*else
+            fprintf(stderr, "File %s, ignore %s metadata.\n", l->file, c);*/
+
+        c = e;
     }
+    return 1;
 }
 
-static void level_init_hs(const char *filename)
+int level_load(const char *filename, struct level *level)
+/* Load the sol file 'filename' and fill the 'level' structure
+ * return 1 on success, 0 on error */
 {
-    char buf[MAXSTR];
-    FILE *fin;
-    int i = 0;
-
-    /* Set some sane values in case the scores file is missing. */
-
-    for (i = 0; i < MAXLVL; i++)
-    {
-        strcpy(score_v[i].time_n[0], "Hard");
-        strcpy(score_v[i].time_n[1], "Medium");
-        strcpy(score_v[i].time_n[2], "Easy");
-
-        score_v[i].time_t[0] = i ? 59999 : 359999;
-        score_v[i].time_t[1] = i ? 59999 : 359999;
-        score_v[i].time_t[2] = i ? 59999 : 359999;
-
-        score_v[i].time_c[0] = 0;
-        score_v[i].time_c[1] = 0;
-        score_v[i].time_c[2] = 0;
-
-        strcpy(score_v[i].coin_n[0], "Hard");
-        strcpy(score_v[i].coin_n[1], "Medium");
-        strcpy(score_v[i].coin_n[2], "Easy");
+    struct s_file sol; /* The solid file data */
+    int i;
+    int money; /* sum of coin value */
 
-        score_v[i].coin_t[0] = i ? 59999 : 359999;
-        score_v[i].coin_t[1] = i ? 59999 : 359999;
-        score_v[i].coin_t[2] = i ? 59999 : 359999;
+    /* raz level */
+    memset(level, 0, sizeof(struct level));
 
-        score_v[i].coin_c[0] = 0;
-        score_v[i].coin_c[1] = 0;
-        score_v[i].coin_c[2] = 0;
-    }
+    memset(&sol, 0, sizeof(sol));
 
-    /* Load the default high scores file. */
-
-    if ((fin = fopen(config_data(filename), "r")))
+    /* Try to load the sol file */
+    if (!sol_load_only_file(&sol, filename))
     {
-        for (i = 0; i < MAXLVL && fgets(buf, MAXSTR, fin); i++)
-            sscanf(buf, "%d %d %d %d %d %d",
-                   &score_v[i].time_t[0], &score_v[i].coin_c[0],
-                   &score_v[i].time_t[1], &score_v[i].coin_c[1],
-                   &score_v[i].time_t[2], &score_v[i].coin_c[2]);
-
-        fclose(fin);
+        fprintf(stderr, _("Error while loading level file '%s': "), filename);
+        if (errno)
+           perror(NULL);
+        else
+           fprintf(stderr, _("Not a valid level file\n"));
+        return 0;
     }
-}
 
-/*---------------------------------------------------------------------------*/
+    /* Set filename */
+    strcpy(level->file, filename);
 
-const char *level_shot(int i)
-{
-    return level_v[i].shot;
-}
+    /* Init hs with default values */
+    score_init_hs(&level->time_score, 59999, 0);
+    score_init_hs(&level->goal_score, 59999, 0);
+    score_init_hs(&level->coin_score, 59999, 0);
 
-const char *level_time_n(int i, int j)
-{
-    return score_v[i].time_n[j];
-}
+    /* Compute money and default max money */
+    money = 0;
+    for (i = 0; i < sol.cc; i++)
+        money += sol.cv[i].n;
+    level->coin_score.coins[0] = money;
 
-const char *level_coin_n(int i, int j)
-{
-    return score_v[i].coin_n[j];
-}
+    /* Scan sol metadata */
+    if (sol.ac > 0)
+        level_scan_metadata(level, sol.av);
 
-/*---------------------------------------------------------------------------*/
-/* Return the coin count for the Most Coins or Best Time score.              */
+    /* Compute initial hs default values */
 
-int level_coin_c(int i, int j)
-{
-    if (j < 0)
-        return score;
-    else
-        return score_v[i].coin_c[j];
-}
+#define HOP(t, c) \
+    if (t[2] c t[0]) \
+        t[0] = t[1] = t[2]; \
+    else if (t[2] c t[1]) \
+        t[1] = (t[0] + t[2]) / 2
 
-int level_time_c(int i, int j)
-{
-    return score_v[i].time_c[j];
-}
+    HOP(level->time_score.timer, <=);
+    HOP(level->goal_score.timer, <=);
+    HOP(level->coin_score.coins, >=);
 
-/*---------------------------------------------------------------------------*/
-/* Return the time for the Most Coins or Best Time score.                    */
-
-int level_coin_t(int i, int j)
-{
-    return score_v[i].coin_t[j];
-}
+    /* Free the sol structure, no more needed */
+    sol_free(&sol);
 
-int level_time_t(int i, int j)
-{
-    if (j < 0)
-        return level_v[i].time - curr_clock();
-    else
-        return score_v[i].time_t[j];
+    return 1;
 }
 
 /*---------------------------------------------------------------------------*/
 
-void level_init(const char *init_levels,
-                const char *init_scores,
-                const char *user_scores)
-{
-    memset(level_v, 0, sizeof (struct level) * MAXLVL);
-    memset(score_v, 0, sizeof (struct score) * MAXLVL);
-
-    level_init_rc(init_levels);
-    level_init_hs(init_scores);
-    level_load_hs(user_scores);
-
-    strncpy(scores_file, user_scores, MAXSTR);
-
-    level = 0;
-
-}
-
-void level_cheat(void)
-/* Open each level of the set */
-{
-    limit = count;
-}
-
-void level_free(void)
-{
-    int i;
-
-    level_store_hs(scores_file);
-
-    for (i = 0; i < count; i++)
-        if (glIsTexture(level_v[i].text))
-            glDeleteTextures(1, &level_v[i].text);
-
-    count = 0;
-}
-
-int level_exists(int i)
-{
-    return (0 < i && i < count);
-}
-
-int level_opened(int i)
-{
-    return level_exists(i) && (0 < i && i < count && i <= limit);
-}
-
-int level_locked(int i)
-{
-    return level_opened(i) && (i == limit) && (level_v[i].goal > 0);
-}
-
-/*---------------------------------------------------------------------------*/
-
-int curr_times_total(void) { return times_total; }
-int curr_coins_total(void) { return coins_total; }
-
-int curr_count(void) { return count; }
-int curr_score(void) { return score; }
-int curr_coins(void) { return coins; }
-int curr_balls(void) { return balls; }
-int curr_level(void) { return level; }
-int curr_goal (void) { return goal;  }
-
-/*---------------------------------------------------------------------------*/
-
-static int score_time_comp(const struct score *S, int i, int j)
-{
-    if (S->time_t[i] <  S->time_t[j])
-        return 1;
-
-    if (S->time_t[i] == S->time_t[j] &&
-        S->time_c[i] >  S->time_c[j])
-        return 1;
-
-    return 0;
-}
-
-static int score_coin_comp(const struct score *S, int i, int j)
-{
-    if (S->coin_c[i] >  S->coin_c[j])
-        return 1;
-
-    if (S->coin_c[i] == S->coin_c[j] &&
-        S->coin_t[i] <  S->coin_t[j])
-        return 1;
-
-    return 0;
+void level_dump_info(const struct level *l)
+/* This function dumps the info of a demo structure
+ * It's only a function for debugging, no need of I18N */
+{
+    printf("filename:        %s\n"
+           "name:            %s\n"
+           "version:         %d\n"
+           "author:          %s\n"
+           "time limit:      %d\n"
+           "goal count:      %d\n"
+           "time hs:         %d %d %d\n"
+           "goal hs:         %d %d %d\n"
+           "coin hs:         %d %d %d\n"
+           "message:         %s\n"
+           "background:      %s\n"
+           "gradiant:        %s\n"
+           "screenshot:      %s\n"
+           "song:            %s\n",
+           l->file, l->name, l->version, l->author,
+           l->time, l->goal,
+           l->time_score.timer[0],
+           l->time_score.timer[1],
+           l->time_score.timer[2],
+           l->goal_score.timer[0],
+           l->goal_score.timer[1],
+           l->goal_score.timer[2],
+           l->coin_score.coins[0],
+           l->coin_score.coins[1],
+           l->coin_score.coins[2],
+           l->message, l->back, l->grad, l->shot, l->song);
 }
 
 /*---------------------------------------------------------------------------*/
 
-static void score_time_swap(struct score *S, int i, int j)
+const char *mode_to_str(int m)
 {
-    char n[MAXNAM];
-    int  t;
-    int  c;
-
-    strncpy(n,            S->time_n[i], MAXNAM);
-    strncpy(S->time_n[i], S->time_n[j], MAXNAM);
-    strncpy(S->time_n[j], n,            MAXNAM);
-
-    t            = S->time_t[i];
-    S->time_t[i] = S->time_t[j];
-    S->time_t[j] = t;
-
-    c            = S->time_c[i];
-    S->time_c[i] = S->time_c[j];
-    S->time_c[j] = c;
-}
-
-static void score_coin_swap(struct score *S, int i, int j)
-{
-    char n[MAXNAM];
-    int  t;
-    int  c;
-
-    strncpy(n,            S->coin_n[i], MAXNAM);
-    strncpy(S->coin_n[i], S->coin_n[j], MAXNAM);
-    strncpy(S->coin_n[j], n,            MAXNAM);
-
-    t            = S->coin_t[i];
-    S->coin_t[i] = S->coin_t[j];
-    S->coin_t[j] = t;
-
-    c            = S->coin_c[i];
-    S->coin_c[i] = S->coin_c[j];
-    S->coin_c[j] = c;
-}
-
-/*---------------------------------------------------------------------------*/
-
-int level_replay(const char *filename)
-{
-    int r, time;
-
-    r = demo_replay_init(filename, &score, &coins, &balls, &goal, &time);
-
-    mode = (time == 0) ? MODE_PRACTICE : MODE_NORMAL;
-
-    return r;
-}
-
-int level_play_go(void)
-/* Start to play the current level */
-{
-    int time;
-    
-    coins  = 0;
-    goal   = (mode == MODE_PRACTICE) ? 0 : level_v[level].goal;
-    time   = (mode == MODE_PRACTICE) ? 0 : level_v[level].time;
-    
-    return demo_play_init(USER_REPLAY_FILE,
-                          level_v[level].file,
-                          level_v[level].back,
-                          level_v[level].grad,
-                          level_v[level].song,
-                          level_v[level].shot,
-                          time,
-                          goal, score, coins, balls);
-}
-
-void level_play(int i, int m)
-/* Prepare to play a level sequence from the `i'th level */
-{
-    mode = m;
-    level = i;
-
-    score = 0;
-    balls = 3;
-    coins_total = 0;
-    times_total = 0;
-}
-
-/*---------------------------------------------------------------------------*/
-
-void level_stop(int state)
-/* Stop the current playing level */
-{
-    int time;
-    
-    /* open next level */
-    if (state == GAME_GOAL && mode != MODE_PRACTICE && limit < level+1)
-       limit = level + 1;
-    
-    if (mode == MODE_CHALLENGE)
-    {
-       /* sum time */
-       times_total += level_v[level].time - curr_clock(); 
-           
-       /* sum coins */
-       if (state == GAME_GOAL)
-           coins_total += coins;
-
-       /* lose ball */
-        if (state == GAME_TIME || state == GAME_FALL)
-           balls--;
-    }
-
-    /* stop demo recording */  
-    time = (mode == MODE_PRACTICE) ? curr_clock() : level_v[level].time - curr_clock();
-    demo_play_stop(curr_coins(), time);
-}
-
-int level_dead(void)
-{
-    return (mode == MODE_CHALLENGE) && (balls <= 0);
-}
-
-int level_last(void)
-{
-    return (level + 1 == count);
-}
-
-void level_next(void)
-{
-    level++;
-}
-
-int level_sort(int *time_i, int *coin_i)
-{
-    int i, clock;
-    char player[MAXNAM];
-     
-    if (mode == MODE_PRACTICE)
-       clock = curr_clock();
-    else
-        clock = level_v[level].time - curr_clock();
-
-    config_get_s(CONFIG_PLAYER, player, MAXNAM);
-
-    /* Insert the time record into the high score list. */
-
-    strncpy(score_v[level].time_n[3], player, MAXNAM);
-    score_v[level].time_c[3] = coins;
-    score_v[level].time_t[3] = clock;
-
-    for (i = 2; i >= 0 && score_time_comp(score_v + level, i + 1, i); i--)
-    {
-        score_time_swap(score_v + level, i + 1, i);
-        *time_i = i;
-    }
-
-    /* Insert the coin record into the high score list. */
-
-    strncpy(score_v[level].coin_n[3], player, MAXNAM);
-    score_v[level].coin_c[3] = coins;
-    score_v[level].coin_t[3] = clock;
-
-    for (i = 2; i >= 0 && score_coin_comp(score_v + level, i + 1, i); i--)
-    {
-        score_coin_swap(score_v + level, i + 1, i);
-        *coin_i = i;
-    }
-
-    return (*time_i < 3 || *coin_i < 3);
-}
-
-int level_done(int *time_i, int *coin_i)
-{
-    int i;
-    char player[MAXNAM];
-
-    config_get_s(CONFIG_PLAYER, player, MAXNAM);
-
-    /* Note a global high score. */
-
-    strncpy(score_v[0].time_n[3], player, MAXNAM);
-    score_v[0].time_c[3] = coins_total;
-    score_v[0].time_t[3] = times_total;
-
-    strncpy(score_v[0].coin_n[3], player, MAXNAM);
-    score_v[0].coin_c[3] = coins_total;
-    score_v[0].coin_t[3] = times_total;
-
-    if (level == count)
+    switch (m)
     {
-        /* Insert the time record into the global high score list. */
-
-        for (i = 2; i >= 0 && score_time_comp(score_v, i + 1, i); i--)
-        {
-            score_time_swap(score_v, i + 1, i);
-            *time_i = i;
-        }
-
-        /* Insert the coin record into the global high score list. */
-
-        for (i = 2; i >= 0 && score_coin_comp(score_v, i + 1, i); i--)
-        {
-            score_coin_swap(score_v, i + 1, i);
-            *coin_i = i;
-        }
+    case MODE_CHALLENGE: return _("Challenge");
+    case MODE_NORMAL:    return _("Normal");
+    case MODE_PRACTICE:  return _("Practice");
+    case MODE_SINGLE:    return _("Single");
+    default:             return "???";
     }
-
-    return (*time_i < 3 || *coin_i < 3);
-}
-
-int level_score(int n)
-{
-    int sound = AUD_COIN;
-    int value = 0;
-
-    coins += n;
-
-    /* Pulse the coin counter based on the value of the grabbed coin. */
-
-    if      (n >= 10) hud_coin_pulse(2.00f);
-    else if (n >=  5) hud_coin_pulse(1.50f);
-    else              hud_coin_pulse(1.25f);
-
-    /* Check for goal open. */
-
-    if (goal > 0)
-    {
-        if      (n >= 10) hud_goal_pulse(2.00f);
-        else if (n >=  5) hud_goal_pulse(1.50f);
-        else              hud_goal_pulse(1.25f);
-
-        if (goal - n <= 0)
-        {
-            sound = AUD_SWITCH;
-            value = 1;
-            hud_goal_pulse(2.0f);
-        }
-
-        goal = (goal > n) ? (goal - n) : 0;
-    }
-
-    audio_play(sound, 1.f);
-    return value;
-}
-
-int level_count(void)
-{
-    if (mode != MODE_CHALLENGE)
-        return 0;
-    if (coins > 0)
-    {
-        score++;
-        coins--;
-
-        if (score % 100 == 0)
-        {
-            balls += 1;
-            audio_play(AUD_BALL, 1.0f);
-        }
-        return 1;
-    }
-    return 0;
 }
 
 /*---------------------------------------------------------------------------*/
 
-void level_name(int i, const char *name, int time_i, int coin_i)
+const char *state_to_str(int m)
 {
-    strncpy(score_v[i].time_n[time_i], name, MAXNAM);
-    strncpy(score_v[i].coin_n[coin_i], name, MAXNAM);
-}
-
-void level_snap(int i)
-{
-    char filename[MAXSTR];
-
-    /* Convert the level name to a BMP filename. */
-
-    memset(filename, 0, MAXSTR);
-    strncpy(filename, level_v[i].file, strcspn(level_v[i].file, "."));
-    strcat(filename, ".bmp");
-
-    /* Initialize the game for a snapshot. */
-
-    if (game_init(level_v[i].file, level_v[i].back, level_v[i].grad, 0, 1))
+    switch (m)
     {
-        /* Render the level and grab the screen. */
-
-        config_clear();
-        game_set_fly(1.f);
-        game_kill_fade();
-        game_draw(1, 0);
-        SDL_GL_SwapBuffers();
-
-        image_snap(filename);
+    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 "???";
     }
 }
 
 /*---------------------------------------------------------------------------*/
-
-int level_mode(void)
-{
-    return mode;
-}
-