Remove "angle" attribute of info_player_start. See
[neverball] / share / solid.c
index 54007bf..5699ab4 100644 (file)
@@ -1,4 +1,4 @@
-/*   
+/*
  * Copyright (C) 2003 Robert Kooima
  *
  * NEVERBALL is  free software; you can redistribute  it and/or modify
 
 #include "glext.h"
 #include "vec3.h"
-#include "geom.h"
-#include "image.h"
+#include "geom.h" /* Only for height constants! */
+#include "base_image.h"
 #include "solid.h"
-#include "config.h"
+#include "base_config.h"
 #include "binary.h"
 
+#define MAGIC 0x4F425251        /* SOL file magic number (should not change). */
+#define SOL_VERSION  5          /* SOL file format version (can change).      */
+
 #define LARGE 1.0e+5f
 
 /*---------------------------------------------------------------------------*/
@@ -65,9 +68,9 @@ static void sol_body_v(float v[3],
     }
 }
 
-static void sol_body_p(float p[3],
-                       const struct s_file *fp,
-                       const struct s_body *bp)
+void sol_body_p(float p[3],
+                const struct s_file *fp,
+                const struct s_body *bp)
 {
     float v[3];
 
@@ -89,560 +92,6 @@ static void sol_body_p(float p[3],
 
 /*---------------------------------------------------------------------------*/
 
-static int sol_enum_mtrl(const struct s_file *fp,
-                         const struct s_body *bp, int mi)
-{
-    int li, gi, c = 0;
-
-    /* Count all lump geoms with this material. */
-
-    for (li = 0; li < bp->lc; li++)
-    {
-        int g0 = fp->lv[bp->l0 + li].g0;
-        int gc = fp->lv[bp->l0 + li].gc;
-
-        for (gi = 0; gi < gc; gi++)
-            if (fp->gv[fp->iv[g0 + gi]].mi == mi)
-                c++;
-    }
-                    
-    /* Count all body geoms with this material. */
-
-    for (gi = 0; gi < bp->gc; gi++)
-        if (fp->gv[fp->iv[bp->g0 + gi]].mi == mi)
-            c++;
-
-    return c;
-}
-
-static int sol_enum_body(const struct s_file *fp,
-                           const struct s_body *bp, int fl)
-{
-    int mi, c = 0;
-
-    /* Count all geoms with this flag. */
-
-    for (mi = 0; mi < fp->mc; mi++)
-        if (fp->mv[mi].fl & fl)
-            c = c + sol_enum_mtrl(fp, bp, mi);
-
-    return c;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static void sol_draw_mtrl(const struct s_file *fp, int i)
-{
-    const struct s_mtrl *mp = fp->mv + i;
-
-    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   mp->a);
-    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   mp->d);
-    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  mp->s);
-    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  mp->e);
-    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp->h);
-
-    if (mp->fl & M_ENVIRONMENT)
-    {
-        glEnable(GL_TEXTURE_GEN_S);
-        glEnable(GL_TEXTURE_GEN_T);
-
-        glBindTexture(GL_TEXTURE_2D, mp->o);
-
-        glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
-        glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
-    }
-    else
-    {
-        glDisable(GL_TEXTURE_GEN_S);
-        glDisable(GL_TEXTURE_GEN_T);
-
-        glBindTexture(GL_TEXTURE_2D, mp->o);
-    }
-
-    if (mp->fl & M_ADDITIVE)
-        glBlendFunc(GL_ONE, GL_ONE);
-    else
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
-}
-
-static void sol_draw_bill(const struct s_file *fp,
-                          const struct s_bill *rp, float t)
-{
-    float T  = fmodf(t, rp->t) - rp->t / 2;
-
-    float w  = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
-    float h  = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
-
-    if (w > 0 && h > 0)
-    {
-        float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
-        float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
-        float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
-
-        glPushMatrix();
-        {
-            float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
-            float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
-
-            glRotatef(ry, 0.0f, 1.0f, 0.0f);
-            glRotatef(rx, 1.0f, 0.0f, 0.0f);
-            glTranslatef(0.0f, 0.0f, -rp->d);
-
-            if (rp->fl & B_FLAT)
-            {
-                glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
-                glRotatef(-ry,         0.0f, 0.0f, 1.0f);
-            }
-            if (rp->fl & B_EDGE)
-                glRotatef(-rx,         1.0f, 0.0f, 0.0f);
-
-            glRotatef(rz, 0.0f, 0.0f, 1.0f);
-
-            sol_draw_mtrl(fp, rp->mi);
-
-            glBegin(GL_QUADS);
-            {
-                glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, y0);
-                glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, y0);
-                glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, y1);
-                glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, y1);
-            }
-            glEnd();
-        }
-        glPopMatrix();
-    }
-}
-
-void sol_back(const struct s_file *fp, float n, float f, float t)
-{
-    int ri;
-
-    glPushAttrib(GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
-    {
-        /* Render all billboards in the given range. */
-
-        glDisable(GL_LIGHTING);
-        glDepthMask(GL_FALSE);
-
-        for (ri = 0; ri < fp->rc; ri++)
-            if (n <= fp->rv[ri].d && fp->rv[ri].d < f)
-                sol_draw_bill(fp, fp->rv + ri, t);
-    }
-    glPopAttrib();
-}
-
-/*---------------------------------------------------------------------------*/
-/*
- * The  following code  renders a  body in  a  ludicrously inefficient
- * manner.  It iterates the materials and scans the data structure for
- * geometry using each.  This  has the effect of absolutely minimizing
- * material  changes,  texture  bindings,  and  Begin/End  pairs,  but
- * maximizing trips through the data.
- *
- * However, this  is only done once  for each level.   The results are
- * stored in display lists.  Thus, it is well worth it.
- */
-
-static void sol_draw_geom(const struct s_file *fp,
-                          const struct s_geom *gp, int mi)
-{
-    if (gp->mi == mi)
-    {
-        const float *ui = fp->tv[gp->ti].u;
-        const float *uj = fp->tv[gp->tj].u;
-        const float *uk = fp->tv[gp->tk].u;
-
-        const float *ni = fp->sv[gp->si].n;
-        const float *nj = fp->sv[gp->sj].n;
-        const float *nk = fp->sv[gp->sk].n;
-
-        const float *vi = fp->vv[gp->vi].p;
-        const float *vj = fp->vv[gp->vj].p;
-        const float *vk = fp->vv[gp->vk].p;
-
-        glTexCoord2fv(ui);
-        glNormal3fv(ni);
-        glVertex3fv(vi);
-
-        glTexCoord2fv(uj);
-        glNormal3fv(nj);
-        glVertex3fv(vj);
-
-        glTexCoord2fv(uk);
-        glNormal3fv(nk);
-        glVertex3fv(vk);
-    }
-}
-
-static void sol_draw_lump(const struct s_file *fp,
-                          const struct s_lump *lp, int mi)
-{
-    int i;
-
-    for (i = 0; i < lp->gc; i++)
-        sol_draw_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
-}
-
-static void sol_draw_body(const struct s_file *fp,
-                          const struct s_body *bp, int fl)
-{
-    int mi, li, gi;
-
-    /* Iterate all materials of the correct opacity. */
-
-    for (mi = 0; mi < fp->mc; mi++)
-        if (fp->mv[mi].fl & fl)
-        {
-            if (sol_enum_mtrl(fp, bp, mi))
-            {
-                /* Set the material state. */
-
-                sol_draw_mtrl(fp, mi);
-
-                /* Render all geometry of that material. */
-
-                glBegin(GL_TRIANGLES);
-                {
-                    for (li = 0; li < bp->lc; li++)
-                        sol_draw_lump(fp, fp->lv + bp->l0 + li, mi);
-                    for (gi = 0; gi < bp->gc; gi++)
-                        sol_draw_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
-                }
-                glEnd();
-            }
-        }
-}
-
-static void sol_draw_list(const struct s_file *fp,
-                          const struct s_body *bp, GLuint list)
-{
-    float p[3];
-
-    sol_body_p(p, fp, bp);
-
-    glPushMatrix();
-    {
-        /* Translate a moving body. */
-
-        glTranslatef(p[0], p[1], p[2]);
-
-        /* Draw the body. */
-
-        glCallList(list);
-    }
-    glPopMatrix();
-}
-
-void sol_draw(const struct s_file *fp)
-{
-    int bi;
-
-    glPushAttrib(GL_TEXTURE_BIT      |
-                 GL_LIGHTING_BIT     |
-                 GL_COLOR_BUFFER_BIT |
-                 GL_DEPTH_BUFFER_BIT);
-    {
-        /* Render all obaque geometry into the color and depth buffers. */
-
-        for (bi = 0; bi < fp->bc; bi++)
-            if (fp->bv[bi].ol)
-                sol_draw_list(fp, fp->bv + bi, fp->bv[bi].ol);
-
-        /* Render all translucent geometry into only the color buffer. */
-
-        glDepthMask(GL_FALSE);
-
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
-        for (bi = 0; bi < fp->bc; bi++)
-            if (fp->bv[bi].tl)
-                sol_draw_list(fp, fp->bv + bi, fp->bv[bi].tl);
-    }
-    glPopAttrib();
-}
-
-void sol_refl(const struct s_file *fp)
-{
-    int bi;
-
-    glPushAttrib(GL_LIGHTING_BIT);
-    {
-        /* Render all reflective geometry into the color and depth buffers. */
-
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
-        for (bi = 0; bi < fp->bc; bi++)
-            if (fp->bv[bi].rl)
-                sol_draw_list(fp, fp->bv + bi, fp->bv[bi].rl);
-    }
-    glPopAttrib();
-}
-
-/*---------------------------------------------------------------------------*/
-
-static void sol_shad_geom(const struct s_file *fp,
-                          const struct s_geom *gp, int mi)
-{
-    if (gp->mi == mi)
-    {
-        const float *vi = fp->vv[gp->vi].p;
-        const float *vj = fp->vv[gp->vj].p;
-        const float *vk = fp->vv[gp->vk].p;
-
-        glTexCoord2f(vi[0], vi[2]);
-        glVertex3fv(vi);
-
-        glTexCoord2f(vj[0], vj[2]);
-        glVertex3fv(vj);
-
-        glTexCoord2f(vk[0], vk[2]);
-        glVertex3fv(vk);
-    }
-}
-
-static void sol_shad_lump(const struct s_file *fp,
-                          const struct s_lump *lp, int mi)
-{
-    int i;
-
-    for (i = 0; i < lp->gc; i++)
-        sol_shad_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
-}
-
-static void sol_shad_body(const struct s_file *fp,
-                          const struct s_body *bp, int fl)
-{
-    int mi, li, gi;
-
-    glBegin(GL_TRIANGLES);
-    {
-        for (mi = 0; mi < fp->mc; mi++)
-            if (fp->mv[mi].fl & fl)
-            {
-                for (li = 0; li < bp->lc; li++)
-                    sol_shad_lump(fp, fp->lv + bp->l0 + li, mi);
-                for (gi = 0; gi < bp->gc; gi++)
-                    sol_shad_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
-            }
-    }
-    glEnd();
-}
-
-static void sol_shad_list(const struct s_file *fp,
-                          const struct s_body *bp, GLuint list)
-{
-    float p[3];
-
-    sol_body_p(p, fp, bp);
-
-    glPushMatrix();
-    {
-        /* Translate a moving body. */
-
-        glTranslatef(p[0], p[1], p[2]);
-
-        /* Translate the shadow on a moving body. */
-
-        glMatrixMode(GL_TEXTURE);
-        {
-            glPushMatrix();
-            glTranslatef(p[0], p[2], 0.0f);
-        }
-        glMatrixMode(GL_MODELVIEW);
-        
-        /* Draw the body. */
-
-        glCallList(list);
-
-        /* Pop the shadow translation. */
-
-        glMatrixMode(GL_TEXTURE);
-        {
-            glPopMatrix();
-        }
-        glMatrixMode(GL_MODELVIEW);
-    }
-    glPopMatrix();
-}
-
-void sol_shad(const struct s_file *fp)
-{
-    int bi;
-
-    glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT);
-    {
-        /* Render all shadowed geometry. */
-
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
-        glDepthFunc(GL_LEQUAL);
-        glDepthMask(GL_FALSE);
-
-        for (bi = 0; bi < fp->bc; bi++)
-            if (fp->bv[bi].sl)
-                sol_shad_list(fp, fp->bv + bi, fp->bv[bi].sl);
-    }
-    glPopAttrib();
-}
-
-/*---------------------------------------------------------------------------*/
-
-static void sol_load_objects(struct s_file *fp, int s)
-{
-    int i;
-
-    for (i = 0; i < fp->bc; i++)
-    {
-        struct s_body *bp = fp->bv + i;
-
-        /* Draw all opaque geometry. */
-
-        if (sol_enum_body(fp, bp, M_OPAQUE | M_ENVIRONMENT))
-        {
-            fp->bv[i].ol = glGenLists(1);
-            
-            glNewList(fp->bv[i].ol, GL_COMPILE);
-            {
-                sol_draw_body(fp, fp->bv + i, M_OPAQUE | M_ENVIRONMENT);
-            }
-            glEndList();
-        }
-        else fp->bv[i].ol = 0;
-
-        /* Draw all translucent geometry. */
-
-        if (sol_enum_body(fp, bp, M_TRANSPARENT))
-        {
-            fp->bv[i].tl = glGenLists(1);
-
-            glNewList(fp->bv[i].tl, GL_COMPILE);
-            {
-                sol_draw_body(fp, fp->bv + i, M_TRANSPARENT);
-            }
-            glEndList();
-        }
-        else fp->bv[i].tl = 0;
-
-        /* Draw all reflective geometry. */
-
-        if (sol_enum_body(fp, bp, M_REFLECTIVE))
-        {
-            fp->bv[i].rl = glGenLists(1);
-
-            glNewList(fp->bv[i].rl, GL_COMPILE);
-            {
-                sol_draw_body(fp, fp->bv + i, M_REFLECTIVE);
-            }
-            glEndList();
-        }
-        else fp->bv[i].rl = 0;
-
-        /* Draw all shadowed geometry. */
-
-        if (s && sol_enum_body(fp, bp, M_SHADOWED))
-        {
-            fp->bv[i].sl = glGenLists(1);
-
-            glNewList(fp->bv[i].sl, GL_COMPILE);
-            {
-                sol_shad_body(fp, fp->bv + i, M_SHADOWED);
-            }
-            glEndList();
-        }
-        else fp->bv[i].sl = 0;
-    }
-}
-
-static SDL_Surface *sol_find_texture(const char *name)
-{
-    char png[MAXSTR];
-    char tga[MAXSTR];
-    char jpg[MAXSTR];
-    SDL_Surface *s;
-
-    /* Prefer a lossless copy of the texture over a lossy compression. */
-
-    strncpy(png, name, PATHMAX); strcat(png, ".png");
-    strncpy(tga, name, PATHMAX); strcat(tga, ".tga");
-    strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
-
-    /* Check for a PNG. */
-
-    if ((s = IMG_Load(config_data(png))))
-        return s;
-
-    /* Check for a TGA, swapping channels if found. */
-
-    if ((s = IMG_Load(config_data(tga))))
-    {
-        image_swab(s);
-        return s;
-    }
-
-    /* Check for a JPG. */
-
-    if ((s = IMG_Load(config_data(jpg))))
-        return s;
-
-    return NULL;
-}
-
-static void sol_load_textures(struct s_file *fp, int k)
-{
-    SDL_Surface *s;
-    SDL_Surface *d;
-
-    int i;
-
-    for (i = 0; i < fp->mc; i++)
-        if ((s = sol_find_texture(fp->mv[i].f)))
-        {
-            GLenum f = (s->format->BitsPerPixel == 32) ? GL_RGBA : GL_RGB;
-
-            glGenTextures(1, &fp->mv[i].o);
-            glBindTexture(GL_TEXTURE_2D, fp->mv[i].o);
-
-            if (k > 1)
-            {
-                /* Create a new buffer and copy the scaled image to it. */
-
-                if ((d = image_scale(s, k)))
-                {
-                    glTexImage2D(GL_TEXTURE_2D, 0, f, d->w, d->h, 0, f,
-                                 GL_UNSIGNED_BYTE, d->pixels);
-                    SDL_FreeSurface(d);
-                }
-            }
-            else
-                glTexImage2D(GL_TEXTURE_2D, 0, f, s->w, s->h, 0, f,
-                             GL_UNSIGNED_BYTE, s->pixels);
-
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-            /* Set the texture to clamp or repeat based on material type. */
-
-            if (fp->mv[i].fl & M_CLAMPED)
-            {
-                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
-                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
-            }
-            else
-            {
-                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-            }
-
-            SDL_FreeSurface(s);
-        }
-}
-
-/*---------------------------------------------------------------------------*/
-
 static void sol_load_mtrl(FILE *fin, struct s_mtrl *mp)
 {
     get_array(fin,  mp->a, 4);
@@ -741,6 +190,8 @@ static void sol_load_goal(FILE *fin, struct s_goal *zp)
 {
     get_array(fin,  zp->p, 3);
     get_float(fin, &zp->r);
+    get_index(fin, &zp->s);
+    get_index(fin, &zp->c);
 }
 
 static void sol_load_swch(FILE *fin, struct s_swch *xp)
@@ -752,6 +203,7 @@ static void sol_load_swch(FILE *fin, struct s_swch *xp)
     get_float(fin, &xp->t);
     get_index(fin, &xp->f0);
     get_index(fin, &xp->f);
+    get_index(fin, &xp->i);
 }
 
 static void sol_load_bill(FILE *fin, struct s_bill *rp)
@@ -789,9 +241,17 @@ static void sol_load_view(FILE *fin, struct s_view *wp)
     get_array(fin,  wp->q, 3);
 }
 
-static void sol_load_file(FILE *fin, struct s_file *fp)
+static int sol_load_file(FILE *fin, struct s_file *fp)
 {
     int i;
+    int magic;
+    int version;
+
+    get_index(fin, &magic);
+    get_index(fin, &version);
+
+    if (magic != MAGIC || version != SOL_VERSION)
+        return 0;
 
     get_index(fin, &fp->mc);
     get_index(fin, &fp->vc);
@@ -872,23 +332,21 @@ static void sol_load_file(FILE *fin, struct s_file *fp)
     for (i = 0; i < fp->ic; i++) get_index(fin, fp->iv + i);
 
     if (fp->ac) fread(fp->av, 1, fp->ac, fin);
+
+    return 1;
 }
 
-int sol_load(struct s_file *fp, const char *filename, int k, int s)
+int sol_load_only_file(struct s_file *fp, const char *filename)
 {
     FILE *fin;
+    int res = 0;
 
     if ((fin = fopen(filename, FMODE_RB)))
     {
-        sol_load_file(fin, fp);
-        sol_load_textures(fp, k);
-        sol_load_objects (fp, s);
-
+        res = sol_load_file(fin, fp);
         fclose(fin);
-
-        return 1;
     }
-    return 0;
+    return res;
 }
 
 /*---------------------------------------------------------------------------*/
@@ -991,6 +449,8 @@ static void sol_stor_goal(FILE *fout, struct s_goal *zp)
 {
     put_array(fout,  zp->p, 3);
     put_float(fout, &zp->r);
+    put_index(fout, &zp->s);
+    put_index(fout, &zp->c);
 }
 
 static void sol_stor_swch(FILE *fout, struct s_swch *xp)
@@ -1002,6 +462,7 @@ static void sol_stor_swch(FILE *fout, struct s_swch *xp)
     put_float(fout, &xp->t);
     put_index(fout, &xp->f0);
     put_index(fout, &xp->f);
+    put_index(fout, &xp->i);
 }
 
 static void sol_stor_bill(FILE *fout, struct s_bill *rp)
@@ -1042,6 +503,11 @@ static void sol_stor_view(FILE *fout, struct s_view *wp)
 static void sol_stor_file(FILE *fin, struct s_file *fp)
 {
     int i;
+    int magic   = MAGIC;
+    int version = SOL_VERSION;
+
+    put_index(fin, &magic);
+    put_index(fin, &version);
 
     put_index(fin, &fp->mc);
     put_index(fin, &fp->vc);
@@ -1103,24 +569,6 @@ int sol_stor(struct s_file *fp, const char *filename)
 
 void sol_free(struct s_file *fp)
 {
-    int i;
-
-    for (i = 0; i < fp->mc; i++)
-    {
-        if (glIsTexture(fp->mv[i].o))
-            glDeleteTextures(1, &fp->mv[i].o);
-    }
-
-    for (i = 0; i < fp->bc; i++)
-    {
-        if (glIsList(fp->bv[i].ol))
-            glDeleteLists(fp->bv[i].ol, 1);
-        if (glIsList(fp->bv[i].tl))
-            glDeleteLists(fp->bv[i].tl, 1);
-        if (glIsList(fp->bv[i].rl))
-            glDeleteLists(fp->bv[i].rl, 1);
-    }
-
     if (fp->mv) free(fp->mv);
     if (fp->vv) free(fp->vv);
     if (fp->ev) free(fp->ev);
@@ -1334,7 +782,7 @@ static float sol_bounce(struct s_ball *up,
     v_mad(u, w, n, -wn);
     v_mad(v, v, n, -vn);
     v_mad(v, v, u, +km * dt);
-    v_mad(v, v, n, xn + yn); 
+    v_mad(v, v, n, xn + yn);
 
     v_mad(p, q, n, up->r);
 
@@ -1574,7 +1022,8 @@ static float sol_test_lump(float dt,
                            const float o[3],
                            const float w[3])
 {
-    float U[3], u, t = dt;
+    float U[3] = {0.0f, 0.0f, 0.0f}; /* init value only to avoid gcc warnings */
+    float u, t = dt;
     int i;
 
     /* Short circuit a non-solid lump. */
@@ -1594,7 +1043,7 @@ static float sol_test_lump(float dt,
                 t = u;
             }
         }
+
     /* Test all edges */
 
     if (up->r > 0.0f)
@@ -1834,7 +1283,7 @@ int sol_coin_test(struct s_file *fp, float *p, float coin_r)
     return 0;
 }
 
-int sol_goal_test(struct s_file *fp, float *p, int ui)
+struct s_goal *sol_goal_test(struct s_file *fp, float *p, int ui)
 {
     const float *ball_p = fp->uv[ui].p;
     const float  ball_r = fp->uv[ui].r;
@@ -1856,17 +1305,23 @@ int sol_goal_test(struct s_file *fp, float *p, int ui)
             p[1] = fp->zv[zi].p[1];
             p[2] = fp->zv[zi].p[2];
 
-            return 1;
+            return &fp->zv[zi];
         }
     }
-    return 0;
+    return NULL;
 }
 
 int sol_jump_test(struct s_file *fp, float *p, int ui)
+/* Test if the ball ui is inside a jump. */
+/* Return 1 if yes and fill p with the destination position. */
+/* Return 0 if no. */
+/* Return 2 if the ball is on the border of a jump. */
 {
     const float *ball_p = fp->uv[ui].p;
     const float  ball_r = fp->uv[ui].r;
     int ji;
+    float l;
+    int res = 0;
 
     for (ji = 0; ji < fp->jc; ji++)
     {
@@ -1876,26 +1331,36 @@ int sol_jump_test(struct s_file *fp, float *p, int ui)
         r[1] = ball_p[2] - fp->jv[ji].p[2];
         r[2] = 0;
 
-        if (v_len(r) < fp->jv[ji].r - ball_r &&
+        l = v_len(r) - fp->jv[ji].r;
+        if (l < 0 &&
             ball_p[1] > fp->jv[ji].p[1] &&
             ball_p[1] < fp->jv[ji].p[1] + JUMP_HEIGHT / 2)
         {
-            p[0] = fp->jv[ji].q[0] + (ball_p[0] - fp->jv[ji].p[0]);
-            p[1] = fp->jv[ji].q[1] + (ball_p[1] - fp->jv[ji].p[1]);
-            p[2] = fp->jv[ji].q[2] + (ball_p[2] - fp->jv[ji].p[2]);
+            if (l < - ball_r )
+            {
+                p[0] = fp->jv[ji].q[0] + (ball_p[0] - fp->jv[ji].p[0]);
+                p[1] = fp->jv[ji].q[1] + (ball_p[1] - fp->jv[ji].p[1]);
+                p[2] = fp->jv[ji].q[2] + (ball_p[2] - fp->jv[ji].p[2]);
 
-            return 1;
+                return 1;
+            }
+            else
+                res = 2;
         }
     }
-    return 0;
+    return res;
 }
 
-int sol_swch_test(struct s_file *fp, int flag, int ui)
+int sol_swch_test(struct s_file *fp, int ui)
+/* In the SOL fp, test and process the event the ball ui enters a switch.
+ * Return 1 if a visible switch is activated, return 0 otherwise (no switch is
+ * activated or only invisible switchs) */
 {
     const float *ball_p = fp->uv[ui].p;
     const float  ball_r = fp->uv[ui].r;
     int xi;
-    int f = 1;
+    float l;
+    int res = 0; /* result */
 
     for (xi = 0; xi < fp->xc; xi++)
     {
@@ -1909,15 +1374,20 @@ int sol_swch_test(struct s_file *fp, int flag, int ui)
             r[1] = ball_p[2] - xp->p[2];
             r[2] = 0;
 
-            if (v_len(r)  < xp->r - ball_r &&
+            l = v_len(r) - xp->r;
+            if (l < ball_r &&
                 ball_p[1] > xp->p[1] &&
                 ball_p[1] < xp->p[1] + SWCH_HEIGHT / 2)
             {
-                if (flag)
+                if (!xp->e && l < - ball_r)
                 {
                     int pi = xp->pi;
                     int pj = xp->pi;
 
+                    /* The ball enter */
+                    if (xp->t0 == 0)
+                        xp->e = 1;
+
                     /* Toggle the state, update the path. */
 
                     xp->f = xp->f ? 0 : 1;
@@ -1937,12 +1407,18 @@ int sol_swch_test(struct s_file *fp, int flag, int ui)
 
                     if (xp->f != xp->f0)
                         xp->t  = xp->t0;
+
+                    /* If visible, set the result */
+                    if (!xp->i)
+                        res = 1;
                 }
-                f = 0;
             }
+            else if (xp->e)
+                /* A ball go out */
+                xp->e = 0;
         }
     }
-    return f;
+    return res;
 }
 
 /*---------------------------------------------------------------------------*/