Index: share/config.c =================================================================== --- share/config.c (.../trunk) (revision 1922) +++ share/config.c (.../branches/putt-collisions) (revision 1922) @@ -115,7 +115,6 @@ config_set_s(CONFIG_PLAYER, DEFAULT_PLAYER); config_set_s(CONFIG_BALL, DEFAULT_BALL); config_set_s(CONFIG_WIIMOTE_ADDR, DEFAULT_WIIMOTE_ADDR); - config_set_d(CONFIG_CHEAT, DEFAULT_CHEAT); config_set_d(CONFIG_STATS, DEFAULT_STATS); config_set_d(CONFIG_UNIFORM, DEFAULT_UNIFORM); config_set_d(CONFIG_KEY_FORWARD, DEFAULT_KEY_FORWARD); @@ -127,6 +126,9 @@ 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); + config_set_s(CONFIG_BALL_GAMMA, DEFAULT_BALL_GAMMA); + config_set_d(CONFIG_BALL_COLLISIONS, DEFAULT_BALL_COLLISIONS); + config_set_d(CONFIG_CHEAT, DEFAULT_CHEAT); } void config_load(void) @@ -256,16 +258,22 @@ else if (strcmp(key, "wiimote_addr") == 0) config_set_s(CONFIG_WIIMOTE_ADDR, val); - else if (strcmp(key, "cheat") == 0) - config_set_d(CONFIG_CHEAT, atoi(val)); - else if (strcmp(key, "stats") == 0) - config_set_d(CONFIG_STATS, atoi(val)); - else if (strcmp(key, "uniform") == 0) + else if (strcmp(key, "stats") == 0) + config_set_d(CONFIG_STATS, atoi(val)); + else if (strcmp(key, "uniform") == 0) 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)); + + else if (strcmp(key, "gamma") == 0) + config_set_s(CONFIG_BALL_GAMMA, val); + else if (strcmp(key, "ball_collisions") == 0) + config_set_d(CONFIG_BALL_COLLISIONS, atoi(val)); + + else if (strcmp(key, "cheat") == 0) + config_set_d(CONFIG_CHEAT, atoi(val)); } fclose(fp); @@ -388,17 +396,24 @@ 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]); + fprintf(fp, "player %s\n", option_s[CONFIG_PLAYER]); if (strlen(option_s[CONFIG_BALL]) > 0) - fprintf(fp, "ball_file %s\n", option_s[CONFIG_BALL]); + fprintf(fp, "ball_file %s\n", option_s[CONFIG_BALL]); if (strlen(option_s[CONFIG_WIIMOTE_ADDR]) > 0) - fprintf(fp, "wiimote_addr %s\n", option_s[CONFIG_WIIMOTE_ADDR]); + 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, "lock_goals %d\n", option_d[CONFIG_LOCK_GOALS]); + if (strlen(option_s[CONFIG_BALL_GAMMA]) > 0) + fprintf(fp, "gamma %s\n", + option_s[CONFIG_BALL_GAMMA]); + + fprintf(fp, "ball_collisions %d\n", + option_d[CONFIG_BALL_COLLISIONS]); + if (config_cheat()) fprintf(fp, "cheat %d\n", option_d[CONFIG_CHEAT]); Index: share/config.h =================================================================== --- share/config.h (.../trunk) (revision 1922) +++ share/config.h (.../branches/putt-collisions) (revision 1922) @@ -86,11 +86,12 @@ CONFIG_KEY_PAUSE, CONFIG_KEY_RESTART, CONFIG_KEY_SCORE_NEXT, - CONFIG_CHEAT, CONFIG_STATS, CONFIG_UNIFORM, CONFIG_SCREENSHOT, CONFIG_LOCK_GOALS, + CONFIG_BALL_COLLISIONS, + CONFIG_CHEAT, CONFIG_OPTION_D_COUNT }; @@ -99,6 +100,7 @@ CONFIG_PLAYER, CONFIG_BALL, CONFIG_WIIMOTE_ADDR, + CONFIG_BALL_GAMMA, CONFIG_OPTION_S_COUNT }; @@ -115,7 +117,7 @@ #define DEFAULT_REFLECTION 1 #define DEFAULT_MULTISAMPLE 0 #define DEFAULT_MIPMAP 0 -#define DEFAULT_ANISO 0 +#define DEFAULT_ANISO 1 #define DEFAULT_BACKGROUND 1 #define DEFAULT_SHADOW 1 #define DEFAULT_AUDIO_BUFF AUDIO_BUFF_HI @@ -156,7 +158,6 @@ #define DEFAULT_ROTATE_FAST 200 #define DEFAULT_PLAYER "" #define DEFAULT_BALL "ball/basic-ball/basic-ball" -#define DEFAULT_CHEAT 0 #define DEFAULT_KEY_FORWARD SDLK_UP #define DEFAULT_KEY_BACKWARD SDLK_DOWN #define DEFAULT_KEY_LEFT SDLK_LEFT @@ -167,7 +168,10 @@ #define DEFAULT_STATS 0 #define DEFAULT_UNIFORM 0 #define DEFAULT_SCREENSHOT 0 +#define DEFAULT_BALL_GAMMA "0.78" +#define DEFAULT_BALL_COLLISIONS 0 #define DEFAULT_LOCK_GOALS 0 +#define DEFAULT_CHEAT 0 /*---------------------------------------------------------------------------*/ Index: share/mapc.c =================================================================== --- share/mapc.c (.../trunk) (revision 1922) +++ share/mapc.c (.../branches/putt-collisions) (revision 1922) @@ -61,6 +61,7 @@ #define MAXX 1024 #define MAXR 2048 #define MAXU 1024 +#define MAXY 1024 #define MAXW 1024 #define MAXD 1024 #define MAXA 16384 @@ -153,6 +154,11 @@ return (fp->uc < MAXU) ? fp->uc++ : overflow("ball"); } +static int incy(struct s_file *fp) +{ + return (fp->yc < MAXY) ? fp->yc++ : overflow("ball"); +} + static int incw(struct s_file *fp) { return (fp->wc < MAXW) ? fp->wc++ : overflow("view"); @@ -186,6 +192,7 @@ fp->xc = 0; fp->rc = 0; fp->uc = 0; + fp->yc = 0; fp->wc = 0; fp->dc = 0; fp->ac = 0; @@ -207,6 +214,7 @@ fp->xv = (struct s_swch *) calloc(MAXX, sizeof (struct s_swch)); fp->rv = (struct s_bill *) calloc(MAXR, sizeof (struct s_bill)); fp->uv = (struct s_ball *) calloc(MAXU, sizeof (struct s_ball)); + fp->yv = (struct s_ball *) calloc(MAXY, sizeof (struct s_ball)); fp->wv = (struct s_view *) calloc(MAXW, sizeof (struct s_view)); fp->dv = (struct s_dict *) calloc(MAXD, sizeof (struct s_dict)); fp->av = (char *) calloc(MAXA, sizeof (char)); @@ -1204,6 +1212,9 @@ up->p[1] = 0.0f; up->p[2] = 0.0f; up->r = 0.25f; + up->m = 1; + up->n = 0; + up->c = 0; for (i = 0; i < c; i++) { @@ -1220,11 +1231,65 @@ up->p[1] = +(float) (z - 24) / SCALE; up->p[2] = -(float) (y) / SCALE; } + + if (strcmp(k[i], "mobile") == 0) + sscanf(v[i], "%d", &up->m); + + if (strcmp(k[i], "return") == 0) + sscanf(v[i], "%d", &up->n); + + if (strcmp(k[i], "collisions") == 0) + sscanf(v[i], "%d", &up->c); } up->p[1] += up->r + SMALL; } +static void make_abal(struct s_file *fp, + char k[][MAXSTR], + char v[][MAXSTR], int c) +{ + int i, yi = incy(fp); + + struct s_ball *yp = fp->yv + yi; + + yp->p[0] = 0.0f; + yp->p[1] = 0.0f; + yp->p[2] = 0.0f; + yp->r = 0.25f; + yp->m = 1; + yp->n = 0; + yp->c = 0; + + for (i = 0; i < c; i++) + { + if (strcmp(k[i], "radius") == 0) + sscanf(v[i], "%f", &yp->r); + + if (strcmp(k[i], "origin") == 0) + { + int x = 0, y = 0, z = 0; + + sscanf(v[i], "%d %d %d", &x, &y, &z); + + yp->p[0] = +(float) (x) / SCALE; + yp->p[1] = +(float) (z - 24) / SCALE; + yp->p[2] = -(float) (y) / SCALE; + } + + if (strcmp(k[i], "mobile") == 0) + sscanf(v[i], "%d", &yp->m); + + if (strcmp(k[i], "return") == 0) + sscanf(v[i], "%d", &yp->n); + + if (strcmp(k[i], "collisions") == 0) + sscanf(v[i], "%d", &yp->c); + } + + yp->p[1] += yp->r + SMALL; +} + /*---------------------------------------------------------------------------*/ static void read_ent(struct s_file *fp, FILE *fin) @@ -1254,6 +1319,7 @@ if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c); if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c); if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c); + if (!strcmp(v[i], "info_notnull")) make_abal(fp, k, v, c); if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c); if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c); if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c); @@ -2313,10 +2379,10 @@ name, n, m, c, #endif name, n, c, - p->mc, p->vc, p->ec, p->sc, p->tc, - p->gc, p->lc, p->pc, p->nc, p->bc, - p->hc, p->zc, p->wc, p->jc, p->xc, - p->rc, p->uc, p->ac, p->dc, p->ic); + p->mc, p->vc, p->ec, p->sc, p->tc, + p->gc, p->lc, p->pc, p->nc, p->bc, + p->hc, p->zc, p->wc, p->jc, p->xc, + p->rc, p->uc + p->yc, p->ac, p->dc, p->ic); } int main(int argc, char *argv[]) @@ -2370,4 +2436,3 @@ return 0; } - Index: share/solid.c =================================================================== --- share/solid.c (.../trunk) (revision 1922) +++ share/solid.c (.../branches/putt-collisions) (revision 1922) @@ -24,15 +24,20 @@ #include "solid.h" #include "base_config.h" #include "binary.h" +#include "config.h" #define MAGIC 0x4c4f53af -#define SOL_VERSION 6 +#define SOL_VERSION 7 #define LARGE 1.0e+5f #define SMALL 1.0e-3f /*---------------------------------------------------------------------------*/ +static int amplitude = 0.f; + +/*---------------------------------------------------------------------------*/ + static float erp(float t) { return 3.0f * t * t - 2.0f * t * t * t; @@ -236,7 +241,12 @@ { get_array(fin, bp->p, 3); get_float(fin, &bp->r); + get_index(fin, &bp->m); + get_index(fin, &bp->n); + get_index(fin, &bp->c); + v_cpy(bp->O, bp->p); + bp->e[0][0] = bp->E[0][0] = 1.0f; bp->e[0][1] = bp->E[0][1] = 0.0f; bp->e[0][2] = bp->E[0][2] = 0.0f; @@ -292,6 +302,7 @@ get_index(fin, &fp->xc); get_index(fin, &fp->rc); get_index(fin, &fp->uc); + get_index(fin, &fp->yc); get_index(fin, &fp->wc); get_index(fin, &fp->ic); @@ -329,6 +340,8 @@ fp->rv = (struct s_bill *) calloc(fp->rc, sizeof (struct s_bill)); if (fp->uc) fp->uv = (struct s_ball *) calloc(fp->uc, sizeof (struct s_ball)); + if (fp->yc) + fp->yv = (struct s_ball *) calloc(fp->yc, sizeof (struct s_ball)); if (fp->wc) fp->wv = (struct s_view *) calloc(fp->wc, sizeof (struct s_view)); if (fp->dc) @@ -356,6 +369,7 @@ for (i = 0; i < fp->xc; i++) sol_load_swch(fin, fp->xv + i); for (i = 0; i < fp->rc; i++) sol_load_bill(fin, fp->rv + i); for (i = 0; i < fp->uc; i++) sol_load_ball(fin, fp->uv + i); + for (i = 0; i < fp->yc; i++) sol_load_ball(fin, fp->yv + i); for (i = 0; i < fp->wc; i++) sol_load_view(fin, fp->wv + i); for (i = 0; i < fp->ic; i++) get_index(fin, fp->iv + i); @@ -393,10 +407,11 @@ get_index(fin, &fp->xc); get_index(fin, &fp->rc); get_index(fin, &fp->uc); + get_index(fin, &fp->yc); get_index(fin, &fp->wc); get_index(fin, &fp->ic); #endif - fseek(fin, 18 * 4, SEEK_CUR); + fseek(fin, 19 * 4, SEEK_CUR); if (fp->ac) { @@ -584,6 +599,9 @@ { put_array(fout, bp->p, 3); put_float(fout, &bp->r); + put_index(fout, &bp->m); + put_index(fout, &bp->n); + put_index(fout, &bp->c); } static void sol_stor_view(FILE *fout, struct s_view *wp) @@ -625,6 +643,7 @@ put_index(fin, &fp->xc); put_index(fin, &fp->rc); put_index(fin, &fp->uc); + put_index(fin, &fp->yc); put_index(fin, &fp->wc); put_index(fin, &fp->ic); @@ -647,6 +666,7 @@ for (i = 0; i < fp->xc; i++) sol_stor_swch(fin, fp->xv + i); for (i = 0; i < fp->rc; i++) sol_stor_bill(fin, fp->rv + i); for (i = 0; i < fp->uc; i++) sol_stor_ball(fin, fp->uv + i); + for (i = 0; i < fp->yc; i++) sol_stor_ball(fin, fp->yv + i); for (i = 0; i < fp->wc; i++) sol_stor_view(fin, fp->wv + i); for (i = 0; i < fp->ic; i++) put_index(fin, fp->iv + i); } @@ -686,6 +706,7 @@ if (fp->xv) free(fp->xv); if (fp->rv) free(fp->rv); if (fp->uv) free(fp->uv); + if (fp->yv) free(fp->yv); if (fp->wv) free(fp->wv); if (fp->dv) free(fp->dv); if (fp->iv) free(fp->iv); @@ -914,11 +935,132 @@ v_mad(p, q, n, up->r); /* Return the "energy" of the impact, to determine the sound amplitude. */ - return fabsf(v_dot(n, d)); } /* + * Compute the new linear velocities of two colliding balls. + * t gives the time after which they collide. + */ +static float sol_bounce_ball(struct s_ball *up, + struct s_ball *u2p, + const float t) +{ + float r_rel[3], v_rel[3], v1_par[3], v1_perp[3], v2_par[3], v2_perp[3], + u[3]; + float v11[3], v12[3], v21[3], v22[3]; + float *p1 = up->p, *v1 = up->v, *p2 = u2p->p, *v2 = u2p->v; + float inertia, factor, gamma; + const int u1 = up->m; + const int u2 = u2p->m; + + { + char gamma_str[MAXNAM]; + config_get_s(CONFIG_BALL_GAMMA, gamma_str, MAXNAM); + gamma = atof(gamma_str); + } + + /* Correct positions up to the collision */ + v_mad(p1, p1, v1, t); + v_mad(p2, p2, v2, t); + + /* Floating point precision */ + if (!(p1[1] - p2[1] > 0.001f ) && !(p2[1] - p1[1] > 0.001f)) + { + if (p1[1] > p2[1]) + p2[1] = p1[1]; + else + p1[1] = p2[1]; + } + + /* Hack: prevent losing balls */ + v_sub(v_rel, v2, v1); + if (v_len(v_rel) < 0.001f) + { + return 0.0f; + } + + /* r_rel is the unit vector from p1 to p2 */ + v_sub(r_rel, p2, p1); + v_nrm(r_rel, r_rel); + + /* + * project velocities upon r_rel to get components parallel + * to r_rel - only these will be changed in the collision + */ + factor = v_dot(v1, r_rel); + v_scl(v1_par, r_rel, factor); + v_sub(v1_perp, v1, v1_par); + + factor = v_dot(v2, r_rel); + v_scl(v2_par, r_rel, factor); + v_sub(v2_perp, v2, v2_par); + + /* u is used to calculate the "energy" of the impact */ + v_sub(u, v2_par, v1_par); + + /* Ensure immobile balls don't travel inside of each other */ + if (!up->m || !u2p->m) + gamma = 0.78f; + + /* + * New parallel velocities follow from momentum conservation, + * coefficient of restitution GAMMA, mass ratio inertia + */ + inertia = pow(up->r / u2p->r, 3); + + if (!u2p->m) + { + v_scl(v11, v1_par, -gamma); + v_scl(v12, v2_par, gamma + 1.0f); + v_add(v1_par, v11, v12); + } + + else if (!up->m) + { + v_scl(v21, v1_par, gamma + 1.0f); + v_scl(v22, v2_par, -gamma); + v_add(v2_par, v21, v22); + } + + else + { + v_scl(v11, v1_par, (inertia - gamma) / (inertia + 1.0f)); + v_scl(v12, v1_par, (gamma + 1.0f) * inertia / (inertia + 1.0f)); + v_scl(v21, v2_par, (gamma + 1.0f) / (inertia + 1.0f)); + v_scl(v22, v2_par, (1.0f - gamma * inertia) / (inertia + 1.0f)); + v_add(v1_par, v11, v21); + v_add(v2_par, v12, v22); + } + + if (up->m) + v_add(v1, v1_par, v1_perp); + if (u2p->m) + v_add(v2, v2_par, v2_perp); + + /* Hack: prevent accidental spinning while the ball is stationary */ + if (v_len(v1) < 0.01f && u1) + { + up->w[0] = 0.0f; + up->w[1] = 0.0f; + up->w[2] = 0.0f; + } + + if (v_len(v2) < 0.01f && u2) + { + u2p->w[0] = 0.0f; + u2p->w[1] = 0.0f; + u2p->w[2] = 0.0f; + } + + /* + * Return the length of the relative velocity parallel + * to the line of impact + */ + return fabsf(v_len(u)); +} + +/* * Compute the new angular velocity and orientation of a ball pendulum. * A gives the accelleration of the ball. G gives the gravity vector. */ @@ -1039,6 +1181,18 @@ { int i; + for (i = 0; i < fp->yc; i++) + { + struct s_ball *yp = fp->yv + i; + + if (!yp->m) + continue; + + v_mad(yp->p, yp->p, yp->v, dt); + + sol_rotate(yp->e, yp->w, dt); + } + for (i = 0; i < fp->uc; i++) { struct s_ball *up = fp->uv + i; @@ -1061,6 +1215,27 @@ return v_vert(T, o, vp->p, w, up->p, up->v, up->r); } +/*---------------------------------------------------------------------------*/ + +static float sol_test_sphere_inter(const struct s_ball *up, + const struct s_ball *u2p) +{ + float P[3], V[3]; + float t = LARGE; + + v_sub(P, up->p, u2p->p); + v_sub(V, up->v, u2p->v); + + if (v_dot(P, V) < 0.0f) + { + t = v_sol(P, V, up->r + u2p->r); + } + + return (t > LARGE) ? (LARGE) : (t); +} + +/*---------------------------------------------------------------------------*/ + static float sol_test_edge(float dt, float T[3], const struct s_ball *up, @@ -1181,7 +1356,7 @@ { float U[3] = {0.0f, 0.0f, 0.0f}; /* init value only to avoid gcc warnings */ float u, t = dt; - int i; + int i; /* Short circuit a non-solid lump. */ @@ -1302,9 +1477,70 @@ v_cpy(V, W); t = u; } + return t; } +static float sol_test_balls(float dt, const struct s_file *fp) +{ + float t = 0.0f; + int i, j; + + for (i = 0; i < fp->yc; i++) + { + struct s_ball *yp = fp->yv + i; + + for (j = i + 1; j < fp->yc; j++) + { + struct s_ball *y2p = fp->yv + j; + + if (!(yp->r > 0.0f || y2p->r > 0.0f)) + continue; + + if (sol_test_sphere_inter(yp, y2p) < dt) + { + t = sol_bounce_ball(yp, y2p, t); + } + } + } + + for (i = 0; i < fp->uc; i++) + { + struct s_ball *up = fp->uv + i; + + if (i > 0 && !up->P) + continue; + + for (j = 0; j < fp->yc; j++) + { + struct s_ball *y2p = fp->yv + j; + + if (!(up->r > 0.0f || y2p->r > 0.0f)) + continue; + + if (sol_test_sphere_inter(up, y2p) < dt) + { + t = sol_bounce_ball(up, y2p, t); + } + } + + for (j = i + 1; config_get_d(CONFIG_BALL_COLLISIONS) && j < fp->uc; j++) + { + struct s_ball *u2p = fp->uv + j; + + if (!(up->r > 0.0f || u2p->r > 0.0f)) + continue; + + if (u2p->P && up->P && sol_test_sphere_inter(up, u2p) < dt) + { + t = sol_bounce_ball(up, u2p, t); + } + } + } + + return t; +} + static float sol_test_file(float dt, float T[3], float V[3], const struct s_ball *up, @@ -1324,6 +1560,12 @@ t = u; } } + + if ((u = sol_test_balls(t, fp)) > 0.0f) + amplitude = u; + else + amplitude = 0.f; + return t; } @@ -1339,80 +1581,177 @@ float sol_step(struct s_file *fp, const float *g, float dt, int ui, int *m) { - float P[3], V[3], v[3], r[3], a[3], d, e, nt, b = 0.0f, tt = dt; - int c = 16; + float b = 0.0f, nt = 0.0f; + int i, c = 16; - if (ui < fp->uc) + /* + * The user ball loop + */ + for (i = 0; (config_get_d(CONFIG_BALL_COLLISIONS) && i < fp->uc) || + (!config_get_d(CONFIG_BALL_COLLISIONS) && i < 4 + 1); i++) { - struct s_ball *up = fp->uv + ui; + float P[3], V[3], v[3], r[3], a[3], d, e, tt = dt; - /* If the ball is in contact with a surface, apply friction. */ + if (i < fp->uc) + { + struct s_ball *up = fp->uv + i; - v_cpy(a, up->v); - v_cpy(v, up->v); - v_cpy(up->v, g); + /* If the ball is in contact with a surface, apply friction. */ - if (m && sol_test_file(tt, P, V, up, fp) < 0.0005f) - { - v_cpy(up->v, v); - v_sub(r, P, up->p); + v_cpy(a, up->v); + v_cpy(v, up->v); + v_cpy(up->v, g); - if ((d = v_dot(r, g) / (v_len(r) * v_len(g))) > 0.999f) + if (m && sol_test_file(tt, P, V, up, fp) < 0.0005f) { - if ((e = (v_len(up->v) - dt)) > 0.0f) + v_cpy(up->v, v); + v_sub(r, P, up->p); + + if ((d = v_dot(r, g) / (v_len(r) * v_len(g))) > 0.999f) { - /* Scale the linear velocity. */ + if ((e = (v_len(up->v) - dt)) > 0.0f) + { + /* Scale the linear velocity. */ - v_nrm(up->v, up->v); - v_scl(up->v, up->v, e); + v_nrm(up->v, up->v); + v_scl(up->v, up->v, e); - /* Scale the angular velocity. */ + /* Scale the angular velocity. */ - v_sub(v, V, up->v); - v_crs(up->w, v, r); - v_scl(up->w, up->w, -1.0f / (up->r * up->r)); - } - else - { - /* Friction has brought the ball to a stop. */ + v_sub(v, V, up->v); + v_crs(up->w, v, r); + v_scl(up->w, up->w, -1.0f / (up->r * up->r)); + } + else + { + /* Friction has brought the ball to a stop. */ - up->v[0] = 0.0f; - up->v[1] = 0.0f; - up->v[2] = 0.0f; + up->v[0] = 0.0f; + up->v[1] = 0.0f; + up->v[2] = 0.0f; - (*m)++; + if(i == ui) + (*m)++; + } } + else v_mad(up->v, v, g, tt); } else v_mad(up->v, v, g, tt); + + /* Test for collision. */ + + while (tt && tt > (nt = sol_test_file(tt, P, V, up, fp)) && c > 0) + { + sol_body_step(fp, nt); + sol_swch_step(fp, nt); + sol_ball_step(fp, nt); + + tt -= nt; + + if (b < ((d = sol_bounce(up, P, V, nt)))) + b = d; + + c--; + } + + if (b < amplitude) + b = amplitude; + + /* Apply the ball's accelleration to the pendulum. */ + + v_sub(a, up->v, a); + + sol_pendulum(up, a, g, dt); } - else v_mad(up->v, v, g, tt); + } - /* Test for collision. */ + /* + * The arbitrary balls loop + */ + for (i = 0; i < fp->yc; i++) + { + float P[3], V[3], v[3], r[3], a[3], d, e, tt = dt; - while (c > 0 && tt > 0 && tt > (nt = sol_test_file(tt, P, V, up, fp))) + if (i < fp->yc) { - sol_body_step(fp, nt); - sol_swch_step(fp, nt); - sol_ball_step(fp, nt); + struct s_ball *yp = fp->yv + i; - tt -= nt; + if ((!yp->m) || (!config_get_d(CONFIG_BALL_COLLISIONS) && yp->c)) + continue; - if (b < (d = sol_bounce(up, P, V, nt))) - b = d; + /* If the ball is in contact with a surface, apply friction. */ - c--; - } + v_cpy(a, yp->v); + v_cpy(v, yp->v); + v_cpy(yp->v, g); - sol_body_step(fp, tt); - sol_swch_step(fp, tt); - sol_ball_step(fp, tt); + if (m && sol_test_file(tt, P, V, yp, fp) < 0.0005f) + { + v_cpy(yp->v, v); + v_sub(r, P, yp->p); - /* Apply the ball's accelleration to the pendulum. */ + if ((d = v_dot(r, g) / (v_len(r) * v_len(g))) > 0.999f) + { + if ((e = (v_len(yp->v) - dt)) > 0.0f) + { + /* Scale the linear velocity. */ - v_sub(a, up->v, a); + v_nrm(yp->v, yp->v); + v_scl(yp->v, yp->v, e); - sol_pendulum(up, a, g, dt); + /* Scale the angular velocity. */ + + v_sub(v, V, yp->v); + v_crs(yp->w, v, r); + v_scl(yp->w, yp->w, -1.0f / (yp->r * yp->r)); + } + else + { + /* Friction has brought the ball to a stop. */ + + yp->v[0] = 0.0f; + yp->v[1] = 0.0f; + yp->v[2] = 0.0f; + } + } + else v_mad(yp->v, v, g, tt); + } + else v_mad(yp->v, v, g, tt); + + /* Test for collision. */ + + while (tt && tt > (nt = sol_test_file(tt, P, V, yp, fp)) && c > 0) + { + sol_body_step(fp, nt); + sol_swch_step(fp, nt); + sol_ball_step(fp, nt); + + tt -= nt; + + if (b < ((d = sol_bounce(yp, P, V, nt)))) + b = d; + + c--; + } + + if (b < amplitude) + b = amplitude; + + /* Apply the ball's accelleration to the pendulum. */ + + v_sub(a, yp->v, a); + + sol_pendulum(yp, a, g, dt); + } } + + if (!c && !ui) + nt += dt; + + sol_body_step(fp, nt); + sol_swch_step(fp, nt); + sol_ball_step(fp, nt); + return b; } @@ -1420,13 +1759,13 @@ struct s_item *sol_item_test(struct s_file *fp, float *p, float item_r) { - const float *ball_p = fp->uv->p; - const float ball_r = fp->uv->r; + int hi, yi; - int hi; - for (hi = 0; hi < fp->hc; hi++) { + const float *ball_p = fp->uv->p; + const float ball_r = fp->uv->r; + float r[3]; r[0] = ball_p[0] - fp->hv[hi].p[0]; @@ -1441,36 +1780,110 @@ return &fp->hv[hi]; } + + for (yi = 0; yi < fp->yc; yi++) + { + const float *ball_p = fp->yv[yi].p; + const float ball_r = fp->yv[yi].r; + + float r[3]; + + r[0] = ball_p[0] - fp->hv[hi].p[0]; + r[1] = ball_p[1] - fp->hv[hi].p[1]; + r[2] = ball_p[2] - fp->hv[hi].p[2]; + + if (fp->hv[hi].t != ITEM_NONE && v_len(r) < ball_r + item_r) + { + p[0] = fp->hv[hi].p[0]; + p[1] = fp->hv[hi].p[1]; + p[2] = fp->hv[hi].p[2]; + + return &fp->hv[hi]; + } + } } + return NULL; } -struct s_goal *sol_goal_test(struct s_file *fp, float *p, int ui) +int 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; - int zi; - - for (zi = 0; zi < fp->zc; zi++) + if (config_get_d(CONFIG_BALL_COLLISIONS) && ui) { - float r[3]; + const float *ball_p = fp->uv[ui].p; + const float ball_r = fp->uv[ui].r; + float z[3] = {0.0f, 0.0f, 0.0f}; + int zi, i; - r[0] = ball_p[0] - fp->zv[zi].p[0]; - r[1] = ball_p[2] - fp->zv[zi].p[2]; - r[2] = 0; + for (i = 1; i < fp->uc; i++) + { + if(fp->uv[i].p[1] < -199.9f) + v_cpy(fp->uv[i].v, z); + if (v_len(fp->uv[i].v) > 0.0f) + return 0; + else + v_cpy(fp->uv[i].v, z); + } - if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r && - ball_p[1] > fp->zv[zi].p[1] && - ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2) + for (i = 0; i < fp->yc; i++) { - p[0] = fp->zv[zi].p[0]; - p[1] = fp->zv[zi].p[1]; - p[2] = fp->zv[zi].p[2]; + if(fp->yv[i].p[1] < -199.9f) + v_cpy(fp->yv[i].v, z); + if (v_len(fp->yv[i].v) > 0.0f) + return 0; + else + v_cpy(fp->yv[i].v, z); + } - return &fp->zv[zi]; + for (zi = 0; zi < fp->zc; zi++) + { + float r[3]; + + r[0] = ball_p[0] - fp->zv[zi].p[0]; + r[1] = ball_p[2] - fp->zv[zi].p[2]; + r[2] = 0; + + if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r && + ball_p[1] > fp->zv[zi].p[1] && + ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2) + { + p[0] = fp->zv[zi].p[0]; + p[1] = -200.0f; + p[2] = fp->zv[zi].p[2]; + + return 2; + } } + return 1; } - return NULL; + + else + { + const float *ball_p = fp->uv[ui].p; + const float ball_r = fp->uv[ui].r; + int zi; + + for (zi = 0; zi < fp->zc; zi++) + { + float r[3]; + + r[0] = ball_p[0] - fp->zv[zi].p[0]; + r[1] = ball_p[2] - fp->zv[zi].p[2]; + r[2] = 0; + + if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r && + ball_p[1] > fp->zv[zi].p[1] && + ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2) + { + p[0] = fp->zv[zi].p[0]; + p[1] = -200.0f; + p[2] = fp->zv[zi].p[2]; + + return 1; + } + } + return 0; + } } /* @@ -1519,75 +1932,145 @@ * a visible switch is activated, return 0 otherwise (no switch is * activated or only invisible switches). */ -int sol_swch_test(struct s_file *fp, int ui) +int sol_swch_test(struct s_file *fp) { - const float *ball_p = fp->uv[ui].p; - const float ball_r = fp->uv[ui].r; - int xi; - int res = 0; + int xi, i, res = 0; for (xi = 0; xi < fp->xc; xi++) { struct s_swch *xp = fp->xv + xi; - if (xp->t0 == 0 || xp->f == xp->f0) + for (i = 0; i < fp->uc; i++) { - float l; - float r[3]; + float l, r[3]; - r[0] = ball_p[0] - xp->p[0]; - r[1] = ball_p[2] - xp->p[2]; - r[2] = 0; + const float *ball_p = fp->uv[i].p; + const float ball_r = fp->uv[i].r; - l = v_len(r) - xp->r; + if (xp->t0 == 0 || xp->f == xp->f0) + { + r[0] = ball_p[0] - xp->p[0]; + r[1] = ball_p[2] - xp->p[2]; + r[2] = 0; - if (l < ball_r && - ball_p[1] > xp->p[1] && - ball_p[1] < xp->p[1] + SWCH_HEIGHT / 2) - { - if (!xp->e && l < - 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) { - int pi = xp->pi; - int pj = xp->pi; + if (!xp->e && l < -ball_r) + { + int pi = xp->pi; + int pj = xp->pi; - /* The ball enters. */ + xp->b = i * 2; - if (xp->t0 == 0) - xp->e = 1; + /* The ball enters. */ - /* Toggle the state, update the path. */ + if (xp->t0 == 0) + xp->e = 1; - xp->f = xp->f ? 0 : 1; + /* Toggle the state, update the path. */ - do /* Tortoise and hare cycle traverser. */ - { - fp->pv[pi].f = xp->f; - fp->pv[pj].f = xp->f; + xp->f = xp->f ? 0 : 1; - pi = fp->pv[pi].pi; - pj = fp->pv[pj].pi; - pj = fp->pv[pj].pi; - } - while (pi != pj); + do /* Tortoise and hare cycle traverser. */ + { + fp->pv[pi].f = xp->f; + fp->pv[pj].f = xp->f; - /* It toggled to non-default state, start the timer. */ + pi = fp->pv[pi].pi; + pj = fp->pv[pj].pi; + pj = fp->pv[pj].pi; + } + while (pi != pj); - if (xp->f != xp->f0) - xp->t = xp->t0; + /* It toggled to non-default state, start the timer. */ - /* If visible, set the result. */ + if (xp->f != xp->f0) + xp->t = xp->t0; - if (!xp->i) - res = 1; + /* If visible, set the result. */ + + if (!xp->i) + res = 1; + } } + + /* The ball exits. */ + + else if (xp->e && xp->b == i * 2) + xp->e = 0; } + } - /* The ball exits. */ + for (i = 0; i < fp->yc; i++) + { + float l, r[3]; - else if (xp->e) - xp->e = 0; + const float *ball_p = fp->yv[i].p; + const float ball_r = fp->yv[i].r; + + if (xp->t0 == 0 || xp->f == xp->f0) + { + r[0] = ball_p[0] - xp->p[0]; + r[1] = ball_p[2] - xp->p[2]; + r[2] = 0; + + 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 (!xp->e && l < -ball_r) + { + int pi = xp->pi; + int pj = xp->pi; + + xp->b = i * 2 + 1; + + /* The ball enters. */ + + if (xp->t0 == 0) + xp->e = 1; + + /* Toggle the state, update the path. */ + + xp->f = xp->f ? 0 : 1; + + do /* Tortoise and hare cycle traverser. */ + { + fp->pv[pi].f = xp->f; + fp->pv[pj].f = xp->f; + + pi = fp->pv[pi].pi; + pj = fp->pv[pj].pi; + pj = fp->pv[pj].pi; + } + while (pi != pj); + + /* It toggled to non-default state, start the timer. */ + + if (xp->f != xp->f0) + xp->t = xp->t0; + + /* If visible, set the result. */ + + if (!xp->i) + res = 1; + } + } + + /* The ball exits. */ + + else if (xp->e && xp->b == i * 2 + 1) + xp->e = 0; + } } } + return res; } @@ -1616,4 +2099,3 @@ } /*---------------------------------------------------------------------------*/ - Index: share/solid.h =================================================================== --- share/solid.h (.../trunk) (revision 1922) +++ share/solid.h (.../branches/putt-collisions) (revision 1922) @@ -32,27 +32,29 @@ * * The Xs are as documented by struct s_file: * - * f File (struct s_file) - * m Material (struct s_mtrl) - * v Vertex (struct s_vert) - * e Edge (struct s_edge) - * s Side (struct s_side) - * t Texture coord (struct s_texc) - * g Geometry (struct s_geom) - * l Lump (struct s_lump) - * n Node (struct s_node) - * p Path (struct s_path) - * b Body (struct s_body) - * h Item (struct s_item) - * z Goal (struct s_goal) - * j Jump (struct s_jump) - * x Switch (struct s_swch) - * r Billboard (struct s_bill) - * u User (struct s_ball) - * w Viewpoint (struct s_view) - * d Dictionary (struct s_dict) - * i Index (int) - * a Text (char) + * f File (struct s_file) + * m Material (struct s_mtrl) + * v Vertex (struct s_vert) + * e Edge (struct s_edge) + * s Side (struct s_side) + * t Texture coord (struct s_texc) + * g Geometry (struct s_geom) + * l Lump (struct s_lump) + * n Node (struct s_node) + * p Path (struct s_path) + * b Body (struct s_body) + * h Item (struct s_item) + * z Goal (struct s_goal) + * j Jump (struct s_jump) + * x Switch (struct s_swch) + * r Billboard (struct s_bill) + * u User (struct s_ball) + * y Arbitrary ball (struct s_ball) + * w Viewpoint (struct s_view) + * d Dictionary (struct s_dict) + * i Index (int) + * a Text (char) + * * The Ys are as follows: * @@ -72,7 +74,7 @@ * Those members that do not conform to this convention are explicitly * documented with a comment. * - * These prefixes are still available: c k o q y. + * These prefixes are still available: c k o q. */ /*---------------------------------------------------------------------------*/ @@ -223,6 +225,7 @@ int f; /* current state */ int i; /* is invisible? */ int e; /* is a ball inside it? */ + int b; /* which ball? */ }; struct s_bill @@ -258,6 +261,11 @@ float E[3][3]; /* basis of pendulum */ float W[3]; /* angular pendulum velocity */ float r; /* radius */ + int P; /* ball in play state */ + int m; /* is ball mobile? */ + int n; /* return on fall-out flag */ + int c; /* exists only when BCis set? */ + float O[3]; /* original position */ }; struct s_view @@ -274,26 +282,27 @@ struct s_file { - int ac; - int mc; - int vc; - int ec; - int sc; - int tc; - int gc; - int lc; - int nc; - int pc; - int bc; - int hc; - int zc; - int jc; - int xc; - int rc; - int uc; - int wc; - int dc; - int ic; + int ac; + int mc; + int vc; + int ec; + int sc; + int tc; + int gc; + int lc; + int nc; + int pc; + int bc; + int hc; + int zc; + int jc; + int xc; + int rc; + int uc; + int wc; + int dc; + int ic; + int yc; char *av; struct s_mtrl *mv; @@ -312,6 +321,7 @@ struct s_swch *xv; struct s_bill *rv; struct s_ball *uv; + struct s_ball *yv; struct s_view *wv; struct s_dict *dv; int *iv; @@ -331,9 +341,9 @@ float sol_step(struct s_file *, const float *, float, int, int *); int sol_jump_test(struct s_file *, float *, int); -int sol_swch_test(struct s_file *, int); +int sol_swch_test(struct s_file *); +int sol_goal_test(struct s_file *, float *, int); -struct s_goal *sol_goal_test(struct s_file *, float *, int); struct s_item *sol_item_test(struct s_file *, float *, float); /*---------------------------------------------------------------------------*/ Index: doc/AUTHORS =================================================================== --- doc/AUTHORS (.../trunk) (revision 1922) +++ doc/AUTHORS (.../branches/putt-collisions) (revision 1922) @@ -17,6 +17,7 @@ Mehdi Yousfi-Monod (mym) Michael Middleton (slippifishi) Florian Priester + Byron James Johnson (Krabby Krap) * TRANSLATORS @@ -63,6 +64,7 @@ - Feature ideas and testing Byron James Johnson - Platform acceleration toggle + - Neverputt ball vs. ball collision Laurent Moussault (Lorant) - Menu navigation improvements - Layout improvements in several screens @@ -82,6 +84,8 @@ - Map compiler speed-ups for debugging large maps Uoti Urpala - Bounces with moving objects + Georg Wachter + - Generic sphere-vs-sphere collision Mehdi Yousfi-Monod - Neverball Hall of Fame management - Windows packaging Index: doc/MANUAL =================================================================== --- doc/MANUAL (.../trunk) (revision 1922) +++ doc/MANUAL (.../branches/putt-collisions) (revision 1922) @@ -222,6 +222,20 @@ Joystick exit button + gamma 0.78 + gamma gives the Coefficient of restitution of a sphere-sphere + collision. Its square gives the fraction of kinetic energy + (in the center-of-mass system) that will be conserved in the + collision, so possible values range from 0 (completely + inelastic) to 1 (completely elastic) collision. Values greater + than 1 mean that in each collision, extra energy is generated. + putt_collisions 0 + + This key allows balls to collide with other balls in + Neverputt. + + + Contact: Index: data/map-ckk/17.map =================================================================== --- data/map-ckk/17.map (.../trunk) (revision 1922) +++ data/map-ckk/17.map (.../branches/putt-collisions) (revision 1922) @@ -3950,3 +3950,39 @@ ( -112 -800 464 ) ( -128 -800 464 ) ( -128 -928 464 ) mtrl/turf-grey 128 128 0 0.5 0.5 0 0 0 } } +// entity 96 +{ +"classname" "info_notnull" +"origin" "-48 6848 96" +"radius" "0.0625" +"collisions" "1" +"return" "1" +"mobile" "1" +} +// entity 97 +{ +"classname" "info_notnull" +"origin" "48 6848 96" +"radius" "0.0625" +"collisions" "1" +"return" "1" +"mobile" "1" +} +// entity 98 +{ +"classname" "info_notnull" +"origin" "32 6848 96" +"radius" "0.0625" +"collisions" "1" +"return" "1" +"mobile" "1" +} +// entity 99 +{ +"classname" "info_notnull" +"origin" "-32 6848 96" +"radius" "0.0625" +"collisions" "1" +"return" "1" +"mobile" "1" +} Index: data/set-misc.txt =================================================================== --- data/set-misc.txt (.../trunk) (revision 1922) +++ data/set-misc.txt (.../branches/putt-collisions) (revision 1922) @@ -17,3 +17,4 @@ map-misc/hurdles.sol map-misc/canals.sol map-misc/groweasy.sol +map-misc/arbitrary_demo.sol Index: data/shot-misc/arbitrary_demo.jpg =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: data/shot-misc/arbitrary_demo.jpg ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: data/ball/eyeball.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: data/map-misc/arbitrary_demo.map =================================================================== --- data/map-misc/arbitrary_demo.map (.../trunk) (revision 0) +++ data/map-misc/arbitrary_demo.map (.../branches/putt-collisions) (revision 1922) @@ -0,0 +1,582 @@ + +// entity 0 +{ +"classname" "worldspawn" +"levelname" "billiard" +"version" "1.0" +"author" "Byron James Johnson (Krabby Krap)" +"shot" "shot-misc/arbitrary_demo.jpg" +"back" "map-back/clouds.sol" +"grad" "back/land.png" +"song" "bgm/track2.ogg" +"time" "12000" +"goal" "2" +"time_hs" "7500 8000" +"coin_hs" "16 15" +"goal_hs" "600 700" +"message" "Use the other balls to your advantage\Be careful, the universe effects all balls!" +// brush 0 +{ +( 280 -192 64 ) ( 272 -192 64 ) ( 280 -64 64 ) mtrl/edge-green 0 0 0 0.5 0.5 0 0 0 +( 336 144 64 ) ( 336 144 56 ) ( 336 -208 56 ) mtrl/turf-grey -0 0 0 0.5 0.5 0 0 0 +( 336 -16 48 ) ( 336 -16 40 ) ( 320 0 40 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +( 272 -112 56 ) ( 272 -240 56 ) ( 280 -112 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 320 -56 40 ) ( 320 -56 48 ) ( 320 -184 40 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +( 336 144 40 ) ( 336 144 48 ) ( 320 128 48 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +} +// brush 1 +{ +( 320 88 64 ) ( 320 80 64 ) ( 192 88 64 ) mtrl/edge-green 0 0 0 0.5 0.5 0 0 0 +( -16 144 64 ) ( -16 144 56 ) ( 336 144 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 336 144 48 ) ( 336 144 40 ) ( 320 128 40 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +( 192 80 56 ) ( 320 80 56 ) ( 192 88 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( -136 128 56 ) ( -136 128 64 ) ( -8 128 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -336 144 56 ) ( -336 144 64 ) ( -320 128 64 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 2 +{ +( 0 40 64 ) ( 0 48 64 ) ( 128 40 64 ) mtrl/edge-green 0 0 0 0.5 0.5 0 0 0 +( 336 -16 64 ) ( 336 -16 56 ) ( -16 -16 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( -336 -16 64 ) ( -336 -16 56 ) ( -320 0 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 80 48 56 ) ( -48 48 56 ) ( 80 40 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 136 0 56 ) ( 136 0 64 ) ( 8 0 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 336 -16 40 ) ( 336 -16 48 ) ( 320 0 48 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +} +// brush 3 +{ +( -280 320 64 ) ( -272 320 64 ) ( -280 192 64 ) mtrl/edge-green 128 0 0 0.5 0.5 0 0 0 +( -336 -16 64 ) ( -336 -16 56 ) ( -336 336 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( -336 144 64 ) ( -336 144 56 ) ( -320 128 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -272 240 56 ) ( -272 368 56 ) ( -280 240 56 ) mtrl/turf-grey 128 0 0 0.5 0.5 0 0 0 +( -320 184 56 ) ( -320 184 64 ) ( -320 312 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -336 -16 56 ) ( -336 -16 64 ) ( -320 0 64 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 4 +{ +( 320 320 64 ) ( 320 192 64 ) ( 192 320 64 ) mtrl/arrow-green-light 128 128 90 0.5 0.5 134217728 0 0 +( 320 320 64 ) ( 192 320 64 ) ( 320 320 48 ) mtrl/invisible 128 128 90 0.5 0.5 134217728 0 0 +( 320 320 64 ) ( 320 320 48 ) ( 320 192 64 ) mtrl/invisible 128 128 90 0.5 0.5 134217728 0 0 +( 191 192 63 ) ( 319 192 63 ) ( 191 320 63 ) mtrl/invisible 128 128 90 0.5 0.5 134217728 0 0 +( 192 192 48 ) ( 192 192 64 ) ( 320 192 48 ) mtrl/invisible 128 128 90 0.5 0.5 134217728 0 0 +( 192 192 48 ) ( 192 320 48 ) ( 192 192 64 ) mtrl/invisible 128 128 90 0.5 0.5 134217728 0 0 +} +// brush 5 +{ +( 320 128 64 ) ( 320 0 64 ) ( 192 128 64 ) mtrl/switch 128 192 0 1 1 0 0 0 +( 320 160 64 ) ( 192 160 64 ) ( 320 160 48 ) mtrl/invisible 128 192 0 1 1 0 0 0 +( 352 128 64 ) ( 352 128 48 ) ( 352 0 64 ) mtrl/invisible 128 192 0 1 1 0 0 0 +( 191 0 63 ) ( 319 0 63 ) ( 191 128 63 ) mtrl/invisible 128 192 0 1 1 0 0 0 +( 176 -32 48 ) ( 176 -32 64 ) ( 304 -32 48 ) mtrl/invisible 128 192 0 1 1 0 0 0 +( 160 -16 48 ) ( 160 112 48 ) ( 160 -16 64 ) mtrl/invisible 128 192 0 1 1 0 0 0 +} +// brush 6 +{ +( 320 512 64 ) ( 320 384 64 ) ( 192 512 64 ) mtrl/teleporter 128 0 0 0.5 0.5 134217728 0 0 +( 320 512 64 ) ( 192 512 64 ) ( 320 512 48 ) mtrl/invisible -0 0 0 0.5 0.5 134217728 0 0 +( 320 512 64 ) ( 320 512 48 ) ( 320 384 64 ) mtrl/invisible -0 0 0 0.5 0.5 134217728 0 0 +( 191 384 63 ) ( 319 384 63 ) ( 191 512 63 ) mtrl/invisible -0 0 0 0.5 0.5 134217728 0 0 +( 192 384 48 ) ( 192 384 64 ) ( 320 384 48 ) mtrl/invisible -0 0 0 0.5 0.5 134217728 0 0 +( 192 384 48 ) ( 192 512 48 ) ( 192 384 64 ) mtrl/invisible -0 0 0 0.5 0.5 134217728 0 0 +} +// brush 7 +{ +( -192 320 64 ) ( -192 192 64 ) ( -320 320 64 ) mtrl/teleporter 128 128 0 0.25 0.25 134217728 0 0 +( -160 288 64 ) ( -288 288 64 ) ( -160 288 48 ) mtrl/invisible 128 128 0 0.25 0.25 134217728 0 0 +( -224 352 64 ) ( -224 352 48 ) ( -224 224 64 ) mtrl/invisible 128 128 0 0.25 0.25 134217728 0 0 +( -321 192 63 ) ( -193 192 63 ) ( -321 320 63 ) mtrl/invisible 128 128 0 0.25 0.25 134217728 0 0 +( -352 224 48 ) ( -352 224 64 ) ( -224 224 48 ) mtrl/invisible 128 128 0 0.25 0.25 134217728 0 0 +( -288 176 48 ) ( -288 304 48 ) ( -288 176 64 ) mtrl/invisible 128 128 0 0.25 0.25 134217728 0 0 +} +// brush 8 +{ +( 320 264 64 ) ( 320 256 64 ) ( 192 264 64 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( 264 320 64 ) ( 136 320 64 ) ( 264 320 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 320 264 64 ) ( 320 264 56 ) ( 320 256 64 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 192 256 56 ) ( 320 256 56 ) ( 192 264 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 192 192 56 ) ( 192 192 64 ) ( 320 192 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -320 320 56 ) ( -320 328 56 ) ( -320 320 64 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 9 +{ +( 320 72 64 ) ( 320 64 64 ) ( 192 72 64 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( 264 128 64 ) ( 136 128 64 ) ( 264 128 56 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( 320 72 64 ) ( 320 72 56 ) ( 320 64 64 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( 192 64 56 ) ( 320 64 56 ) ( 192 72 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 192 0 56 ) ( 192 0 64 ) ( 320 0 56 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( -320 128 56 ) ( -320 136 56 ) ( -320 128 64 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +} +// brush 10 +{ +( -80 288 576 ) ( -64 288 576 ) ( -80 160 576 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -80 320 416 ) ( -80 192 416 ) ( -80 320 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -80 336 416 ) ( -80 336 400 ) ( -64 336 416 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 192 400 ) ( -64 320 400 ) ( -80 192 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 192 400 ) ( -64 192 416 ) ( -64 320 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 176 400 ) ( -80 176 400 ) ( -64 176 416 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +} +// brush 11 +{ +( -32 176 576 ) ( -32 192 576 ) ( 96 176 576 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 176 416 ) ( 64 176 416 ) ( -64 176 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 176 416 ) ( -64 176 400 ) ( -64 192 416 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 64 192 400 ) ( -64 192 400 ) ( 64 176 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 192 400 ) ( 64 192 416 ) ( -64 192 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 192 400 ) ( 64 176 400 ) ( 64 192 416 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 12 +{ +( 80 224 576 ) ( 64 224 576 ) ( 80 352 576 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 80 192 416 ) ( 80 320 416 ) ( 80 192 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 80 176 416 ) ( 80 176 400 ) ( 64 176 416 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 320 400 ) ( 64 192 400 ) ( 80 320 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 320 400 ) ( 64 320 416 ) ( 64 192 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 336 400 ) ( 80 336 400 ) ( 64 336 416 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +} +// brush 13 +{ +( 32 336 576 ) ( 32 320 576 ) ( -96 336 576 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 336 416 ) ( -64 336 416 ) ( 64 336 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 336 416 ) ( 64 336 400 ) ( 64 320 416 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -64 320 400 ) ( 64 320 400 ) ( -64 336 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 320 400 ) ( -64 320 416 ) ( 64 320 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 320 400 ) ( -64 336 400 ) ( -64 320 416 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 14 +{ +( 64 320 576 ) ( 64 192 576 ) ( -64 320 576 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 320 576 ) ( -64 320 576 ) ( 64 320 400 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 64 320 576 ) ( 64 320 400 ) ( 64 192 576 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -80 192 560 ) ( 48 192 560 ) ( -80 320 560 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 192 400 ) ( -64 192 576 ) ( 64 192 400 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -64 192 400 ) ( -64 320 400 ) ( -64 192 576 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 15 +{ +( 320 456 64 ) ( 320 448 64 ) ( 192 456 64 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( 264 512 64 ) ( 136 512 64 ) ( 264 512 56 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( 320 456 64 ) ( 320 456 56 ) ( 320 448 64 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( 192 448 56 ) ( 320 448 56 ) ( 192 456 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 192 384 56 ) ( 192 384 64 ) ( 320 384 56 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +( -320 512 56 ) ( -320 520 56 ) ( -320 512 64 ) mtrl/turf-green-small 0 0 0 0.5 0.5 0 0 0 +} +// brush 16 +{ +( 280 192 64 ) ( 272 192 64 ) ( 280 320 64 ) mtrl/edge-green 0 0 0 0.5 0.5 0 0 0 +( 336 528 64 ) ( 336 528 56 ) ( 336 176 56 ) mtrl/turf-grey -0 0 0 0.5 0.5 0 0 0 +( 336 368 48 ) ( 336 368 40 ) ( 320 384 40 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +( 272 272 56 ) ( 272 144 56 ) ( 280 272 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 320 328 40 ) ( 320 328 48 ) ( 320 200 40 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +( 336 528 40 ) ( 336 528 48 ) ( 320 512 48 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +} +// brush 17 +{ +( 0 424 64 ) ( 0 432 64 ) ( 128 424 64 ) mtrl/edge-green 0 0 0 0.5 0.5 0 0 0 +( 336 368 64 ) ( 336 368 56 ) ( -16 368 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( -336 368 64 ) ( -336 368 56 ) ( -320 384 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 80 432 56 ) ( -48 432 56 ) ( 80 424 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 136 384 56 ) ( 136 384 64 ) ( 8 384 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 336 368 40 ) ( 336 368 48 ) ( 320 384 48 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +} +// brush 18 +{ +( -280 704 64 ) ( -272 704 64 ) ( -280 576 64 ) mtrl/edge-green 128 0 0 0.5 0.5 0 0 0 +( -336 368 64 ) ( -336 368 56 ) ( -336 720 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( -336 528 64 ) ( -336 528 56 ) ( -320 512 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -272 624 56 ) ( -272 752 56 ) ( -280 624 56 ) mtrl/turf-grey 128 0 0 0.5 0.5 0 0 0 +( -320 568 56 ) ( -320 568 64 ) ( -320 696 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -336 368 56 ) ( -336 368 64 ) ( -320 384 64 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 19 +{ +( 320 472 64 ) ( 320 464 64 ) ( 192 472 64 ) mtrl/edge-green 0 0 0 0.5 0.5 0 0 0 +( -16 528 64 ) ( -16 528 56 ) ( 336 528 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 336 528 48 ) ( 336 528 40 ) ( 320 512 40 ) mtrl/invisible 0 96 0 0.5 0.5 0 0 0 +( 192 464 56 ) ( 320 464 56 ) ( 192 472 56 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( -136 512 56 ) ( -136 512 64 ) ( -8 512 56 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -336 528 56 ) ( -336 528 64 ) ( -320 512 64 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 20 +{ +( -192 512 64 ) ( -192 384 64 ) ( -320 512 64 ) mtrl/switch 128 0 0 0.5 0.5 134217728 0 0 +( -256 512 64 ) ( -384 512 64 ) ( -256 512 48 ) mtrl/invisible 128 0 0 0.5 0.5 134217728 0 0 +( -192 480 64 ) ( -192 480 48 ) ( -192 352 64 ) mtrl/invisible 128 0 0 0.5 0.5 134217728 0 0 +( -321 384 63 ) ( -193 384 63 ) ( -321 512 63 ) mtrl/invisible 128 0 0 0.5 0.5 134217728 0 0 +( -320 384 48 ) ( -320 384 64 ) ( -192 384 48 ) mtrl/invisible 128 0 0 0.5 0.5 134217728 0 0 +( -320 384 48 ) ( -320 512 48 ) ( -320 384 64 ) mtrl/invisible 128 0 0 0.5 0.5 134217728 0 0 +} +// brush 21 +{ +( 1072 208 160 ) ( 1072 48 160 ) ( 1024 208 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 1072 192 160 ) ( 1024 192 160 ) ( 1072 192 144 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 1168 176 152 ) ( 1168 176 160 ) ( 1152 192 160 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 1016 48 152 ) ( 1064 48 152 ) ( 1016 208 152 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 1168 176 160 ) ( 1168 176 152 ) ( 640 176 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 640 160 144 ) ( 640 320 144 ) ( 640 160 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +} +// brush 22 +{ +( 1088 336 160 ) ( 1088 176 160 ) ( 1040 336 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 1168 336 152 ) ( 1168 336 160 ) ( 1152 320 160 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 1168 176 152 ) ( 1168 176 160 ) ( 1168 336 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 1032 176 152 ) ( 1080 176 152 ) ( 1032 336 152 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 1168 176 160 ) ( 1168 176 152 ) ( 1152 192 160 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 1152 160 144 ) ( 1152 320 144 ) ( 1152 160 160 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +// brush 23 +{ +( 1072 352 160 ) ( 1072 192 160 ) ( 1024 352 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 1168 336 152 ) ( 1168 336 160 ) ( 640 336 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 1168 336 160 ) ( 1168 336 152 ) ( 1152 320 152 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 1016 192 152 ) ( 1064 192 152 ) ( 1016 352 152 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 1024 320 144 ) ( 1024 320 160 ) ( 1072 320 144 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 640 304 144 ) ( 640 464 144 ) ( 640 304 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +} +// brush 24 +{ +( 1072 336 160 ) ( 1072 176 160 ) ( 1024 336 160 ) mtrl/turf-red 0 0 0 0.5 0.5 0 0 0 +( 1072 320 160 ) ( 1024 320 160 ) ( 1072 320 144 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 1152 352 160 ) ( 1152 352 144 ) ( 1152 192 160 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 1016 176 152 ) ( 1064 176 152 ) ( 1016 336 152 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 1024 192 144 ) ( 1024 192 160 ) ( 1072 192 144 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 640 176 144 ) ( 640 336 144 ) ( 640 176 160 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +} +// brush 25 +{ +( 1344 320 0 ) ( 1344 192 0 ) ( 1216 320 0 ) mtrl/goal 0 128 0 0.5 0.5 134217728 0 0 +( 1472 320 8 ) ( 1344 320 8 ) ( 1472 320 -8 ) mtrl/invisible 0 128 0 0.5 0.5 134217728 0 0 +( 1408 448 8 ) ( 1408 448 -8 ) ( 1408 320 8 ) mtrl/invisible 0 128 0 0.5 0.5 134217728 0 0 +( 1211 192 -1 ) ( 1339 192 -1 ) ( 1211 320 -1 ) mtrl/invisible 0 128 0 0.5 0.5 134217728 0 0 +( 1152 192 -8 ) ( 1152 192 8 ) ( 1280 192 -8 ) mtrl/invisible 0 128 0 0.5 0.5 134217728 0 0 +( 1280 128 -8 ) ( 1280 256 -8 ) ( 1280 128 8 ) mtrl/invisible 0 128 0 0.5 0.5 134217728 0 0 +} +// brush 26 +{ +( 1344 320 0 ) ( 1344 192 0 ) ( 1216 320 0 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 1408 384 8 ) ( 1280 384 8 ) ( 1408 384 -8 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 1472 384 8 ) ( 1472 384 -8 ) ( 1472 256 8 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 1216 192 -16 ) ( 1344 192 -16 ) ( 1216 320 -16 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 1216 128 -8 ) ( 1216 128 8 ) ( 1344 128 -8 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 1216 192 -8 ) ( 1216 320 -8 ) ( 1216 192 8 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +} +} +// entity 1 +{ +"classname" "info_player_intermission" +"target" "t1" +"origin" "601 -444 624" +} +// entity 2 +{ +"classname" "target_position" +"targetname" "t1" +"origin" "-38 560 -40" +} +// entity 3 +{ +"classname" "info_player_start" +"origin" "256 256 88" +} +// entity 4 +{ +"classname" "info_notnull" +"origin" "144 256 88" +"radius" "0.125" +"collisions" "0" +"mobile" "1" +"return" "1" +} +// entity 5 +{ +"classname" "light" +"origin" "-328 256 96" +"light" "10" +} +// entity 6 +{ +"classname" "light" +"origin" "0 328 96" +"light" "5" +} +// entity 7 +{ +"classname" "light" +"origin" "0 184 96" +"light" "5" +} +// entity 8 +{ +"classname" "light" +"origin" "-128 64 80" +"light" "1" +} +// entity 9 +{ +"classname" "light" +"origin" "80 256 80" +"light" "1" +} +// entity 10 +{ +"classname" "light" +"origin" "40 256 80" +"light" "1" +} +// entity 11 +{ +"classname" "light" +"origin" "64 64 80" +"light" "1" +} +// entity 12 +{ +"classname" "light" +"origin" "-32 64 80" +"light" "5" +} +// entity 13 +{ +"classname" "func_train" +"target" "p1_0" +// brush 0 +{ +( 64 320 416 ) ( 64 192 416 ) ( -64 320 416 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( 64 320 416 ) ( -64 320 416 ) ( 64 320 400 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 64 320 416 ) ( 64 320 400 ) ( 64 192 416 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 192 400 ) ( 64 192 400 ) ( -64 320 400 ) mtrl/glass 0 0 0 0.5 0.5 0 0 0 +( -64 192 400 ) ( -64 192 416 ) ( 64 192 400 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( -64 192 400 ) ( -64 320 400 ) ( -64 192 416 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +} +} +// entity 14 +{ +"classname" "path_corner" +"origin" "0 256 480" +"targetname" "p1_0" +"target" "p1_1" +"speed" "3" +"state" "0" +} +// entity 15 +{ +"classname" "path_corner" +"origin" "-128 256 480" +"targetname" "p1_1" +} +// entity 16 +{ +"classname" "info_notnull" +"origin" "0 256 440" +"mobile" "1" +"collisions" "0" +"return" "1" +"radius" "0.375" +} +// entity 17 +{ +"classname" "light" +"origin" "-32 448 80" +"light" "1" +} +// entity 18 +{ +"classname" "light" +"origin" "64 448 80" +"light" "1" +} +// entity 19 +{ +"classname" "light" +"origin" "-80 448 96" +"light" "10" +} +// entity 20 +{ +"classname" "info_camp" +"origin" "-256 448 64" +"target" "p1_0" +"timer" "9999" +} +// entity 21 +{ +"classname" "info_camp" +"origin" "256 64 64" +"target" "p2_0" +"timer" "9999" +"radius" "1.0" +} +// entity 22 +{ +"classname" "info_camp" +"origin" "256 64 64" +"target" "p3_0" +"timer" "9999" +"invisible" "1" +"radius" "1.0" +} +// entity 23 +{ +"classname" "target_position" +"origin" "-256 256 64" +"targetname" "t2" +} +// entity 24 +{ +"classname" "target_position" +"origin" "256 448 64" +"targetname" "t3" +} +// entity 25 +{ +"classname" "target_teleporter" +"origin" "-256 256 64" +"target" "t3" +"radius" "0.25" +} +// entity 26 +{ +"classname" "target_teleporter" +"origin" "256 448 64" +"target" "t2" +} +// entity 27 +{ +"classname" "func_train" +"target" "p3_0" +// brush 0 +{ +( 320 280 80 ) ( 320 272 80 ) ( 192 280 80 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -16 336 64 ) ( -16 336 56 ) ( 336 336 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 336 336 64 ) ( 336 336 56 ) ( 320 320 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 192 272 56 ) ( 320 272 56 ) ( 192 280 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 184 320 56 ) ( 184 320 64 ) ( 312 320 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -336 336 56 ) ( -336 336 64 ) ( -320 320 64 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +} +// brush 1 +{ +( 280 0 80 ) ( 272 0 80 ) ( 280 128 80 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 336 336 64 ) ( 336 336 56 ) ( 336 -16 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 336 176 64 ) ( 336 176 56 ) ( 320 192 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 272 80 56 ) ( 272 -48 56 ) ( 280 80 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 320 136 56 ) ( 320 136 64 ) ( 320 8 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 336 336 56 ) ( 336 336 64 ) ( 320 320 64 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +} +// brush 2 +{ +( 0 232 80 ) ( 0 240 80 ) ( 128 232 80 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 336 176 64 ) ( 336 176 56 ) ( -16 176 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -336 176 64 ) ( -336 176 56 ) ( -320 192 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 80 240 56 ) ( -48 240 56 ) ( 80 232 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -184 192 56 ) ( -184 192 64 ) ( -312 192 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( 336 176 56 ) ( 336 176 64 ) ( 320 192 64 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +} +// brush 3 +{ +( -280 512 80 ) ( -272 512 80 ) ( -280 384 80 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -336 176 64 ) ( -336 176 56 ) ( -336 528 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -336 336 64 ) ( -336 336 56 ) ( -320 320 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -272 432 56 ) ( -272 560 56 ) ( -280 432 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -320 376 56 ) ( -320 376 64 ) ( -320 504 56 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +( -336 176 56 ) ( -336 176 64 ) ( -320 192 64 ) mtrl/wood 0 0 0 0.5 0.5 0 0 0 +} +} +// entity 28 +{ +"classname" "path_corner" +"origin" "0 256 192" +"targetname" "p3_0" +"target" "p3_1" +"speed" "3" +"state" "0" +} +// entity 29 +{ +"classname" "path_corner" +"origin" "0 256 192" +"targetname" "p3_1" +"target" "p3_2" +"speed" "0.75" +} +// entity 30 +{ +"classname" "path_corner" +"origin" "0 256 184" +"targetname" "p3_2" +} +// entity 31 +{ +"classname" "light" +"origin" "704 288 176" +"light" "5" +} +// entity 32 +{ +"classname" "light" +"origin" "704 224 176" +"light" "5" +} +// entity 33 +{ +"classname" "info_notnull" +"origin" "832 256 184" +"collisions" "0" +"return" "1" +"mobile" "0" +} +// entity 34 +{ +"classname" "info_player_deathmatch" +"origin" "1344 256 24" +} +// entity 35 +{ +"classname" "func_train" +"target" "p2_0" +// brush 0 +{ +( 640 352 240 ) ( 640 192 240 ) ( 336 192 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 400 336 160 ) ( 336 336 160 ) ( 400 336 144 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 640 464 232 ) ( 640 304 232 ) ( 640 304 240 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 640 192 232 ) ( 640 352 232 ) ( 336 192 152 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 336 320 144 ) ( 336 320 160 ) ( 400 320 144 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 336 192 144 ) ( 336 352 144 ) ( 336 192 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +} +// brush 1 +{ +( 640 208 240 ) ( 640 48 240 ) ( 336 48 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 400 192 160 ) ( 336 192 160 ) ( 400 192 144 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 640 320 232 ) ( 640 160 232 ) ( 640 160 240 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 640 48 232 ) ( 640 208 232 ) ( 336 48 152 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 336 176 144 ) ( 336 176 160 ) ( 400 176 144 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +( 336 48 144 ) ( 336 208 144 ) ( 336 48 160 ) mtrl/chrome-faceted 0 0 0 0.5 0.5 0 0 0 +} +// brush 2 +{ +( 640 336 240 ) ( 640 176 240 ) ( 336 176 160 ) mtrl/turf-red 0 0 0 0.5 0.5 0 0 0 +( 400 320 160 ) ( 336 320 160 ) ( 400 320 144 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 640 336 232 ) ( 640 176 232 ) ( 640 176 240 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 640 176 232 ) ( 640 336 232 ) ( 336 176 152 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +( 336 192 144 ) ( 336 192 160 ) ( 400 192 144 ) mtrl/invisible 0 0 0 0.5 0.5 0 0 0 +( 336 176 144 ) ( 336 336 144 ) ( 336 176 160 ) mtrl/turf-grey 0 0 0 0.5 0.5 0 0 0 +} +} +// entity 36 +{ +"classname" "path_corner" +"origin" "1024 512 192" +"targetname" "p2_0" +"target" "p2_1" +"state" "0" +"speed" "3" +} +// entity 37 +{ +"classname" "path_corner" +"origin" "1024 512 112" +"targetname" "p2_1" +} +// entity 38 +{ +"classname" "light" +"origin" "-128 448 80" +"light" "1" +} Index: ball/game.c =================================================================== --- ball/game.c (.../trunk) (revision 1922) +++ ball/game.c (.../branches/putt-collisions) (revision 1922) @@ -54,11 +54,12 @@ static float view_k; static int coins = 0; /* Collected coins */ -static int goal_e = 0; /* Goal enabled flag */ +static int goal_e = 0; /* Goal coins remaining (0 = open) */ static float goal_k = 0; /* Goal animation */ static int jump_e = 1; /* Jumping enabled flag */ static int jump_b = 0; /* Jump-in-progress flag */ static float jump_dt; /* Jump duration */ +static int jump_y = 0; /* Which arbitrary ball is jumping? */ static float jump_p[3]; /* Jump destination */ static float fade_k = 0.0; /* Fade in/out level */ static float fade_d = 0.0; /* Fade in/out direction */ @@ -78,7 +79,7 @@ * r: * -32767 = -VIEWR_BOUND * +32767 = +VIEWR_BOUND - * + * */ struct input @@ -156,7 +157,7 @@ put_short(fout, &input_current.z); put_short(fout, &input_current.r); put_short(fout, &input_current.c); - + return 1; } return 0; @@ -340,6 +341,7 @@ jump_e = 1; jump_b = 0; + jump_y = 0; goal_e = e ? 1 : 0; goal_k = e ? 1.0f : 0.0f; @@ -398,14 +400,60 @@ /*---------------------------------------------------------------------------*/ +void game_check_balls(struct s_file *fp) +{ + float z[3] = {0.0f, 0.0f, 0.0f}; + int i; + + for (i = 0; i < fp->yc; i++) + { + struct s_ball *yp = fp->yv + i; + + /* + * Test and deal with any fall-outs + */ + if (yp->p[1] < fp->vv[0].p[1] && yp->n) + { + v_cpy(yp->p, yp->O); + v_cpy(yp->v, z); + v_cpy(yp->w, z); + } + } +} + static void game_draw_balls(const struct s_file *fp, const float *bill_M, float t) { - float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; float ball_M[16]; float pend_M[16]; + int yi; + + for (yi = 0; yi < fp->yc; yi++) + { + float ball_M[16]; + float pend_M[16]; + + m_basis(ball_M, fp->yv[yi].e[0], fp->yv[yi].e[1], fp->yv[yi].e[2]); + m_basis(pend_M, fp->yv[yi].E[0], fp->yv[yi].E[1], fp->yv[yi].E[2]); + + glPushMatrix(); + { + glTranslatef(fp->yv[yi].p[0], + fp->yv[yi].p[1] + BALL_FUDGE, + fp->yv[yi].p[2]); + glScalef(fp->yv[yi].r, + fp->yv[yi].r, + fp->yv[yi].r); + + glColor4fv(c); + ball_draw(ball_M, pend_M, bill_M, t); + } + glPopMatrix(); + } + m_basis(ball_M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]); m_basis(pend_M, fp->uv[0].E[0], fp->uv[0].E[1], fp->uv[0].E[2]); @@ -510,7 +558,7 @@ glTranslatef(fp->zv[zi].p[0], fp->zv[zi].p[1], fp->zv[zi].p[2]); - + part_draw_goal(M, fp->zv[zi].r, goal_k, t); } glPopMatrix(); @@ -795,8 +843,12 @@ { float fov = view_fov; - if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5); + if (jump_b && jump_y) + fov /= 1.9f * fabsf(jump_dt - 0.5f); + else if (jump_b) + fov *= 2.0f * fabsf(jump_dt - 0.5f); + if (game_state) { config_push_persp(fov, 0.1f, FAR_DIST); @@ -994,12 +1046,13 @@ static int game_update_state(int bt) { struct s_file *fp = &file; - struct s_goal *zp; struct s_item *hp; float p[3]; float c[3]; + int i; + /* Test for an item. */ if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS))) @@ -1016,30 +1069,56 @@ /* Discard item. */ + /* Reset item type. */ + hp->t = ITEM_NONE; } /* Test for a switch. */ - if (sol_swch_test(fp, 0)) + if (sol_swch_test(fp)) audio_play(AUD_SWITCH, 1.f); /* Test for a jump. */ - if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1) + if (!jump_y && jump_e == 1 && jump_b == 0 && + sol_jump_test(fp, jump_p, 0) == 1) { jump_b = 1; jump_e = 0; jump_dt = 0.f; + jump_y = 0; audio_play(AUD_JUMP, 1.f); } if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0) jump_e = 1; + if (!jump_b && !jump_y && sol_jump_test(fp, jump_p, 0) == 0) + jump_y = 0; + for (i = 0; i < fp->yc; i++) + { + if (!jump_y && jump_e == 1 && jump_b == 0 && + sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 1) + { + jump_b = 1; + jump_e = 0; + jump_dt = 0.f; + jump_y = i + 1; + + audio_play(AUD_JUMP, 1.f); + } + if (jump_e == 0 && jump_b == 0 && + sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 0) + jump_e = 1; + if (!jump_b && jump_y && i == jump_y - 1 && + sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 0) + jump_y = 0; + } + /* Test for a goal. */ - if (bt && goal_e && (zp = sol_goal_test(fp, p, 0))) + if (bt && goal_e && (sol_goal_test(fp, p, 0))) { audio_play(AUD_GOAL, 1.0f); return GAME_GOAL; @@ -1090,9 +1169,19 @@ if (0.5f < jump_dt) { - fp->uv[0].p[0] = jump_p[0]; - fp->uv[0].p[1] = jump_p[1]; - fp->uv[0].p[2] = jump_p[2]; + if (jump_y) + { + fp->yv[jump_y - 1].p[0] = jump_p[0]; + fp->yv[jump_y - 1].p[1] = jump_p[1]; + fp->yv[jump_y - 1].p[2] = jump_p[2]; + } + + else + { + fp->uv[0].p[0] = jump_p[0]; + fp->uv[0].p[1] = jump_p[1]; + fp->uv[0].p[2] = jump_p[2]; + } } if (1.0f < jump_dt) jump_b = 0; @@ -1103,6 +1192,8 @@ float b = sol_step(fp, h, dt, 0, NULL); + game_check_balls(fp); + /* Mix the sound of a ball bounce. */ if (b > 0.5f) Index: putt/game.c =================================================================== --- putt/game.c (.../trunk) (revision 1922) +++ putt/game.c (.../branches/putt-collisions) (revision 1922) @@ -46,6 +46,7 @@ static float jump_e = 1; /* Jumping enabled flag */ static float jump_b = 0; /* Jump-in-progress flag */ +static int jump_u = 0; /* Which ball is jumping? */ static float jump_dt; /* Jump duration */ static float jump_p[3]; /* Jump destination */ @@ -82,6 +83,7 @@ { jump_e = 1; jump_b = 0; + jump_u = 0; view_init(); sol_load_gl(&file, config_data(s), config_get_d(CONFIG_TEXTURES), @@ -95,6 +97,124 @@ /*---------------------------------------------------------------------------*/ +int game_check_balls(struct s_file *fp) +{ + float z[3] = {0.0f, 0.0f, 0.0f}; + int i, j; + + for (i = 1; i < fp->uc && config_get_d(CONFIG_BALL_COLLISIONS); i++) + { + struct s_ball *up = fp->uv + i; + + /* + * If a ball falls out, return the ball to the camera marker + * and reset the play state for fair play + */ + if (i != ball && up->p[1] < -10.f && + (up->p[1] > -199.9f || up->p[1] < -599.9f)) + { + up->P = 0; + v_cpy(up->p, fp->uv->p); + v_cpy(up->v, z); + v_cpy(up->w, z); + } + + if (i == ball && up->p[1] < -30.0f) + { + v_cpy(up->p, fp->uv->p); + v_cpy(up->v, z); + v_cpy(up->w, z); + } + + /* + * If an OTHER ball stops in a hole, mark it as done + * and drop it -200.0 units to allow room for more balls + */ + if (i != ball && !(v_len(up->v) > 0.0f)) + { + const float *ball_p = up->p; + const float ball_r = up->r; + int zi; + for (zi = 0; zi < fp->zc; zi++) + { + float r[3]; + + r[0] = ball_p[0] - fp->zv[zi].p[0]; + r[1] = ball_p[2] - fp->zv[zi].p[2]; + r[2] = 0; + + if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r && + ball_p[1] > fp->zv[zi].p[1] && + ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2) + { + up->p[1] = -200.0f; + v_cpy(up->v, z); + v_cpy(up->w, z); + return i; + } + } + } + + /* + * Check for intesecting balls. + * If there are any, reset the proper + * ball's play state + */ + for (j = i + 1; j < fp->uc && config_get_d(CONFIG_BALL_COLLISIONS); j++) + { + struct s_ball *u2p = fp->uv + j; + float d[3]; + v_sub(d, up->p, u2p->p); + if (v_len(up->v) > 0.005f || v_len(u2p->v) > 0.005f) + continue; + if (v_len(d) < (fsqrtf((up->r + u2p->r) + * (up->r + u2p->r))) * 1.0f && i != ball) + up->P = 0; + else if (v_len(d) < (fsqrtf((up->r + + u2p->r) * (up->r + u2p->r)) * 1.0f)) + u2p->P = 0; + } + } + + for (i = 0; i < fp->yc; i++) + { + struct s_ball *yp = fp->yv + i; + + if (yp->p[1] < -20.0f && yp->n) + { + v_cpy(yp->p, yp->O); + v_cpy(yp->v, z); + v_cpy(yp->w, z); + } + + if (!(v_len(yp->v) > 0.0f)) + { + const float *ball_p = yp->p; + const float ball_r = yp->r; + int zi; + for (zi = 0; zi < fp->zc; zi++) + { + float r[3]; + + r[0] = ball_p[0] - fp->zv[zi].p[0]; + r[1] = ball_p[2] - fp->zv[zi].p[2]; + r[2] = 0; + + if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r && + ball_p[1] > fp->zv[zi].p[1] && + ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2) + { + v_cpy(yp->p, yp->O); + v_cpy(yp->v, z); + v_cpy(yp->w, z); + } + } + } + } + + return 0; +} + static void game_draw_vect_prim(const struct s_file *fp, GLenum mode) { float p[3]; @@ -158,19 +278,50 @@ static void game_draw_balls(const struct s_file *fp, const float *bill_M, float t) { - static const GLfloat color[5][4] = { + static const GLfloat color[6][4] = { { 1.0f, 1.0f, 1.0f, 0.7f }, { 1.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 1.0f, 0.0f, 1.0f }, + { 0.1f, 0.1f, 0.1f, 1.0f }, }; - int ui; + int ui, yi; + for (yi = 0; yi < fp->yc; yi++) + { + float ball_M[16]; + float pend_M[16]; + + if (!config_get_d(CONFIG_BALL_COLLISIONS) && fp->yv[yi].c) + continue; + + m_basis(ball_M, fp->yv[yi].e[0], fp->yv[yi].e[1], fp->yv[yi].e[2]); + m_basis(pend_M, fp->yv[yi].E[0], fp->yv[yi].E[1], fp->yv[yi].E[2]); + + glPushMatrix(); + { + glTranslatef(fp->yv[yi].p[0], + fp->yv[yi].p[1] + BALL_FUDGE, + fp->yv[yi].p[2]); + glScalef(fp->yv[yi].r, + fp->yv[yi].r, + fp->yv[yi].r); + + glEnable(GL_COLOR_MATERIAL); + glColor4fv(color[5]); + + ball_draw(ball_M, pend_M, bill_M, t); + glDisable(GL_COLOR_MATERIAL); + } + glPopMatrix(); + } + for (ui = curr_party(); ui > 0; ui--) { - if (ui == ball) + if (ui == ball || (config_get_d(CONFIG_BALL_COLLISIONS) && + fp->uv[ui].P)) { float ball_M[16]; float pend_M[16]; @@ -189,6 +340,7 @@ glEnable(GL_COLOR_MATERIAL); glColor4fv(color[ui]); + ball_draw(ball_M, pend_M, bill_M, t); glDisable(GL_COLOR_MATERIAL); } @@ -280,15 +432,21 @@ static const float s[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; static const float e[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; static const float h[1] = { 0.0f }; - + const float light_p[4] = { 8.f, 32.f, 8.f, 1.f }; const struct s_file *fp = &file; float fov = FOV; - if (jump_b) fov *= 2.0f * fabsf(jump_dt - 0.5f); + int i = 0; + if (config_get_d(CONFIG_BALL_COLLISIONS) && jump_b && jump_u != ball * 2) + fov /= 1.9f * fabsf(jump_dt - 0.5f); + + else if (jump_b) + fov *= 2.0f * fabsf(jump_dt - 0.5f); + config_push_persp(fov, 0.1f, FAR_DIST); glPushAttrib(GL_LIGHTING_BIT); glPushMatrix(); @@ -325,6 +483,23 @@ if (config_get_d(CONFIG_SHADOW) && !pose) { + for (i = 0; i < fp->yc; i++) + { + shad_draw_set(fp->yv[i].p, fp->yv[i].r); + sol_shad(fp); + shad_draw_clr(); + } + + for (i = 0; i < fp->uc; i++) + { + if (fp->uv[i].P) + { + shad_draw_set(fp->uv[i].p, fp->uv[i].r); + sol_shad(fp); + shad_draw_clr(); + } + } + shad_draw_set(fp->uv[ball].p, fp->uv[ball].r); sol_shad(fp); shad_draw_clr(); @@ -442,49 +617,133 @@ struct s_file *fp = &file; float p[3]; + int i; + if (dt > 0.f) t += dt; else t = 0.f; /* Test for a switch. */ - - if (sol_swch_test(fp, ball)) + if (sol_swch_test(fp)) audio_play(AUD_SWITCH, 1.f); /* Test for a jump. */ - if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 1) + if (config_get_d(CONFIG_BALL_COLLISIONS)) { - jump_b = 1; - jump_e = 0; - jump_dt = 0.f; + for (i = 1; i < curr_party() + 1; i++) + { + if (!jump_u && jump_e == 1 && jump_b == 0 && + sol_jump_test(fp, jump_p, i) == 1) + { + jump_b = 1; + jump_e = 0; + jump_dt = 0.f; + jump_u = i * 2; - audio_play(AUD_JUMP, 1.f); + audio_play(AUD_JUMP, 1.f); + } + if (jump_e == 0 && jump_b == 0 && + sol_jump_test(fp, jump_p, i) == 0) + jump_e = 1; + if (!jump_b && jump_u && i == jump_u / 2 && + sol_jump_test(fp, jump_p, i) == 0) + jump_u = 0; + } + + for (i = 0; i < fp->yc; i++) + { + if (!jump_u && jump_e == 1 && jump_b == 0 && + sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 1) + { + jump_b = 1; + jump_e = 0; + jump_dt = 0.f; + jump_u = i * 2 + 1; + + audio_play(AUD_JUMP, 1.f); + } + if (jump_e == 0 && jump_b == 0 && + sol_jump_test(fp, + jump_p, fp->yv + i - fp->uv) == 0) + jump_e = 1; + if (!jump_b && jump_u && i == jump_u / 2 && + sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 0) + jump_u = 0; + } } - if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 0) - jump_e = 1; + else + { + if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 1) + { + jump_b = 1; + jump_e = 0; + jump_dt = 0.f; + audio_play(AUD_JUMP, 1.f); + } + if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 0) + jump_e = 1; + } + /* Test for fall-out. */ - if (fp->uv[ball].p[1] < -10.f) + if (fp->uv[ball].p[1] < -10.0f) return GAME_FALL; /* Test for a goal or stop. */ - if (t > 1.f) + if (t > 1.0f) { - t = 0.f; + t = 0.0f; - if (sol_goal_test(fp, p, ball)) - return GAME_GOAL; + if (config_get_d(CONFIG_BALL_COLLISIONS)) + { + switch (sol_goal_test(fp, p, ball)) + { + case 2: /* All balls stopped & Player's ball stopped in hole */ + t = 0.0f; + return GAME_GOAL; + break; + case 1: /* All balls stopped */ + t = 0.0f; + return GAME_STOP; + break; + case 0: /* At least one ball is still in motion */ + return GAME_NONE; + break; + default: /* Should never reach this */ + break; + } + } + else - return GAME_STOP; + { + if (sol_goal_test(fp, p, ball)) + return GAME_GOAL; + else + return GAME_STOP; + } } return GAME_NONE; } +void game_set_played(int b) +{ + if (ball) + file.uv[ball].P = b; + if (!b) + { + file.uv[0].P = 0; + file.uv[1].P = 0; + file.uv[2].P = 0; + file.uv[3].P = 0; + file.uv[4].P = 0; + } +} + /* * On most hardware, rendering requires much more computing power than * physics. Since physics takes less time than graphics, it make sense to @@ -517,18 +776,49 @@ if (jump_b) { - jump_dt += dt; + if (config_get_d(CONFIG_BALL_COLLISIONS)) + { + jump_dt += dt; - /* Handle a jump. */ + /* Handle a jump. */ - if (0.5 < jump_dt) + if (0.5 < jump_dt) + { + if (jump_u % 2) + { + fp->yv[jump_u / 2].p[0] = jump_p[0]; + fp->yv[jump_u / 2].p[1] = jump_p[1]; + fp->yv[jump_u / 2].p[2] = jump_p[2]; + } + + else + { + fp->uv[jump_u / 2].p[0] = jump_p[0]; + fp->uv[jump_u / 2].p[1] = jump_p[1]; + fp->uv[jump_u / 2].p[2] = jump_p[2]; + } + } + if (1.f < jump_dt) + { + jump_b = 0; + } + } + + else { - fp->uv[ball].p[0] = jump_p[0]; - fp->uv[ball].p[1] = jump_p[1]; - fp->uv[ball].p[2] = jump_p[2]; + jump_dt += dt; + + /* Handle a jump. */ + + if (0.5 < jump_dt) + { + fp->uv[ball].p[0] = jump_p[0]; + fp->uv[ball].p[1] = jump_p[1]; + fp->uv[ball].p[2] = jump_p[2]; + } + if (1.f < jump_dt) + jump_b = 0; } - if (1.f < jump_dt) - jump_b = 0; } else { @@ -542,8 +832,13 @@ for (i = 0; i < n; i++) { + int ball_in_hole = 0; + d = sol_step(fp, g, t, ball, &m); + if ((ball_in_hole = game_check_balls(fp))) + hole_goal(ball_in_hole); + if (b < d) b = d; if (m) @@ -568,10 +863,20 @@ * friction too early and stopping the ball prematurely. */ - file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m; - file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE; - file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m; + if (config_get_d(CONFIG_BALL_COLLISIONS)) + { + file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m; + file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE; + file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m; + } + else + { + file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m; + file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE; + file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m; + } + view_m = 0.f; } @@ -705,4 +1010,3 @@ } /*---------------------------------------------------------------------------*/ - Index: putt/st_all.c =================================================================== --- putt/st_all.c (.../trunk) (revision 1922) +++ putt/st_all.c (.../branches/putt-collisions) (revision 1922) @@ -239,7 +239,7 @@ static void title_timer(int id, float dt) { - float g[3] = { 0.f, 0.f, 0.f }; + float g[3] = { 0.0f, -9.8f, 0.0f }; game_step(g, dt); game_set_fly(fcosf(time_state() / 10.f)); @@ -873,6 +873,8 @@ if (paused) paused = 0; + game_set_played(1); + return 0; } @@ -891,7 +893,7 @@ static void stroke_timer(int id, float dt) { - float g[3] = { 0.f, 0.f, 0.f }; + float g[3] = { 0.0f, -9.8f, 0.0f }; float k; @@ -1002,7 +1004,7 @@ if (paused) paused = 0; else - hole_goal(); + hole_goal(0); hud_init(); @@ -1089,7 +1091,7 @@ static void stop_timer(int id, float dt) { - float g[3] = { 0.f, 0.f, 0.f }; + float g[3] = { 0.0f, -9.8f, 0.0f }; game_update_view(dt); game_step(g, dt); @@ -1216,6 +1218,8 @@ if (paused) paused = 0; + game_set_played(0); + return score_card(_("Scores"), gui_yel, gui_red); } Index: putt/game.h =================================================================== --- putt/game.h (.../trunk) (revision 1922) +++ putt/game.h (.../branches/putt-collisions) (revision 1922) @@ -43,6 +43,8 @@ void game_update_view(float); +void game_set_played(int); + void game_set_rot(int); void game_clr_mag(void); void game_set_mag(int); Index: putt/main.c =================================================================== --- putt/main.c (.../trunk) (revision 1922) +++ putt/main.c (.../branches/putt-collisions) (revision 1922) @@ -48,6 +48,7 @@ return 1; } + /*---------------------------------------------------------------------------*/ static void toggle_wire(void) @@ -294,4 +295,3 @@ } /*---------------------------------------------------------------------------*/ - Index: putt/hole.c =================================================================== --- putt/hole.c (.../trunk) (revision 1922) +++ putt/hole.c (.../branches/putt-collisions) (revision 1922) @@ -134,7 +134,7 @@ if (p <= party) { - for (h = 1; h <= hole && h < count; h++) + for (h = 1; h <= hole && h <= 18; h++) T += score_v[h][p]; sprintf(str, "%d", T); @@ -152,7 +152,7 @@ if (p <= party) { - for (h = 1; h <= hole && h <= count / 2; h++) + for (h = 1; h <= hole && h <= 9; h++) T += score_v[h][p]; sprintf(str, "%d", T); @@ -167,11 +167,10 @@ static char str[MAXSTR]; int h, T = 0; - int out = count / 2; - if (hole > out && p <= party) + if (hole > 9 && p <= party) { - for (h = out + 1; h <= hole && h < count; h++) + for (h = 10; h <= hole && h <= 18; h++) T += score_v[h][p]; sprintf(str, "%d", T); @@ -267,31 +266,63 @@ return 0; } -void hole_goal(void) +void hole_goal(int playerid) { - score_v[hole][player]++; + if (playerid) + { + /* HACK: If the player has already beaten the hole, return */ + if (stat_v[playerid] == 1) + return; - if (score_v[hole][player] == 1) - audio_play(AUD_ONE, 1.0f); + if (score_v[hole][playerid] == 1) + audio_play(AUD_ONE, 1.0f); - else if (score_v[hole][player] == score_v[hole][0] - 2) - audio_play(AUD_EAGLE, 1.0f); - else if (score_v[hole][player] == score_v[hole][0] - 1) - audio_play(AUD_BIRDIE, 1.0f); - else if (score_v[hole][player] == score_v[hole][0]) - audio_play(AUD_PAR, 1.0f); - else if (score_v[hole][player] == score_v[hole][0] + 1) - audio_play(AUD_BOGEY, 1.0f); - else if (score_v[hole][player] == score_v[hole][0] + 2) - audio_play(AUD_DOUBLE, 1.0f); + else if (score_v[hole][playerid] == score_v[hole][0] - 2) + audio_play(AUD_EAGLE, 1.0f); + else if (score_v[hole][playerid] == score_v[hole][0] - 1) + audio_play(AUD_BIRDIE, 1.0f); + else if (score_v[hole][playerid] == score_v[hole][0]) + audio_play(AUD_PAR, 1.0f); + else if (score_v[hole][playerid] == score_v[hole][0] + 1) + audio_play(AUD_BOGEY, 1.0f); + else if (score_v[hole][playerid] == score_v[hole][0] + 2) + audio_play(AUD_DOUBLE, 1.0f); + else + audio_play(AUD_SUCCESS, 1.0f); + + stat_v[playerid] = 1; + done++; + + if (done == party) + audio_music_fade_out(2.0f); + } + else - audio_play(AUD_SUCCESS, 1.0f); + { + score_v[hole][player]++; - stat_v[player] = 1; - done++; + if (score_v[hole][player] == 1) + audio_play(AUD_ONE, 1.0f); - if (done == party) - audio_music_fade_out(2.0f); + else if (score_v[hole][player] == score_v[hole][0] - 2) + audio_play(AUD_EAGLE, 1.0f); + else if (score_v[hole][player] == score_v[hole][0] - 1) + audio_play(AUD_BIRDIE, 1.0f); + else if (score_v[hole][player] == score_v[hole][0]) + audio_play(AUD_PAR, 1.0f); + else if (score_v[hole][player] == score_v[hole][0] + 1) + audio_play(AUD_BOGEY, 1.0f); + else if (score_v[hole][player] == score_v[hole][0] + 2) + audio_play(AUD_DOUBLE, 1.0f); + else + audio_play(AUD_SUCCESS, 1.0f); + + stat_v[player] = 1; + done++; + + if (done == party) + audio_music_fade_out(2.0f); + } } void hole_stop(void) Index: putt/st_balt.c =================================================================== --- putt/st_balt.c (.../trunk) (revision 0) +++ putt/st_balt.c (.../branches/putt-collisions) (revision 1922) @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2003 Robert Kooima - 2008 Byron Johnson + * Part of the Neverball Project http://icculus.org/neverball/ + * + * 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 "gui.h" +#include "back.h" +#include "part.h" +#include "game.h" +#include "audio.h" +#include "config.h" + +#include "st_balt.h" + +extern struct state st_conf; +extern struct state st_null; + +/*---------------------------------------------------------------------------*/ + +enum { + BALT_BACK = 1, + BALT_GOLF, + BALT_BILL, + BALT_CRAZ +}; + +static int balt_action(int i) +{ + int r = 1; + + audio_play(AUD_MENU, 1.0f); + + switch (i) + { + case BALT_BACK: + goto_state(&st_conf); + break; + + case BALT_GOLF: + goto_state(&st_null); + config_set_s(CONFIG_BALL_GAMMA, "0.78"); + goto_state(&st_balt); + break; + + case BALT_BILL: + goto_state(&st_null); + config_set_s(CONFIG_BALL_GAMMA, "1.00"); + goto_state(&st_balt); + break; + + case BALT_CRAZ: + goto_state(&st_null); + config_set_s(CONFIG_BALL_GAMMA, "1.50"); + goto_state(&st_balt); + break; + + default: + break; + } + + return r; +} + +static int balt_enter(void) +{ + int id, jd; + + char gamma[MAXNAM]; + + config_get_s(CONFIG_BALL_GAMMA, gamma, MAXNAM); + + back_init("back/gui.png", config_get_d(CONFIG_GEOMETRY)); + + if ((id = gui_vstack(0))) + { + if ((jd = gui_harray(id))) + { + gui_space(jd); + gui_space(jd); + gui_start(jd, _("Back"), GUI_SML, BALT_BACK, 0); + } + + gui_space(id); + gui_space(id); + + if ((jd = gui_harray(id))) + { + gui_state(jd, _("Billiards"), GUI_SML, BALT_BILL, + strcmp(gamma, "1.00") == 0 || + strcmp(gamma, "1.0") == 0); + gui_state(jd, _("Golf Balls"), GUI_SML, BALT_GOLF, + strcmp(gamma, "0.78") == 0); + } + + if ((jd = gui_harray(id))) + { + gui_state(jd, _("Crazy Balls"), GUI_SML, BALT_CRAZ, + strcmp(gamma, "1.50") == 0 || + strcmp(gamma, "1.5") == 0); + } + + gui_layout(id, 0, 0); + } + + audio_music_fade_to(0.5f, "bgm/inter.ogg"); + + return id; +} + +static void balt_leave(int id) +{ + back_free(); + gui_delete(id); +} + +static void balt_paint(int id, float st) +{ + config_push_persp((float) config_get_d(CONFIG_VIEW_FOV), 0.1f, FAR_DIST); + { + back_draw(0); + } + config_pop_matrix(); + gui_paint(id); +} + +static void balt_timer(int id, float dt) +{ + gui_timer(id, dt); +} + +static void balt_point(int id, int x, int y, int dx, int dy) +{ + gui_pulse(gui_point(id, x, y), 1.2f); +} + +static void balt_stick(int id, int a, int v) +{ + if (config_tst_d(CONFIG_JOYSTICK_AXIS_X, a)) + gui_pulse(gui_stick(id, v, 0), 1.2f); + if (config_tst_d(CONFIG_JOYSTICK_AXIS_Y, a)) + gui_pulse(gui_stick(id, 0, v), 1.2f); +} + +static int balt_click(int b, int d) +{ + if (b < 0 && d == 1) + return balt_action(gui_token(gui_click())); + return 1; +} + +static int balt_keybd(int c, int d) +{ + return (d && c == SDLK_ESCAPE) ? goto_state(&st_conf) : 1; +} + +static int balt_buttn(int b, int d) +{ + if (d) + { + if (config_tst_d(CONFIG_JOYSTICK_BUTTON_A, b)) + return balt_action(gui_token(gui_click())); + if (config_tst_d(CONFIG_JOYSTICK_BUTTON_B, b)) + return goto_state(&st_conf); + if (config_tst_d(CONFIG_JOYSTICK_BUTTON_EXIT, b)) + return goto_state(&st_conf); + } + return 1; +} + +/*---------------------------------------------------------------------------*/ + + +struct state st_balt = { + balt_enter, + balt_leave, + balt_paint, + balt_timer, + balt_point, + balt_stick, + NULL, + balt_click, + balt_keybd, + balt_buttn, + 1, 0 +}; Property changes on: putt/st_balt.c ___________________________________________________________________ Name: svn:executable + * Index: putt/st_conf.c =================================================================== --- putt/st_conf.c (.../trunk) (revision 1922) +++ putt/st_conf.c (.../branches/putt-collisions) (revision 1922) @@ -25,6 +25,7 @@ #include "st_conf.h" #include "st_all.h" #include "st_resol.h" +#include "st_balt.h" /*---------------------------------------------------------------------------*/ @@ -38,7 +39,10 @@ CONF_SHDON, CONF_SHDOF, CONF_BACK, - CONF_RESOL + CONF_RESOL, + CONF_BALT, + CONF_BCLON, + CONF_BCLOF }; static int music_id[11]; @@ -112,6 +116,22 @@ goto_state(&st_resol); break; + case CONF_BALT: + goto_state(&st_balt); + break; + + case CONF_BCLON: + goto_state(&st_null); + config_set_d(CONFIG_BALL_COLLISIONS, 1); + goto_state(&st_conf); + break; + + case CONF_BCLOF: + goto_state(&st_null); + config_set_d(CONFIG_BALL_COLLISIONS, 0); + goto_state(&st_conf); + break; + default: if (100 <= i && i <= 110) { @@ -151,18 +171,39 @@ if ((id = gui_vstack(0))) { int f = config_get_d(CONFIG_FULLSCREEN); + int c = config_get_d(CONFIG_BALL_COLLISIONS); int t = config_get_d(CONFIG_TEXTURES); int g = config_get_d(CONFIG_GEOMETRY); int h = config_get_d(CONFIG_SHADOW); int s = config_get_d(CONFIG_SOUND_VOLUME); int m = config_get_d(CONFIG_MUSIC_VOLUME); + char gamma[MAXNAM]; + char resolution[20]; + char balt[22]; + + config_get_s(CONFIG_BALL_GAMMA, gamma, MAXNAM); + sprintf(resolution, "%d x %d", config_get_d(CONFIG_WIDTH), config_get_d(CONFIG_HEIGHT)); + if (strcmp(gamma, "0.78") == 0) + strcpy(balt, "Golf Balls"); + + else if (strcmp(gamma, "1.00") == 0 || + strcmp(gamma, "1.0") == 0) + strcpy(balt, "Billiards"); + + else if (strcmp(gamma, "1.50") == 0 || + strcmp(gamma, "1.5") == 0) + strcpy(balt, "Crazy Balls"); + + else + sprintf(balt, "Custom"); + if ((jd = gui_harray(id))) { gui_label(jd, _("Options"), GUI_SML, GUI_ALL, 0, 0); @@ -184,7 +225,7 @@ if ((jd = gui_harray(id)) && (kd = gui_harray(jd))) { - gui_state(kd, resolution, GUI_SML, CONF_RESOL, 0); + gui_state(kd, resolution, GUI_SML, CONF_RESOL, 0); gui_label(jd, _("Resolution"), GUI_SML, GUI_ALL, 0, 0); } @@ -194,30 +235,57 @@ if ((jd = gui_harray(id)) && (kd = gui_harray(jd))) { - gui_state(kd, _("Low"), GUI_SML, CONF_TEXLO, (t == 2)); - gui_state(kd, _("High"), GUI_SML, CONF_TEXHI, (t == 1)); + gui_state(kd, _("Off"), GUI_SML, CONF_BCLOF, (c == 0)); + gui_state(kd, _("On"), GUI_SML, CONF_BCLON, (c == 1)); - gui_label(jd, _("Textures"), GUI_SML, GUI_ALL, 0, 0); + gui_label(jd, _("Ball Collisions"), GUI_SML, GUI_ALL, 0, 0); } if ((jd = gui_harray(id)) && (kd = gui_harray(jd))) { - gui_state(kd, _("Low"), GUI_SML, CONF_GEOLO, (g == 0)); - gui_state(kd, _("High"), GUI_SML, CONF_GEOHI, (g == 1)); + if (c == 1) + { + gui_state(kd, balt, GUI_SML, CONF_BALT, 0); + gui_space(jd); + } + else + { + gui_space(kd); + gui_space(jd); + gui_space(id); + } + } - gui_label(jd, _("Geometry"), GUI_SML, GUI_ALL, 0, 0); + gui_space(id); + + if ((jd = gui_harray(id)) && + (kd = gui_harray(jd))) + { + gui_state(kd, _("Low"), GUI_SML, CONF_TEXLO, (t == 2)); + gui_state(kd, _("High"), GUI_SML, CONF_TEXHI, (t == 1)); + + gui_label(jd, _("Textures"), GUI_SML, GUI_ALL, 0, 0); } if ((jd = gui_harray(id)) && (kd = gui_harray(jd))) { - gui_state(kd, _("Off"), GUI_SML, CONF_SHDOF, (h == 0)); - gui_state(kd, _("On"), GUI_SML, CONF_SHDON, (h == 1)); + gui_state(kd, _("Low"), GUI_SML, CONF_GEOLO, (g == 0)); + gui_state(kd, _("High"), GUI_SML, CONF_GEOHI, (g == 1)); - gui_label(jd, _("Shadow"), GUI_SML, GUI_ALL, 0, 0); + gui_label(jd, _("Geometry"), GUI_SML, GUI_ALL, 0, 0); } + if ((jd = gui_harray(id)) && + (kd = gui_harray(jd))) + { + gui_state(kd, _("Off"), GUI_SML, CONF_SHDOF, (h == 0)); + gui_state(kd, _("On"), GUI_SML, CONF_SHDON, (h == 1)); + + gui_label(jd, _("Shadow"), GUI_SML, GUI_ALL, 0, 0); + } + gui_space(id); if ((jd = gui_harray(id)) && Index: putt/hole.h =================================================================== --- putt/hole.h (.../trunk) (revision 1922) +++ putt/hole.h (.../branches/putt-collisions) (revision 1922) @@ -28,7 +28,7 @@ void hole_goto(int, int); int hole_next(void); int hole_move(void); -void hole_goal(void); +void hole_goal(int); void hole_stop(void); void hole_fall(void); Index: putt/st_balt.h =================================================================== --- putt/st_balt.h (.../trunk) (revision 0) +++ putt/st_balt.h (.../branches/putt-collisions) (revision 1922) @@ -0,0 +1,8 @@ +#ifndef ST_BALT_H +#define ST_BALT_H + +#include "state.h" + +extern struct state st_balt; + +#endif Property changes on: putt/st_balt.h ___________________________________________________________________ Name: svn:executable + * Index: Makefile =================================================================== --- Makefile (.../trunk) (revision 1922) +++ Makefile (.../branches/putt-collisions) (revision 1922) @@ -125,6 +125,8 @@ share/solid.o \ share/binary.o \ share/base_config.o \ + share/config.o \ + share/sync.o \ share/mapc.o BALL_OBJS := \ share/lang.o \ @@ -200,6 +202,7 @@ putt/course.o \ putt/st_all.o \ putt/st_conf.o \ + putt/st_balt.o \ putt/main.o BALL_DEPS := $(BALL_OBJS:.o=.d) @@ -229,7 +232,7 @@ $(CC) $(ALL_CFLAGS) -o $(PUTT_TARG) $(PUTT_OBJS) $(LDFLAGS) $(ALL_LIBS) $(MAPC_TARG) : $(MAPC_OBJS) - $(CC) $(ALL_CFLAGS) -o $(MAPC_TARG) $(MAPC_OBJS) $(LDFLAGS) $(BASE_LIBS) + $(CC) $(ALL_CFLAGS) -o $(MAPC_TARG) $(MAPC_OBJS) $(LDFLAGS) $(ALL_LIBS) # Work around some extremely helpful sdl-config scripts.