Port adjustable replay playback speed from Nuncabola
authorparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Thu, 11 Nov 2010 19:42:58 +0000 (19:42 +0000)
committerparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Thu, 11 Nov 2010 19:42:58 +0000 (19:42 +0000)
git-svn-id: https://s.snth.net/svn/neverball/trunk@3356 78b8d119-cf0a-0410-b17c-f493084dd1d7

Makefile
ball/demo.c
ball/demo.h
ball/game_common.c
ball/game_common.h
ball/hud.c
ball/hud.h
ball/speed.c [new file with mode: 0644]
ball/speed.h [new file with mode: 0644]
ball/st_demo.c

index 88b367c..c80e8e2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -221,6 +221,7 @@ BALL_OBJS := \
        ball/demo.o         \
        ball/demo_dir.o     \
        ball/util.o         \
+       ball/speed.o        \
        ball/st_conf.o      \
        ball/st_demo.o      \
        ball/st_save.o      \
index dee90e8..55a8a4b 100644 (file)
@@ -27,6 +27,7 @@
 #include "level.h"
 #include "array.h"
 #include "dir.h"
+#include "speed.h"
 
 #include "game_server.h"
 #include "game_client.h"
@@ -465,4 +466,18 @@ void demo_replay_stop(int d)
     }
 }
 
+void demo_speed_set(int speed)
+{
+    if (SPEED_NONE <= speed && speed < SPEED_MAX)
+    {
+        /*
+         * I am torn between the desire to fix the division by zero
+         * when speed is SPEED_NONE and the knowledge that, on all
+         * modern architectures, the result will be an infinity, which
+         * seems well suited for our purposes.
+         */
+        lockstep_scl(&update_step, 1.0f / SPEED_FACTORS[speed]);
+    }
+}
+
 /*---------------------------------------------------------------------------*/
index d06a924..839a977 100644 (file)
@@ -64,6 +64,8 @@ void demo_replay_stop(int);
 
 const char *curr_demo(void);
 
+void demo_speed_set(int);
+
 /*---------------------------------------------------------------------------*/
 
 extern fs_file demo_fp;
index 74e7072..c85baf6 100644 (file)
@@ -176,17 +176,29 @@ void game_view_fly(struct game_view *view, const struct s_file *fp, float k)
 void lockstep_clr(struct lockstep *ls)
 {
     ls->at = 0;
+    ls->ts = 1.0f;
 }
 
 void lockstep_run(struct lockstep *ls, float dt)
 {
     ls->at += dt;
 
-    while (ls->at >= ls->dt)
+    while (ls->at >= ls->dt * ls->ts)
     {
         ls->step(ls->dt);
-        ls->at -= ls->dt;
+        ls->at -= ls->dt * ls->ts;
     }
 }
 
+void lockstep_scl(struct lockstep *ls, float ts)
+{
+    /*
+     * Depending on the size of the previous time scale, there may be
+     * a lot of time left in the accumulator.  Mind-blowing hack: just
+     * reset the accumulator.
+     */
+    ls->at = 0;
+    ls->ts = ts;
+}
+
 /*---------------------------------------------------------------------------*/
index 39443bb..103d0e8 100644 (file)
@@ -106,10 +106,12 @@ struct lockstep
 
     float dt;                           /* Time step length                  */
     float at;                           /* Accumulator                       */
+    float ts;                           /* Time scale factor                 */
 };
 
 void lockstep_clr(struct lockstep *);
 void lockstep_run(struct lockstep *, float);
+void lockstep_scl(struct lockstep *, float);
 
 /*---------------------------------------------------------------------------*/
 
index 8f74f19..7a3fbfd 100644 (file)
@@ -23,6 +23,7 @@
 #include "config.h"
 #include "video.h"
 #include "audio.h"
+#include "speed.h"
 
 #include "game_common.h"
 #include "game_client.h"
@@ -40,7 +41,15 @@ static int goal_id;
 static int view_id;
 static int fps_id;
 
+static int speed_id;
+static int speed_ids[SPEED_MAX];
+
+static const char *speed_labels[SPEED_MAX] = {
+    "", "8", "4", "2", "1", "2", "4", "8"
+};
+
 static float view_timer;
+static float speed_timer;
 
 static void hud_fps(void)
 {
@@ -98,6 +107,26 @@ void hud_init(void)
 
     if ((fps_id = gui_count(0, 1000, GUI_SML, GUI_SE)))
         gui_layout(fps_id, -1, 1);
+
+    if ((speed_id = gui_varray(0)))
+    {
+        int i;
+
+        for (i = SPEED_MAX - 1; i > SPEED_NONE; i--)
+        {
+            int rect = 0;
+
+            if (i == SPEED_MAX - 1)
+                rect = GUI_NW;
+            if (i == SPEED_NONE + 1)
+                rect = GUI_SW;
+
+            speed_ids[i] = gui_label(speed_id, speed_labels[i],
+                                     GUI_SML, rect, 0, 0);
+        }
+
+        gui_layout(speed_id, +1, 0);
+    }
 }
 
 void hud_free(void)
@@ -121,6 +150,7 @@ void hud_paint(void)
         gui_paint(fps_id);
 
     hud_view_paint();
+    hud_speed_paint();
 }
 
 void hud_update(int pulse)
@@ -141,6 +171,8 @@ void hud_update(int pulse)
         gui_pulse(ball_id, 0.f);
         gui_pulse(time_id, 0.f);
         gui_pulse(coin_id, 0.f);
+
+        speed_timer = 0.0f;
     }
 
     /* time and tick-tock */
@@ -220,7 +252,6 @@ void hud_update(int pulse)
 
 void hud_timer(float dt)
 {
-
     hud_update(1);
 
     gui_timer(Rhud_id, dt);
@@ -228,6 +259,7 @@ void hud_timer(float dt)
     gui_timer(time_id, dt);
 
     hud_view_timer(dt);
+    hud_speed_timer(dt);
 }
 
 /*---------------------------------------------------------------------------*/
@@ -252,3 +284,41 @@ void hud_view_paint(void)
 }
 
 /*---------------------------------------------------------------------------*/
+
+void hud_speed_pulse(int speed)
+{
+    int i;
+
+    for (i = SPEED_NONE + 1; i < SPEED_MAX; i++)
+    {
+        const GLfloat *c = gui_gry;
+
+        if      (i > SPEED_NORMAL && i <= speed)
+            c = gui_grn;
+        else if (i < SPEED_NORMAL && i >= speed)
+            c = gui_red;
+        else if (i == SPEED_NORMAL)
+            c = gui_wht;
+
+        gui_set_color(speed_ids[i], c, c);
+
+        if (i == speed)
+            gui_pulse(speed_ids[i], 1.2f);
+    }
+
+    speed_timer = 2.0f;
+}
+
+void hud_speed_timer(float dt)
+{
+    speed_timer -= dt;
+    gui_timer(speed_id, dt);
+}
+
+void hud_speed_paint(void)
+{
+    if (speed_timer > 0.0f)
+        gui_paint(speed_id);
+}
+
+/*---------------------------------------------------------------------------*/
index e349961..5cbf262 100644 (file)
@@ -14,6 +14,10 @@ void hud_view_pulse(int);
 void hud_view_timer(float);
 void hud_view_paint();
 
+void hud_speed_pulse(int);
+void hud_speed_timer(float);
+void hud_speed_paint();
+
 /*---------------------------------------------------------------------------*/
 
 #endif
diff --git a/ball/speed.c b/ball/speed.c
new file mode 100644 (file)
index 0000000..172a0bb
--- /dev/null
@@ -0,0 +1,12 @@
+#include "speed.h"
+
+float SPEED_FACTORS[SPEED_MAX] = {
+    0.0f,
+    1.0f / 8,
+    1.0f / 4,
+    1.0f / 2,
+    1.0f,
+    1.0f * 2,
+    1.0f * 4,
+    1.0f * 8
+};
diff --git a/ball/speed.h b/ball/speed.h
new file mode 100644 (file)
index 0000000..c4facb2
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SPEED_H
+#define SPEED_H
+
+enum
+{
+    SPEED_NONE = 0,
+
+    SPEED_SLOWEST,
+    SPEED_SLOWER,
+    SPEED_SLOW,
+    SPEED_NORMAL,
+    SPEED_FAST,
+    SPEED_FASTER,
+    SPEED_FASTEST,
+
+    SPEED_MAX
+};
+
+extern float SPEED_FACTORS[];
+
+#define SPEED_UP(s) MIN((s) + 1, SPEED_MAX - 1)
+#define SPEED_DN(s) MAX((s) - 1, SPEED_NONE)
+
+#endif
index 045c104..a7d7567 100644 (file)
@@ -26,6 +26,7 @@
 #include "util.h"
 #include "common.h"
 #include "demo_dir.h"
+#include "speed.h"
 
 #include "game_common.h"
 #include "game_server.h"
@@ -384,6 +385,7 @@ static int standalone;
 static int demo_paused;
 static int show_hud;
 static int check_compat;
+static int speed;
 
 static float prelude;
 
@@ -431,6 +433,9 @@ static int demo_play_enter(struct state *st, struct state *prev)
 
     prelude = 1.0f;
 
+    speed = SPEED_NORMAL;
+    demo_speed_set(speed);
+
     show_hud = 1;
     hud_update(0);
 
@@ -468,6 +473,38 @@ static void demo_play_timer(int id, float dt)
         progress_step();
 }
 
+static void set_speed(int d)
+{
+    if (d > 0) speed = SPEED_UP(speed);
+    if (d < 0) speed = SPEED_DN(speed);
+
+    demo_speed_set(speed);
+    hud_speed_pulse(speed);
+}
+
+static void demo_play_stick(int id, int a, float v)
+{
+    if (!STICK_BUMP)
+        return;
+
+    if (config_tst_d(CONFIG_JOYSTICK_AXIS_Y, a))
+    {
+        if (v < 0) set_speed(+1);
+        if (v > 0) set_speed(-1);
+    }
+}
+
+static int demo_play_click(int b, int d)
+{
+    if (d)
+    {
+        if (b == SDL_BUTTON_WHEELUP)   set_speed(+1);
+        if (b == SDL_BUTTON_WHEELDOWN) set_speed(-1);
+    }
+
+    return 1;
+}
+
 static int demo_play_keybd(int c, int d)
 {
     if (d)
@@ -737,9 +774,9 @@ struct state st_demo_play = {
     demo_play_paint,
     demo_play_timer,
     NULL,
+    demo_play_stick,
     NULL,
-    NULL,
-    NULL,
+    demo_play_click,
     demo_play_keybd,
     demo_play_buttn,
     1, 0