share/state.o \
share/audio.o \
share/text.o \
+ share/sync.o \
ball/hud.o \
ball/mode.o \
ball/game.o \
share/state.o \
share/gui.o \
share/text.o \
+ share/sync.o \
putt/hud.o \
putt/game.o \
putt/hole.o \
/*---------------------------------------------------------------------------*/
#define MAGIC 0x52424EAF
-#define DEMO_VERSION 4
+#define DEMO_VERSION 5
#define DATELEN 20
return 0;
}
-void demo_play_step(float dt)
+void demo_play_step()
{
if (demo_fp)
- {
- put_float(demo_fp, &dt);
- put_game_state(demo_fp);
- }
+ input_put(demo_fp);
}
void demo_play_stat(const struct level_game *lg)
if (lg)
{
- lg->mode = demo_replay.mode;
+ lg->mode = demo_replay.mode;
lg->score = demo_replay.score;
lg->times = demo_replay.times;
- lg->time = demo_replay.time;
- lg->goal = demo_replay.goal;
+ lg->time = demo_replay.time;
+ lg->goal = demo_replay.goal;
/* A normal replay demo */
audio_music_fade_to(0.5f, demo_level_replay.song);
return 0;
}
-int demo_replay_step(float *dt)
+int demo_replay_step(float dt)
{
- const float g[3] = { 0.0f, -9.8f, 0.0f };
+ const float gdn[3] = { 0.0f, -9.8f, 0.0f };
+ const float gup[3] = { 0.0f, +9.8f, 0.0f };
if (demo_fp)
{
- get_float(demo_fp, dt);
-
- if (feof(demo_fp) == 0)
+ if (input_get(demo_fp))
{
- /* Play out current game state for particles, clock, etc. */
-
- if (demo_status == GAME_NONE)
- demo_status = game_step(g, *dt, 1);
- else
- game_step(g, *dt, 0);
-
- /* Load real current game state from file. */
-
- if (get_game_state(demo_fp))
- return 1;
+ /* Play out current game state. */
+
+ switch (demo_status)
+ {
+ case GAME_NONE:
+ demo_status = game_step(gdn, dt, 1); break;
+ case GAME_GOAL:
+ (void) game_step(gup, dt, 0); break;
+ default:
+ (void) game_step(gdn, dt, 0); break;
+ }
+
+ return 1;
}
}
return 0;
int demo_play_init(const char *, const struct level *,
const struct level_game *);
-void demo_play_step(float);
+void demo_play_step(void);
void demo_play_stat(const struct level_game *);
void demo_play_stop(void);
/*---------------------------------------------------------------------------*/
int demo_replay_init(const char *, struct level_game *);
-int demo_replay_step(float *);
+int demo_replay_step(float);
void demo_replay_stop(int);
void demo_replay_dump_info(void);
static float clock = 0.f; /* Clock time */
static int clock_down = 1; /* Clock go up or down? */
-static float game_ix; /* Input rotation about X axis */
-static float game_iz; /* Input rotation about Z axis */
static float game_rx; /* Floor rotation about X axis */
static float game_rz; /* Floor rotation about Z axis */
static float view_a; /* Ideal view rotation about Y axis */
-static float view_ry; /* Angular velocity about Y axis */
static float view_dc; /* Ideal view distance above ball */
static float view_dp; /* Ideal view distance above ball */
static float view_dz; /* Ideal view distance behind ball */
/*---------------------------------------------------------------------------*/
+/*
+ * This is an abstraction of the game's input state. All input is
+ * encapsulated here, and all references to the input by the game are made
+ * here. This has the effect of homogenizing input for use in replay
+ * recording and playback.
+ *
+ * x and z:
+ * -32767 = -ANGLE_BOUND
+ * +32767 = +ANGLE_BOUND
+ *
+ * r:
+ * -32767 = -VIEWR_BOUND
+ * +32767 = +VIEWR_BOUND
+ *
+ */
+
+struct input
+{
+ short x;
+ short z;
+ short r;
+ short c;
+};
+
+static struct input input_current;
+
+static void input_init(void)
+{
+ input_current.x = 0;
+ input_current.z = 0;
+ input_current.r = 0;
+ input_current.c = 0;
+}
+
+static void input_set_x(float x)
+{
+ if (x < -ANGLE_BOUND) x = -ANGLE_BOUND;
+ if (x > ANGLE_BOUND) x = ANGLE_BOUND;
+
+ input_current.x = (short) (32767.0f * x / ANGLE_BOUND);
+}
+
+static void input_set_z(float z)
+{
+ if (z < -ANGLE_BOUND) z = -ANGLE_BOUND;
+ if (z > ANGLE_BOUND) z = ANGLE_BOUND;
+
+ input_current.z = (short) (32767.0f * z / ANGLE_BOUND);
+}
+
+static void input_set_r(float r)
+{
+ if (r < -VIEWR_BOUND) r = -VIEWR_BOUND;
+ if (r > VIEWR_BOUND) r = VIEWR_BOUND;
+
+ input_current.r = (short) (32767.0f * r / VIEWR_BOUND);
+}
+
+static void input_set_c(int c)
+{
+ input_current.c = (short) c;
+}
+
+static float input_get_x(void)
+{
+ return ANGLE_BOUND * (float) input_current.x / 32767.0f;
+}
+
+static float input_get_z(void)
+{
+ return ANGLE_BOUND * (float) input_current.z / 32767.0f;
+}
+
+static float input_get_r(void)
+{
+ return VIEWR_BOUND * (float) input_current.r / 32767.0f;
+}
+
+static int input_get_c(void)
+{
+ return (int) input_current.c;
+}
+
+int input_put(FILE *fout)
+{
+ if (game_state)
+ {
+ put_short(fout, &input_current.x);
+ put_short(fout, &input_current.z);
+ put_short(fout, &input_current.r);
+ put_short(fout, &input_current.c);
+
+ return 1;
+ }
+ return 0;
+}
+
+int input_get(FILE *fin)
+{
+ if (game_state)
+ {
+ get_short(fin, &input_current.x);
+ get_short(fin, &input_current.z);
+ get_short(fin, &input_current.r);
+ get_short(fin, &input_current.c);
+
+ return (feof(fin) ? 0 : 1);
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
static int grow = 0; /* Should the ball be changing size? */
static float grow_orig = 0; /* the original ball size */
static float grow_goal = 0; /* how big or small to get! */
static void view_init(void)
{
- view_a = 0.f;
- view_ry = 0.f;
-
view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
view_dp = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
view_dc = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
view_dz = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
view_k = 1.0f;
+ view_a = 0.0f;
view_c[0] = 0.f;
view_c[1] = view_dc;
game_state = 1;
- game_ix = 0.f;
- game_iz = 0.f;
- game_rx = 0.f;
- game_rz = 0.f;
+ input_init();
+
+ game_rx = 0.0f;
+ game_rz = 0.0f;
/* Initialize jump and goal states. */
static void game_update_grav(float h[3], const float g[3])
{
- struct s_file *fp = &file;
-
float x[3];
float y[3] = { 0.f, 1.f, 0.f };
float z[3];
/* Compute the gravity vector from the given world rotations. */
- v_sub(z, view_p, fp->uv->p);
+ z[0] = fsinf(V_RAD(view_a));
+ z[1] = 0.0;
+ z[2] = fcosf(V_RAD(view_a));
+
v_crs(x, y, z);
v_crs(z, x, y);
v_nrm(x, x);
static void game_update_view(float dt)
{
float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
- float dx = view_ry * dt * 5.0f;
+ float da = input_get_r() * dt * 90.0f;
float k;
- view_a += view_ry * dt * 90.f;
+ float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
+
+ view_a += da;
/* Center the view about the ball. */
v_cpy(view_c, file.uv->p);
v_inv(view_v, file.uv->v);
- switch (config_get_d(CONFIG_CAMERA))
+ view_e[2][0] = fsinf(V_RAD(view_a));
+ view_e[2][1] = 0.0;
+ view_e[2][2] = fcosf(V_RAD(view_a));
+
+ switch (input_get_c())
{
- case 1: /* Camera 1: Viewpoint chases the ball position. */
+ case 1: /* Camera 1: Viewpoint chases the ball position. */
+
+ /* TODO: This camera no longer exists. */
- v_sub(view_e[2], view_p, view_c);
break;
case 2: /* Camera 2: View vector is given by view angle. */
- view_e[2][0] = fsinf(V_RAD(view_a));
- view_e[2][1] = 0.f;
- view_e[2][2] = fcosf(V_RAD(view_a));
-
- dx = 0.0f;
-
break;
default: /* Default: View vector approaches the ball velocity vector. */
- k = v_dot(view_v, view_v);
-
- v_sub(view_e[2], view_p, view_c);
- v_mad(view_e[2], view_e[2], view_v, k * dt / 4);
+ v_mad(view_e[2], view_e[2], view_v, v_dot(view_v, view_v) * dt / 4);
break;
}
if (view_k < 0.5) view_k = 0.5;
- v_cpy(view_p, file.uv->p);
- v_mad(view_p, view_p, view_e[0], dx * view_k);
- v_mad(view_p, view_p, view_e[1], view_dp * view_k);
- v_mad(view_p, view_p, view_e[2], view_dz * view_k);
+ v_scl(v, view_e[1], view_dp * view_k);
+ v_mad(v, v, view_e[2], view_dz * view_k);
+ m_rot(M, Y, V_RAD(da));
+ m_vxfm(view_p, M, v);
+ v_add(view_p, view_p, file.uv->p);
/* Compute the new view center. */
float c[3];
/* Test for an item. */
+
if (bt && (hp = sol_item_test(fp, p, COIN_RADIUS)))
{
const char *sound = AUD_COIN;
coins += hp->n;
/* Check for goal open. */
+
if (goal_c > 0)
{
goal_c -= hp->n;
audio_play(sound, 1.f);
/* Reset item type. */
+
hp->t = ITEM_NONE;
}
/* Test for a switch. */
+
if (sol_swch_test(fp, 0))
audio_play(AUD_SWITCH, 1.f);
return GAME_NONE;
}
-/*
- * On most hardware, rendering requires much more computing power than
- * physics. Since physics takes less time than graphics, it make sense to
- * detach the physics update time step from the graphics frame rate. By
- * performing multiple physics updates for each graphics update, we get away
- * with higher quality physics with little impact on overall performance.
- *
- * Toward this end, we establish a baseline maximum physics time step. If
- * the measured frame time exceeds this maximum, we cut the time step in
- * half, and do two updates. If THIS time step exceeds the maximum, we do
- * four updates. And so on. In this way, the physics system is allowed to
- * seek an optimal update rate independent of, yet in integral sync with, the
- * graphics frame rate.
- */
-
int game_step(const float g[3], float dt, int bt)
{
- struct s_file *fp = &file;
-
- float h[3];
- float d = 0.f;
- float b = 0.f;
- float t;
- int i, n = 1;
-
if (game_state)
{
- t = dt;
+ struct s_file *fp = &file;
+
+ float h[3];
/* Smooth jittery or discontinuous input. */
- if (t < RESPONSE)
- {
- game_rx += (game_ix - game_rx) * t / RESPONSE;
- game_rz += (game_iz - game_rz) * t / RESPONSE;
- }
- else
- {
- game_rx = game_ix;
- game_rz = game_iz;
- }
+ game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
+ game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
grow_step(fp, dt);
game_update_grav(h, g);
- part_step(h, t);
+ part_step(h, dt);
if (jump_b)
{
- jump_dt += t;
+ jump_dt += dt;
/* Handle a jump. */
- if (0.5 < jump_dt)
+ 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 (1.f < jump_dt)
+ if (1.0f < jump_dt)
jump_b = 0;
}
else
{
/* Run the sim. */
- while (t > MAX_DT && n < MAX_DN)
- {
- t /= 2;
- n *= 2;
- }
-
- for (i = 0; i < n; i++)
- if (b < (d = sol_step(fp, h, t, 0, NULL)))
- b = d;
+ float b = sol_step(fp, h, dt, 0, NULL);
/* Mix the sound of a ball bounce. */
- if (b > 0.5)
+ if (b > 0.5f)
{
float k = (b - 0.5f) * 2.0f;
/*---------------------------------------------------------------------------*/
-void game_no_aa(void)
-{
- float max = game_ix * game_ix + game_iz * game_iz;
- if (max > ANGLE_BOUND * ANGLE_BOUND)
- {
- max = ANGLE_BOUND / sqrt(max);
- game_ix *= max;
- game_iz *= max;
- }
-}
-
void game_set_x(int k)
{
- game_ix = -(ANGLE_BOUND) * k / JOY_MAX;
-#if NO_AA
- game_no_aa();
-#endif
+ input_set_x(-ANGLE_BOUND * k / JOY_MAX);
}
void game_set_z(int k)
{
- game_iz = +ANGLE_BOUND * k / JOY_MAX;
-#if NO_AA
- game_no_aa();
-#endif
+ input_set_z(+ANGLE_BOUND * k / JOY_MAX);
}
void game_set_pos(int x, int y)
{
- game_ix += 40.f * y / config_get_d(CONFIG_MOUSE_SENSE);
- game_iz += 40.f * x / config_get_d(CONFIG_MOUSE_SENSE);
-
-#if NO_AA
- game_no_aa();
-#else
- if (game_ix > +ANGLE_BOUND) game_ix = +ANGLE_BOUND;
- if (game_ix < -ANGLE_BOUND) game_ix = -ANGLE_BOUND;
- if (game_iz > +ANGLE_BOUND) game_iz = +ANGLE_BOUND;
- if (game_iz < -ANGLE_BOUND) game_iz = -ANGLE_BOUND;
-#endif
+ input_set_x(input_get_x() + 40.0f * y / config_get_d(CONFIG_MOUSE_SENSE));
+ input_set_z(input_get_z() + 40.0f * x / config_get_d(CONFIG_MOUSE_SENSE));
+}
+
+void game_set_cam(int c)
+{
+ input_set_c(c);
}
void game_set_rot(float r)
{
- view_ry = r;
+ input_set_r(r);
}
/*---------------------------------------------------------------------------*/
}
/*---------------------------------------------------------------------------*/
-
-int put_game_state(FILE *fout)
-{
- if (game_state)
- {
- /* Write the view and tilt state. */
-
- put_float(fout, &game_rx);
- put_float(fout, &game_rz);
- put_array(fout, view_c, 3);
- put_array(fout, view_p, 3);
-
- /* Write the game simulation state. */
-
- put_file_state(fout, &file);
-
- return 1;
- }
- return 0;
-}
-
-int get_game_state(FILE *fin)
-{
- if (game_state)
- {
- /* Read the view and tilt state. */
-
- get_float(fin, &game_rx);
- get_float(fin, &game_rz);
- get_array(fin, view_c, 3);
- get_array(fin, view_p, 3);
-
- /* Read the game simulation state. */
-
- get_file_state(fin, &file);
-
- return (feof(fin) ? 0 : 1);
- }
- return 0;
-}
-
-/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
-#define MAX_DT 0.01666666 /* Maximum physics update cycle */
-#define MAX_DN 16 /* Maximum subdivisions of dt */
#define RESPONSE 0.05f /* Input smoothing time */
-
-#define ANGLE_BOUND 20.f /* Angle limit of floor tilting */
+#define ANGLE_BOUND 20.0f /* Angle limit of floor tilting */
+#define VIEWR_BOUND 10.0f /* Maximum rate of view rotation */
#define NO_AA 0 /* Disable Angle Acceleration */
/*---------------------------------------------------------------------------*/
void game_set_pos(int, int);
void game_set_x (int);
void game_set_z (int);
+void game_set_cam(int);
void game_set_rot(float);
void game_set_fly(float);
void game_step_fade(float);
void game_fade(float);
-int put_game_state(FILE *);
-int get_game_state(FILE *);
+/*---------------------------------------------------------------------------*/
+
+int input_put(FILE *);
+int input_get(FILE *);
/*---------------------------------------------------------------------------*/
static void hud_fps(void)
{
- static int fps = 0;
- static int then = 0;
- static int count = 0;
-
- int now = SDL_GetTicks();
-
- if (now - then > 250)
- {
- if (count && config_get_d(CONFIG_STATS))
- fprintf(stdout, "%f\n", (float) (now - then) / count);
-
- fps = count * 1000 / (now - then);
- then = now;
- count = 0;
-
- gui_set_count(fps_id, fps);
- }
- else count++;
+ gui_set_count(fps_id, config_perf());
}
void hud_init(void)
/* Run the main game loop. */
t0 = SDL_GetTicks();
+
while (loop())
- if ((t1 = SDL_GetTicks()) > t0)
- {
- st_timer((t1 - t0) / 1000.f);
- st_paint();
- SDL_GL_SwapBuffers();
+ {
+ t1 = SDL_GetTicks();
- t0 = t1;
+ /* Step the game state at least up to the current time. */
- if (config_get_d(CONFIG_NICE))
- SDL_Delay(1);
+ while (t1 > t0)
+ {
+ st_timer(DT);
+ t0 += (int) (DT * 1000);
}
+ /* Render. */
+
+ st_paint();
+ config_swap();
+
+ if (config_get_d(CONFIG_NICE))
+ SDL_Delay(1);
+ }
+
/* Gracefully close the game */
if (SDL_JoystickOpened(0))
static int first = 0;
static int total = 0;
-static float replay_time;
-static float global_time;
-
/*---------------------------------------------------------------------------*/
static int demo_action(int i)
gui_pulse(id, 1.2f);
}
- global_time = -1.f;
- replay_time = 0.f;
-
hud_update(0);
game_set_fly(0.f);
static void demo_play_timer(int id, float dt)
{
- float t;
-
game_step_fade(dt);
gui_timer(id, dt);
-
- global_time += dt;
hud_timer(dt);
/* Spin or skip depending on how fast the demo wants to run. */
- while (replay_time < global_time)
- if (demo_replay_step(&t))
- {
- replay_time += t;
- }
- else
- {
- demo_paused = 0;
- goto_state(&st_demo_end);
- break;
- }
+ if (!demo_replay_step(dt))
+ {
+ demo_paused = 0;
+ goto_state(&st_demo_end);
+ }
}
static int demo_play_keybd(int c, int d)
if (time_state() < 2.f)
{
+ demo_play_step();
game_step(g, dt, 0);
- demo_play_step(dt);
}
gui_timer(id, dt);
static void goal_timer(int id, float dt)
{
- static float DT = 0.0f;
+ static float t = 0.0f;
float g[3] = { 0.0f, 9.8f, 0.0f };
- DT += dt;
+ t += dt;
if (time_state() < 1.f)
{
+ demo_play_step();
game_step(g, dt, 0);
- demo_play_step(dt);
}
- else if (DT > 0.05f && coins_id)
+ else if (t > 0.05f && coins_id)
{
int coins = gui_value(coins_id);
audio_play(AUD_BALL, 1.0f);
}
}
- DT = 0.0f;
+ t = 0.0f;
}
gui_timer(id, dt);
/*---------------------------------------------------------------------------*/
-static float real_time;
-static float demo_time;
-
static int help_demo_enter(void)
{
- real_time = -1.f;
- demo_time = 0.f;
-
game_set_fly(0.f);
return 0;
static void help_demo_timer(int id, float dt)
{
- float t;
-
- real_time += dt;
-
game_step_fade(dt);
- while (demo_time < real_time)
- if (demo_replay_step(&t))
- demo_time += t;
- else
- {
- goto_state(&st_help);
- break;
- }
+ if (!demo_replay_step(dt))
+ goto_state(&st_help);
}
static int help_demo_buttn(int b, int d)
static void play_loop_timer(int id, float dt)
{
float k = ((SDL_GetModState() & KMOD_SHIFT) ?
- (float) config_get_d(CONFIG_ROTATE_FAST) / 100.f:
- (float) config_get_d(CONFIG_ROTATE_SLOW) / 100.f);
-
- static float at = 0;
+ (float) config_get_d(CONFIG_ROTATE_FAST) / 100.0f :
+ (float) config_get_d(CONFIG_ROTATE_SLOW) / 100.0f);
float g[3] = { 0.0f, -9.8f, 0.0f };
- at = (7 * at + dt) / 8;
-
- gui_timer(id, at);
- hud_timer(at);
+ gui_timer(id, dt);
+ hud_timer(dt);
game_set_rot(view_rotate * k);
+ game_set_cam(config_get_d(CONFIG_CAMERA));
- switch (game_step(g, at, 1))
+ game_step_fade(dt);
+ demo_play_step();
+
+ switch (game_step(g, dt, 1))
{
case GAME_GOAL:
level_stat(GAME_GOAL, curr_clock(), curr_coins());
default:
break;
}
-
- game_step_fade(dt);
- demo_play_step(at);
}
static void play_loop_point(int id, int x, int y, int dx, int dy)
/*---------------------------------------------------------------------------*/
static float real_time = 0.0f;
-static float demo_time = 0.0f;
static int mode = 0;
static int play_id = 0;
game_init(&title_level, 0, 0);
real_time = 0.0f;
- demo_time = 0.0f;
mode = 0;
SDL_EnableUNICODE(1);
static void title_timer(int id, float dt)
{
static const char *demo = NULL;
- float t;
real_time += dt;
if ((demo = demo_pick()))
{
demo_replay_init(demo, NULL);
- demo_time = 0.0f;
+ game_set_fly(0.0f);
real_time = 0.0f;
mode = 2;
}
case 2: /* Mode 2: Run demo. */
- while (demo_time < real_time)
- if (demo_replay_step(&t))
- demo_time += t;
- else
- {
- demo_replay_stop(0);
- game_fade(+1.0f);
- real_time = 0.0f;
- mode = 3;
- }
+ if (!demo_replay_step(dt))
+ {
+ demo_replay_stop(0);
+ game_fade(+1.0f);
+ real_time = 0.0f;
+ mode = 3;
+ }
break;
case 3: /* Mode 3: Fade out. Load title level. */
File scripts\neverball.bat
File scripts\neverputt.bat
- File tools\democonv.exe
- File tools\convert-replays.bat
# http://nsis.sourceforge.net/\
# Add_uninstall_information_to_Add/Remove_Programs
#define MAXNAM 9
#define GUI_FACE _("ttf/DejaVuSans-Bold.ttf")
+/*
+#define DT 0.0166666f
+*/
+#define DT 0.01111111f
/*---------------------------------------------------------------------------*/
#endif
}
+void put_short(FILE *fout, const short *s)
+{
+ const unsigned char *p = (const unsigned char *) s;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ fputc((int) p[1], fout);
+ fputc((int) p[0], fout);
+#else
+ fputc((int) p[0], fout);
+ fputc((int) p[1], fout);
+#endif
+}
+
void put_array(FILE *fout, const float *v, size_t n)
{
size_t i;
#endif
}
+void get_short(FILE *fin, short *s)
+{
+ unsigned char *p = (unsigned char *) s;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ p[1] = (unsigned char) fgetc(fin);
+ p[0] = (unsigned char) fgetc(fin);
+#else
+ p[0] = (unsigned char) fgetc(fin);
+ p[1] = (unsigned char) fgetc(fin);
+#endif
+}
+
void get_array(FILE *fin, float *v, size_t n)
{
size_t i;
void put_float(FILE *, const float *);
void put_index(FILE *, const int *);
+void put_short(FILE *, const short *);
void put_array(FILE *, const float *, size_t);
void get_float(FILE *, float *);
void get_index(FILE *, int *);
+void get_short(FILE *, short *);
void get_array(FILE *, float *, size_t);
void put_string(FILE *fout, const char *);
#include "config.h"
#include "glext.h"
#include "vec3.h"
+#include "sync.h"
/*---------------------------------------------------------------------------*/
config_set_d(CONFIG_AUDIO_BUFF, DEFAULT_AUDIO_BUFF);
config_set_d(CONFIG_MOUSE_SENSE, DEFAULT_MOUSE_SENSE);
config_set_d(CONFIG_MOUSE_INVERT, DEFAULT_MOUSE_INVERT);
+ config_set_d(CONFIG_VSYNC, DEFAULT_VSYNC);
config_set_d(CONFIG_NICE, DEFAULT_NICE);
config_set_d(CONFIG_FPS, DEFAULT_FPS);
config_set_d(CONFIG_SOUND_VOLUME, DEFAULT_SOUND_VOLUME);
config_set_d(CONFIG_MOUSE_SENSE, atoi(val));
else if (strcmp(key, "mouse_invert") == 0)
config_set_d(CONFIG_MOUSE_INVERT, atoi(val));
+ else if (strcmp(key, "vsync") == 0)
+ config_set_d(CONFIG_VSYNC, atoi(val));
else if (strcmp(key, "nice") == 0)
config_set_d(CONFIG_NICE, atoi(val));
else if (strcmp(key, "fps") == 0)
option_d[CONFIG_MOUSE_SENSE]);
fprintf(fp, "mouse_invert %d\n",
option_d[CONFIG_MOUSE_INVERT]);
+ fprintf(fp, "vsync %d\n",
+ option_d[CONFIG_VSYNC]);
fprintf(fp, "nice %d\n",
option_d[CONFIG_NICE]);
fprintf(fp, "fps %d\n",
if (SDL_SetVideoMode(w, h, 0, SDL_OPENGL | (f ? SDL_FULLSCREEN : 0)))
{
config_set_d(CONFIG_FULLSCREEN, f);
- config_set_d(CONFIG_WIDTH, w);
- config_set_d(CONFIG_HEIGHT, h);
+ config_set_d(CONFIG_WIDTH, w);
+ config_set_d(CONFIG_HEIGHT, h);
glViewport(0, 0, w, h);
glClearColor(0.0f, 0.0f, 0.1f, 0.0f);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LEQUAL);
+ if (config_get_d(CONFIG_VSYNC))
+ sync_init();
+
/* If GL supports multisample, and SDL got a multisample buffer... */
#ifdef GL_ARB_multisample
/*---------------------------------------------------------------------------*/
+static float ms = 0;
+static int fps = 0;
+static int last = 0;
+static int ticks = 0;
+static int frames = 0;
+
+int config_perf(void)
+{
+ return fps;
+}
+
+void config_swap(void)
+{
+ int dt;
+
+ SDL_GL_SwapBuffers();
+
+ /* Accumulate time passed and frames rendered. */
+
+ dt = (int) SDL_GetTicks() - last;
+
+ frames += 1;
+ ticks += dt;
+ last += dt;
+
+ /* Average over 250ms. */
+
+ if (ticks > 1000)
+ {
+ /* Round the frames-per-second value to the nearest integer. */
+
+ double k = 1000.0 * frames / ticks;
+ double f = floor(k);
+ double c = ceil (k);
+
+ /* Compute frame time and frames-per-second stats. */
+
+ fps = (int) ((c - k < k - f) ? c : f);
+ ms = (float) ticks / (float) frames;
+
+ /* Reset the counters for the next update. */
+
+ frames = 0;
+ ticks = 0;
+
+ /* Output statistics if configured. */
+
+ if (option_d[CONFIG_STATS])
+ fprintf(stdout, "%4d %8.4f\n", fps, ms);
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+
void config_set_d(int i, int d)
{
option_d[i] = d;
CONFIG_AUDIO_BUFF,
CONFIG_MOUSE_SENSE,
CONFIG_MOUSE_INVERT,
+ CONFIG_VSYNC,
CONFIG_NICE,
CONFIG_FPS,
CONFIG_SOUND_VOLUME,
#define DEFAULT_AUDIO_BUFF AUDIO_BUFF_HI
#define DEFAULT_MOUSE_SENSE 300
#define DEFAULT_MOUSE_INVERT 0
-#define DEFAULT_NICE 1
+#define DEFAULT_VSYNC 1
+#define DEFAULT_NICE 0
#define DEFAULT_FPS 0
#define DEFAULT_SOUND_VOLUME 10
#define DEFAULT_MUSIC_VOLUME 6
void config_save(void);
int config_mode(int, int, int);
+int config_perf(void);
+void config_sync(void);
+void config_swap(void);
+
/*---------------------------------------------------------------------------*/
void config_set_d(int, int);
void part_draw_coin(float rx, float ry)
{
- float r = (float) SDL_GetTicks() / 1000.f;
+ float r = (float) SDL_GetTicks() / 1000.0f;
int i;
glBindTexture(GL_TEXTURE_2D, part_text);
void part_draw_goal(float rx, float ry, float radius, float a)
{
- float r = (float) SDL_GetTicks() / 1000.f;
+ float r = (float) SDL_GetTicks() / 1000.0f;
int i;
glBindTexture(GL_TEXTURE_2D, part_text);
--- /dev/null
+/*
+ * Copyright (C) 2003 Robert Kooima
+ *
+ * NEVERBALL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "glext.h"
+
+/*---------------------------------------------------------------------------*/
+#ifndef __APPLE__
+
+static int search(const char *haystack, const char *needle)
+{
+ const char *c;
+
+ for (; *haystack; haystack++)
+ {
+ for (c = needle; *c && *haystack; c++, haystack++)
+ if (*c != *haystack)
+ break;
+
+ if ((*c == 0) && (*haystack == ' ' || *haystack == '\0'))
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+/*---------------------------------------------------------------------------*/
+#ifdef __linux__
+
+#include <GL/glx.h>
+
+void sync_init(void)
+{
+ Display *dpy = glXGetCurrentDisplay();
+ int scr = DefaultScreen(dpy);
+
+ PFNGLXSWAPINTERVALSGIPROC _glXSwapInvervalSGI = NULL;
+
+ if (search(glXQueryExtensionsString(dpy, scr), "GLX_SGI_swap_control"))
+ {
+ if ((_glXSwapInvervalSGI = (PFNGLXSWAPINTERVALSGIPROC)
+ glXGetProcAddress((const GLubyte *) "glXSwapIntervalSGI")))
+ _glXSwapInvervalSGI(1);
+ }
+}
+
+#endif
+/*---------------------------------------------------------------------------*/
+#ifdef _WIN32
+
+void sync_init(void)
+{
+/* TODO: Sit down at a Windows machine and make this work.
+ PFNWGLSWAPINTERVALEXTPROC _wglSwapInvervalEXT = NULL;
+
+ if (search(wglGetExtensionsString(), "WGL_EXT_swap_control"))
+ {
+ if ((_wglSwapInvervalEXT = (PFNGLXSWAPINTERVALEXTPROC)
+ wglGetProcAddress((const GLubyte *) "wglSwapIntervalEXT")))
+ _wglSwapInvervalEXT(1);
+ }
+*/
+}
+
+#endif
+/*---------------------------------------------------------------------------*/
+#ifdef __APPLE__
+
+#include <OpenGL/OpenGL.h>
+
+void sync_init(void)
+{
+ long swap = 1;
+ CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &swap);
+}
+
+#endif
+/*---------------------------------------------------------------------------*/
--- /dev/null
+/*
+ * Copyright (C) 2003 Robert Kooima
+ *
+ * NEVERBALL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef SYNC_H
+#define SYNC_H
+
+/*---------------------------------------------------------------------------*/
+
+void sync_init(void);
+
+/*---------------------------------------------------------------------------*/
+
+#endif
#------------------------------------------------------------------------------
-DEMOCONV := democonv$(EXT)
-DEMOCONV_CPPFLAGS := \
- -I../ball -I../share $(shell sdl-config --cflags) -Umain
-DEMOCONV_OBJS := ../share/binary.o src/democonv.o
-
-#------------------------------------------------------------------------------
-
-all: $(DEMOCONV)
-
-$(DEMOCONV): $(DEMOCONV_OBJS)
- $(CC) $(CFLAGS) $(DEMOCONV_OBJS) $(LDFLAGS) -o $(DEMOCONV)
-
-$(DEMOCONV): CPPFLAGS := $(DEMOCONV_CPPFLAGS) $(CPPFLAGS)
+all:
clean:
- $(RM) $(DEMOCONV) $(DEMOCONV_OBJS)
#------------------------------------------------------------------------------
+++ /dev/null
-
-The tools subdirectory contains various Neverball-related utilities.
-Currently it only contains the replay converter and a wrapper Windows NT
-command shell script.
-
- * democonv
-
- A bare-bones Neverball 1.4.0 to v4 (Neverball 1.5.0) format replay
- converter. Reads a replay from standard input and writes a
- converted replay to standard output. See below for examples.
-
- democonv recognizes several command line options. Default values
- are used when an option is not present (these are noted below).
- Unrecognized options are silently ignored. Multiple options of the
- same kind may be given, but only the last one will be used. These
- options are recognized:
-
- --help
-
- Request a usage message. A short list of options and their
- meanings is printed to standard error and democonv exits
- with a non-zero exit code.
-
- --goal
- --time-out
- --fall-out
-
- Specify the outcome of the replay. By default none of these
- values is used, resulting in a "status: aborted" message at
- the replay selection screen.
-
- --best-time
- --most-coins
- --freestyle
-
- Specify the type of the record. Although promising, these
- options only influence what game mode to set for the replay.
- The practice mode is used for Best Time replays and the
- normal mode is used for Most Coins replays. A custom value
- is used for Freestyle replays which is also the default when
- none of these options is present. This results in an "mode:
- unknown" message at the replay selection screen.
-
- --player <name>
-
- Specify name of the player. Name of any length may be used
- here, but only the first 8 bytes are meaningful. Using
- characters not in ASCII is discouraged. It will work but it
- won't make sense. The default value is "Player".
-
- --date <datetime>
-
- Specify the date and time of creation. These are treated as
- local date/time and are used to calculate the actual UTC
- time to store in the replay. datetime should be in the
- following format: YYYY-mm-ddTHH:MM:SS. "T" is literally
- the character T. The default value is 2003-07-16T00:00:00
- (UTC). (Trivia: it is the date of posting of Super Empty
- Ball on flipcode.)
-
- A few examples:
-
- $ democonv --goal --most-coins --player parasti \
- --date 2006-10-10T15:30:00 < cE01para > cE01para.nbr
-
- $ wget -O - http://lots.of.replays.org/fM25mym | democonv \
- --fall-out --player mym > fM25mym.nbr
-
- Windows users, note that cmd.exe supports input redirection and
- pipes the same way Unix shells do, so these examples should work in
- Windows NT command shell. Substitute '>' for the prompt sign and
- '^' for the line continuation symbol.
-
- * convert-replays.bat
-
- A convenience wrapper script around democonv for Windows users.
- This script takes a list of files on its command line and saves
- sucessfully converted files to the current working directory.
- Wildcards are supported. Original filenames with the '.nbr'
- extension appended are used for converted files.
-
- convert-replays.bat recognizes all of democonv's command line
- options. In addition, a --prefix option is recognized.
-
- --prefix <pref>
-
- Specify a prefix to use for filenames of converted files.
- Useful if you want to save converted files to a directory
- other than CWD.
-
- Examples:
-
- > convert-replays --player Dave --most-coins c*dav
- > convert-replays --goal --player parasti ^
- --prefix ..\new-replays\ ..\old-replays\*
-
+++ /dev/null
-@echo off\r
-\r
-setlocal enableextensions\r
-\r
- set DCEXEC=democonv\r
- set DCCMD=%DCEXEC%\r
-\r
- if (%1) == () (\r
- :usage\r
- echo Usage: %0 [options] [file-list]\r
- %DCEXEC% --help 2>&1 | more +1\r
- echo --prefix ^<pref^>\r
- echo Add this prefix before filenames of converted\r
- echo replays. Useful for saving replays to a directory\r
- echo other than CWD.\r
-\r
- exit /b 1\r
- )\r
-\r
- rem do .. while ..\r
- :args\r
- if (%1) == (-h) (\r
- goto usage\r
- ) else if (%1) == (--help) (\r
- goto usage\r
- ) else if (%1) == (--goal) (\r
- set DCCMD=%DCCMD% %1\r
- ) else if (%1) == (--fall-out) (\r
- set DCCMD=%DCCMD% %1\r
- ) else if (%1) == (--time-out) (\r
- set DCCMD=%DCCMD% %1\r
- ) else if (%1) == (--best-time) (\r
- set DCCMD=%DCCMD% %1\r
- ) else if (%1) == (--most-coins) (\r
- set DCCMD=%DCCMD% %1\r
- ) else if (%1) == (--freestyle) (\r
- set DCCMD=%DCCMD% %1\r
- ) else if (%1) == (--player) (\r
- set DCCMD=%DCCMD% %1 %2\r
- shift\r
- ) else if (%1) == (--date) (\r
- set DCCMD=%DCCMD% %1 %2\r
- shift\r
- ) else if (%1) == (--prefix) (\r
- set PREFIX=%2\r
- shift\r
- )\r
- shift\r
- if not (%1) == () goto args\r
-\r
- for %%d in (%*) do (\r
- rem This ugly test tries to make sure we're not processing command\r
- rem line options and directories (weird errors pop up).\r
-\r
- if exist %%d if not exist %%dnul (\r
- %DCCMD% < %%d > %PREFIX%%%~nxd.nbr\r
- if errorlevel 1 del %PREFIX%%%~nxd.nbr\r
- )\r
- )\r
- exit /b 0\r
-\r
-endlocal\r
-\r
-rem vim:set sts=4 sw=4 et:\r
+++ /dev/null
-/*
- * democonv -- Neverball 1.4.0 to v4 format replay converter.
- *
- * Copyright (C) 2006 Jānis Rūcis
- *
- * Part of the Neverball project.
- *
- * Copyright (C) 2003 Robert Kooima
- *
- * NEVERBALL is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2 of the License,
- * or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-/*---------------------------------------------------------------------------*/
-
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-
-#ifdef _WIN32
-#include <io.h> /* _setmode() */
-#include <fcntl.h> /* _O_BINARY */
-#endif
-
-#include <SDL_endian.h>
-
-#include "base_config.h"
-#include "binary.h"
-#include "level.h"
-#include "mode.h"
-
-/*----------------------------------------------------------------------------*/
-
-#define DATELEN 20
-
-static void get_short(FILE *fin, short *s)
-{
- unsigned char *p = (unsigned char *) s;
-
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- p[1] = (unsigned char) fgetc(fin);
- p[0] = (unsigned char) fgetc(fin);
-#else
- p[0] = (unsigned char) fgetc(fin);
- p[1] = (unsigned char) fgetc(fin);
-#endif
-}
-
-/*---------------------------------------------------------------------------*/
-
-#define MAG_OLD 0x4E425250
-
-#define MAG_NEW 0x52424EAF
-#define VER_NEW 4
-
-/*---------------------------------------------------------------------------*/
-
-static int read_demo(FILE *fin, int *timer, int *coins,
- int *time, int *goal, int *score, int *balls,
- char *file, char *shot)
-{
- short t, c;
- short zero;
-
- short st, sg, ss, sb;
-
- char none[PATHMAX];
-
- get_short(fin, &t);
- get_short(fin, &c);
-
- fread(shot, 1, PATHMAX, fin);
- fread(file, 1, PATHMAX, fin);
- fread(none, 1, PATHMAX, fin);
- fread(none, 1, PATHMAX, fin);
- fread(none, 1, PATHMAX, fin);
-
- get_short(fin, &st);
- get_short(fin, &sg);
- get_short(fin, &ss);
- get_short(fin, &zero);
- get_short(fin, &sb);
-
- if (!feof(fin))
- {
- *timer = (int) t;
- *coins = (int) c;
-
- *time = (int) st;
- *goal = (int) sg;
- *score = (int) ss;
- *balls = (int) sb;
-
- return 1;
- }
- return 0;
-}
-
-static int parse_args(int argc, char *argv[],
- int *state,
- int *mode,
- char *player,
- char *date)
-{
- int i;
-
- const char *usage =
- "Usage: %s [options]\n"
- "Options:\n"
- " --help Print this message.\n"
- " --goal, --time-out, --fall-out\n"
- " Outcome of the replay. Default: none.\n"
- " --best-time, --most-coins, --freestyle\n"
- " Type of record. Default: unknown.\n"
- " --player <name>\n"
- " Player name. Max 8 characters. Default: \"Player\".\n"
- " --date <datetime>\n"
- " Date/time (local) when the replay was made. Format is\n"
- " YYYY-mm-ddTHH:MM:SS. \"T\" is literally T. Default:\n"
- " \"2003-07-16T00:00:00\" (UTC).\n";
-
- for (i = 1; i < argc; i++)
- {
- if (strcmp(argv[i], "-h") == 0 ||
- strcmp(argv[i], "--help") == 0)
- {
- fprintf(stderr, usage, argv[0]);
- return 0;
- }
-
- else if (strcmp(argv[i], "--goal") == 0)
- *state = GAME_GOAL;
- else if (strcmp(argv[i], "--time-out") == 0)
- *state = GAME_TIME;
- else if (strcmp(argv[i], "--fall-out") == 0)
- *state = GAME_FALL;
-
- else if (strcmp(argv[i], "--best-time") == 0)
- *mode = MODE_PRACTICE;
- else if (strcmp(argv[i], "--most-coins") == 0)
- *mode = MODE_NORMAL;
- else if (strcmp(argv[i], "--freestyle") == 0)
- *mode = 0;
-
- else if (strcmp(argv[i], "--player") == 0)
- strncpy(player, argv[++i], MAXNAM);
- else if (strcmp(argv[i], "--date") == 0)
- {
- struct tm dt;
-
- if (sscanf(argv[++i], "%4d-%2d-%2dT%2d:%2d:%2d",
- &dt.tm_year,
- &dt.tm_mon,
- &dt.tm_mday,
- &dt.tm_hour,
- &dt.tm_min,
- &dt.tm_sec) == 6)
- {
- time_t t;
-
- dt.tm_year -= 1900;
- dt.tm_mon -= 1;
- dt.tm_isdst = -1;
-
- t = mktime(&dt);
-
- strftime(date, DATELEN, "%Y-%m-%dT%H:%M:%S", gmtime(&t));
- }
- else
- {
- fprintf(stderr, "ERROR: incorrect date format. "
- "Try '%s --help'.\n", argv[0]);
- return 0;
- }
- }
- }
- return 1;
-}
-
-/*---------------------------------------------------------------------------*/
-
-int main(int argc, char *argv[])
-{
- FILE *fin, *fout;
-
- int magic;
- int version = VER_NEW;
-
- int state = GAME_NONE;
- int mode = 0;
- char player[MAXNAM] = "Player";
- char date[DATELEN] = "2003-07-16T00:00:00";
-
- if (!parse_args(argc, argv, &state, &mode, player, date))
- return 1;
-
-#ifdef _WIN32
- _setmode(_fileno(stdin), _O_BINARY);
- _setmode(_fileno(stdout), _O_BINARY);
-#endif
-
- fin = stdin;
- fout = stdout;
-
- get_index(fin, &magic);
-
- if (magic == MAG_OLD)
- {
- int timer;
- int coins;
-
- int time, goal, score, balls;
-
- char file[PATHMAX];
- char shot[PATHMAX];
-
- int zero = 0;
-
- if (read_demo(fin, &timer, &coins,
- &time, &goal, &score, &balls,
- file, shot))
- {
- char step[MAXSTR];
- size_t c;
-
- magic = MAG_NEW;
-
- put_index(fout, &magic);
- put_index(fout, &version);
-
- put_index(fout, &timer);
- put_index(fout, &coins);
- put_index(fout, &state);
- put_index(fout, &mode);
-
- put_string(fout, player);
- put_string(fout, date);
-
- put_string(fout, shot);
- put_string(fout, file);
-
- put_index(fout, &time);
- put_index(fout, &goal);
- put_index(fout, &score);
- put_index(fout, &balls);
- put_index(fout, &zero);
-
- while ((c = fread(step, 1, sizeof (step), fin)) > 0)
- fwrite(step, 1, c, fout);
-
- return 0;
- }
- }
- return 1;
-}
-
-/*---------------------------------------------------------------------------*/