aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2023-07-08 22:35:16 +0100
committerJuan J. Martinez <jjm@usebox.net>2023-07-08 22:35:16 +0100
commit8871729a65615df0eab213bbbf942abe75771704 (patch)
tree34a4ab0f412c34383799e14816d1dfe288705085
parent5ae0b6490f5fd138f4cbf5c084680bb03b2fff05 (diff)
downloadgold-mine-run-8871729a65615df0eab213bbbf942abe75771704.tar.gz
gold-mine-run-8871729a65615df0eab213bbbf942abe75771704.zip
Avoid drawing the whole screen on each frame
This allows supporting 386DX "just about" (there will be flickering).
-rw-r--r--README.md2
-rw-r--r--data/stage.json67
-rw-r--r--src/Makefile2
-rw-r--r--src/entities.c31
-rw-r--r--src/entities.h7
-rw-r--r--src/game.c54
-rw-r--r--src/map.c15
-rw-r--r--src/menu.c8
-rw-r--r--src/pickup.c6
-rw-r--r--src/player.c48
-rw-r--r--src/sound.c25
-rw-r--r--src/sound.h3
-rw-r--r--src/tmonster.c4
-rw-r--r--src/vga.c59
-rw-r--r--src/vga.h12
15 files changed, 136 insertions, 207 deletions
diff --git a/README.md b/README.md
index 710073d..04599dc 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Running the game:
- MS/DOS or compatible
- A DOS Protected Mode Interface, e.g. [CWSDPMI](http://sandmann.dotster.com/cwsdpmi/)
-- 386DX (with FPU) or better with VGA
+- Plays on a 386DX with VGA, but a 486 or better is highly recommended
- At least 4MB of RAM
- Sound Blaster (or no sound)
diff --git a/data/stage.json b/data/stage.json
index 5758a5b..6d8b6a4 100644
--- a/data/stage.json
+++ b/data/stage.json
@@ -44,34 +44,6 @@
},
{
"height":16,
- "id":2,
- "name":"Bat",
- "rotation":0,
- "type":"",
- "visible":true,
- "width":16,
- "x":240,
- "y":32
- },
- {
- "height":16,
- "id":3,
- "name":"Snake",
- "properties":[
- {
- "name":"dir",
- "type":"string",
- "value":"left"
- }],
- "rotation":0,
- "type":"",
- "visible":true,
- "width":16,
- "x":288,
- "y":128
- },
- {
- "height":16,
"id":4,
"name":"Bat",
"rotation":0,
@@ -94,17 +66,6 @@
},
{
"height":16,
- "id":10,
- "name":"Pickaxe",
- "rotation":0,
- "type":"",
- "visible":true,
- "width":16,
- "x":56,
- "y":104
- },
- {
- "height":16,
"id":13,
"name":"Pickaxe",
"rotation":0,
@@ -116,17 +77,6 @@
},
{
"height":16,
- "id":11,
- "name":"Bonus",
- "rotation":0,
- "type":"",
- "visible":true,
- "width":16,
- "x":80,
- "y":104
- },
- {
- "height":16,
"id":12,
"name":"Bonus",
"rotation":0,
@@ -155,23 +105,6 @@
},
{
"height":16,
- "id":7,
- "name":"Bat",
- "properties":[
- {
- "name":"dir",
- "type":"string",
- "value":"left"
- }],
- "rotation":0,
- "type":"",
- "visible":true,
- "width":16,
- "x":32,
- "y":40
- },
- {
- "height":16,
"id":8,
"name":"Snake",
"rotation":0,
diff --git a/src/Makefile b/src/Makefile
index 4870f10..be1b0e1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -4,7 +4,7 @@ endif
BIN := ../game.exe
CC := i586-pc-msdosdjgpp-gcc
-CFLAGS := -I. -I$(LIBMIKMOD_BASE)/include -g -c -Wall -Werror -pedantic -O2 -fomit-frame-pointer -ffast-math -march=i386 -DDEBUG
+CFLAGS := -I. -I$(LIBMIKMOD_BASE)/include -c -Wall -Werror -pedantic -O3 -fomit-frame-pointer -ffast-math -march=i386 -DDEBUG
LDFLAGS := -s -L$(LIBMIKMOD_BASE)/dos
LIBS := -lmikmod
diff --git a/src/entities.c b/src/entities.c
index df8fb4e..e24c119 100644
--- a/src/entities.c
+++ b/src/entities.c
@@ -8,7 +8,7 @@
#include "effect.h"
-#define MAX_ENTITY 32
+#define MAX_ENTITY 16
static Entity entities[MAX_ENTITY];
static uint8_t last;
@@ -44,31 +44,36 @@ void entities_update()
for (uint8_t i = 0; i < last; i++, e++)
if (e->used)
+ {
+ e->ox = e->x;
+ e->oy = e->y;
e->update(e);
+ }
}
-void entities_erase()
+void entities_draw()
{
- Entity *e = entities + last - 1;
+ Rect dst = { 0, 0, 16, 16 };
+ Entity *e = entities;
- for (uint8_t i = 0; i < last; i++, e--)
- if (e->used)
+ for (uint8_t i = 0; i < last; i++, e++)
+ if (e->erase)
{
- Rect dst = { e->x, e->y + MAP_OFFS_Y, 16, 16 };
- blit(e->bg, &dst);
+ dst.x = e->ox;
+ dst.y = e->oy + MAP_OFFS_Y;
+ blit_copy(&dst);
+ e->erase = 0;
}
-}
-void entities_draw()
-{
- Entity *e = entities;
+ e = entities;
for (uint8_t i = 0; i < last; i++, e++)
if (e->used)
{
- Rect dst = { e->x, e->y + MAP_OFFS_Y, 16, 16 };
- read_buffer(e->bg, &dst);
+ dst.x = e->x;
+ dst.y = e->y + MAP_OFFS_Y;
blitrc(binary_sprites_start, &e->frames[e->dir * 4 + e->frame], &dst);
+ e->erase = 1;
}
}
diff --git a/src/entities.h b/src/entities.h
index 99424de..169e582 100644
--- a/src/entities.h
+++ b/src/entities.h
@@ -11,13 +11,15 @@ typedef struct entity_s
uint8_t used;
uint16_t x;
uint16_t y;
+ uint8_t erase;
+ uint16_t ox;
+ uint16_t oy;
uint8_t dir;
uint8_t frame;
uint8_t delay;
uint8_t gravity;
uint8_t flags;
uint16_t counter;
- uint8_t bg[16 * 16];
/* expected to be 2 directions per 4 frames max; 8 Rect */
const Rect *frames;
void (*update)(struct entity_s *e);
@@ -28,10 +30,7 @@ void entities_init();
Entity *entities_new();
void entities_update();
-
-void entities_erase();
void entities_draw();
-
void entities_warp_out_all();
#endif /* _ENTITIES_H */
diff --git a/src/game.c b/src/game.c
index ba6ee85..67f7739 100644
--- a/src/game.c
+++ b/src/game.c
@@ -91,7 +91,7 @@ static void hud_render()
sprintf(b, "%02d", time);
put_text(172, 4, b, time > 10 || stageclear ? 1 : 15);
if (!gameover && !stageclear && time <= 10)
- sound_queue_efx(EFX_TIME);
+ sound_play_efx(EFX_TIME);
}
if (hud & HUD_STAGE)
@@ -105,6 +105,8 @@ static void hud_render()
static void run_gameover()
{
+ wait_vsync();
+
blit_erase(0);
/* restore the HUD after erasing the screen */
@@ -113,19 +115,14 @@ static void run_gameover()
put_text(124, 90, "GAME OVER", 1);
- wait_vsync();
- blit_update();
-
/* wait some time */
wait_frames(255);
}
static void run_stageclear()
{
- put_text(116, 100, "STAGE CLEAR", 1);
-
wait_vsync();
- blit_update();
+ put_text(116, 100, "STAGE CLEAR", 1);
/* wait some time */
wait_frames(96);
@@ -135,23 +132,24 @@ static void run_stageclear()
add_score(25);
time--;
hud |= HUD_TIME;
- sound_queue_efx(EFX_GOLD);
- hud_render();
wait_frames(4);
- blit_update();
- sound_play_queue();
+
+ wait_vsync();
+ hud_render();
+ sound_play_efx(EFX_GOLD);
}
/* wait some time */
wait_frames(64);
+ wait_vsync();
blit_erase(0);
+
/* restore the HUD after erasing the screen */
hud = HUD_ALL;
- hud_render();
wait_vsync();
- blit_update();
+ hud_render();
wait_frames(32);
}
@@ -173,25 +171,30 @@ next_stage:
tmonster = NULL;
+ blit_target(TARGET_BUFFER);
blit_erase(0);
map_init(binary_stage_start);
- /* render the scene with the READY? text */
map_render();
- entities_draw();
- player_draw();
hud_render();
- put_text(136, 100, "READY?", 1);
wait_vsync();
- blit_update();
+ blit_copy_all();
+
+ blit_target(TARGET_SCREEN);
+ put_text(136, 100, "READY?", 1);
+
+ entities_draw();
+ player_draw();
/* wait some time */
wait_frames(96);
/* erase the READY? text */
- map_render();
+ wait_vsync();
+ blit_copy_all();
+
entities_draw();
player_draw();
@@ -226,10 +229,6 @@ next_stage:
if (pause)
continue;
- /* erase first the last we draw */
- player_erase();
- entities_erase();
-
if (map_is_complete())
{
if (!stageclear)
@@ -237,7 +236,7 @@ next_stage:
timer_stop();
tmonster = NULL;
entities_warp_out_all();
- sound_queue_efx(EFX_WARP);
+ sound_play_efx(EFX_WARP);
stageclear = STAGECLEAR_DELAY;
}
@@ -271,13 +270,12 @@ next_stage:
entities_update();
+ wait_vsync();
+
+ player_erase();
entities_draw();
player_draw();
- wait_vsync();
- blit_update();
- sound_play_queue();
-
if (gameover)
{
gameover--;
diff --git a/src/map.c b/src/map.c
index e5e67d1..5599ae9 100644
--- a/src/map.c
+++ b/src/map.c
@@ -74,8 +74,8 @@ void map_init(const uint8_t map[])
}
#endif
- e->x = ent[1] * MAP_TILE_W;
- e->y = ent[2] * MAP_TILE_H;
+ e->ox = e->x = ent[1] * MAP_TILE_W;
+ e->oy = e->y = ent[2] * MAP_TILE_H;
e->dir = ent[3] & 1;
init[*ent - 1](e);
@@ -98,15 +98,8 @@ void map_render()
src.y = (t / MAP_TILESET_COLS) * MAP_TILE_H;
blitrc(binary_tiles_start, &src, &dst);
- }
-
- for (uint8_t y = 0; y < MAP_H; y++)
- for (uint8_t x = 0; x < MAP_W; x++)
- {
- dst.x = x * MAP_TILE_W;
- dst.y = y * MAP_TILE_H + MAP_OFFS_Y;
- uint8_t t = gold[x + y * MAP_W];
+ t = gold[x + y * MAP_W];
/* not gold, skip! */
if (t == 0xff)
@@ -150,7 +143,9 @@ uint8_t map_update_gold(uint16_t x, uint16_t y)
src.x = (t % MAP_TILESET_COLS) * MAP_TILE_W;
src.y = (t / MAP_TILESET_COLS) * MAP_TILE_H;
+ blit_target(TARGET_BUFFER);
blitrc(binary_tiles_start, &src, &dst);
+ blit_target(TARGET_SCREEN);
total_gold--;
return 1;
diff --git a/src/menu.c b/src/menu.c
index 4eeed8e..3d4456e 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -63,7 +63,7 @@ static void render_cast()
put_text(dst.x + 16 + 8, dst.y + 6, names[i], i + 2);
wait_vsync();
- blit_update();
+ blit_copy_all();
wait_frames(32);
@@ -77,6 +77,7 @@ uint8_t run_menu()
uint8_t cast = 0;
uint8_t wait;
+ blit_target(TARGET_BUFFER);
while (1)
{
if (keys[KEY_ESC])
@@ -100,7 +101,7 @@ uint8_t run_menu()
}
wait_vsync();
- blit_update();
+ blit_copy_all();
}
/* if is not cast, we do the "blink" effect */
@@ -113,8 +114,9 @@ uint8_t run_menu()
wait = 0;
put_text(84, 110, "PRESS SPACE TO PLAY", wait ? 0 : 1);
+
wait_vsync();
- blit_update();
+ blit_copy_all();
}
}
diff --git a/src/pickup.c b/src/pickup.c
index 91e44b2..7036891 100644
--- a/src/pickup.c
+++ b/src/pickup.c
@@ -136,7 +136,7 @@ void pickup_wait_update(Entity *e)
if (e->counter-- == 0)
{
e->update = pickup_in_update;
- sound_queue_efx(EFX_WARP);
+ sound_play_efx(EFX_WARP);
}
}
@@ -174,7 +174,7 @@ void pickup_update(Entity *e)
if (e->counter++ == MAX_TTL)
{
effect_out_init(e);
- sound_queue_efx(EFX_WARP);
+ sound_play_efx(EFX_WARP);
return;
}
@@ -208,6 +208,6 @@ void pickup_update(Entity *e)
break;
}
e->used = 0;
- sound_queue_efx(EFX_PICKUP);
+ sound_play_efx(EFX_PICKUP);
}
}
diff --git a/src/player.c b/src/player.c
index 8c1765a..399dacf 100644
--- a/src/player.c
+++ b/src/player.c
@@ -24,20 +24,18 @@
#define IS_NOT_GOING_UP(x) (gravity == GRAVITY_OFF || (x)>=GRAVITY_DOWN)
-static uint16_t x, y;
+static uint16_t x, y, ox, oy, erase;
static uint8_t dir, frame, delay, gravity, jump, momentum;
static uint16_t respawn_x, respawn_y;
static uint8_t respawn_dir;
-static uint8_t invuln, dying;
+static uint8_t invuln, dying, dead;
const uint8_t gravity_seq[GRAVITY_SEQ_LEN] = {
6, 4, 4, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 4
};
-static uint8_t bg[256];
-
static const Rect frames[2][6] =
{
{
@@ -77,9 +75,11 @@ void player_init(uint16_t start_x, uint8_t start_y, uint8_t start_dir)
momentum = 0;
invuln = 0;
dying = 0;
+ dead = 0;
+ erase = 0;
- respawn_x = x = start_x;
- respawn_y = y = start_y;
+ respawn_x = ox = x = start_x;
+ respawn_y = oy = y = start_y;
gravity = GRAVITY_OFF;
}
@@ -97,17 +97,20 @@ static void player_dying()
/* exit screen */
if (y == MAP_H * MAP_TILE_H - 16)
{
+ /* oh, well */
+ player_erase();
+
/* if there are lifes left, respawn */
if (dec_lives())
{
player_init(respawn_x, respawn_y, respawn_dir);
invuln = INVULN_TIME;
}
+ else
+ dead = 1;
/* stop falling */
gravity = GRAVITY_OFF;
- /* don't draw the player; XXX: magic number */
- invuln = 4;
return;
}
y++;
@@ -131,6 +134,9 @@ void player_update()
{
uint8_t moved = 0;
+ ox = x;
+ oy = y;
+
if (dying)
{
player_dying();
@@ -160,7 +166,7 @@ void player_update()
momentum = 0;
frame = FRAME_JUMPING;
gravity = GRAVITY_UP;
- sound_queue_efx(EFX_JUMP);
+ sound_play_efx(EFX_JUMP);
}
}
else
@@ -273,26 +279,30 @@ void player_update()
if (map_update_gold(x + (dir == DIR_LEFT ? 7 : 8), y + 15))
{
add_score(10);
- sound_queue_efx(EFX_GOLD);
+ sound_play_efx(EFX_GOLD);
}
}
void player_erase()
{
- Rect dst = { x, y + MAP_OFFS_Y, 16, 16 };
- blit(bg, &dst);
+ if (!erase)
+ return;
+
+ Rect dst = { ox, oy + MAP_OFFS_Y, 16, 16 };
+ blit_copy(&dst);
+
+ erase = 0;
}
void player_draw()
{
- Rect dst = { x, y + MAP_OFFS_Y, 16, 16 };
-
- read_buffer(bg, &dst);
-
- if (invuln && (invuln & 4))
+ if (dead || (invuln && (invuln & 4)))
return;
+ Rect dst = { x, y + MAP_OFFS_Y, 16, 16 };
blitrc(binary_sprites_start, &frames[dir][frame], &dst);
+
+ erase = 1;
}
uint8_t player_collision(Entity *e)
@@ -320,14 +330,14 @@ void player_hit()
if (use_pickaxe())
{
invuln = INVULN_TIME;
- sound_queue_efx(EFX_HIT);
+ sound_play_efx(EFX_HIT);
}
else
{
dying = 1;
frame = FRAME_DYING;
gravity = GRAVITY_UP;
- sound_queue_efx(EFX_DEATH);
+ sound_play_efx(EFX_DEATH);
}
}
diff --git a/src/sound.c b/src/sound.c
index 871fcaa..c9e3b1c 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -30,11 +30,6 @@ static uint32_t voice = 0;
static MODULE *music = NULL;
-#define MAX_QUEUE 4
-
-static uint8_t queue[MAX_QUEUE];
-static uint8_t queue_top;
-
uint8_t sound_init(uint8_t nosound)
{
MikMod_InitThreads();
@@ -71,8 +66,6 @@ uint8_t sound_init(uint8_t nosound)
Player_Start(music);
MikMod_EnableOutput();
- queue_top = 0;
-
return 1;
}
@@ -126,21 +119,3 @@ void sound_play_efx(uint8_t efxno)
cur = efxno;
voice = Sample_Play(efx[cur].s, 0, 0);
}
-
-void sound_queue_efx(uint8_t efxno)
-{
- if (queue_top >= MAX_QUEUE)
- return;
-
- queue[queue_top++] = efxno;
-}
-
-void sound_play_queue()
-{
- while (queue_top > 0)
- sound_play_efx(queue[--queue_top]);
-
- disable();
- MikMod_Update();
- enable();
-}
diff --git a/src/sound.h b/src/sound.h
index cd7c20c..d8b14ac 100644
--- a/src/sound.h
+++ b/src/sound.h
@@ -18,9 +18,6 @@ void sound_update();
void sound_music_pattern(uint16_t pat);
void sound_play_efx(uint8_t efxno);
-void sound_queue_efx(uint8_t efxno);
-void sound_play_queue();
-
void sound_mute();
void sound_unmute();
diff --git a/src/tmonster.c b/src/tmonster.c
index 30d5714..9f3c618 100644
--- a/src/tmonster.c
+++ b/src/tmonster.c
@@ -34,8 +34,8 @@ static const Rect frames[2 * 4] =
void tmonster_init(Entity *e)
{
- e->y = 16;
- e->x = 32 + (rand() % 256);
+ e->ox = e->y = 16;
+ e->oy = e->x = 32 + (rand() % 256);
e->dir = e->x > 160 ? DIR_LEFT : DIR_RIGHT;
e->frames = (const Rect *)frames;
e->update = tmonster_wait_update;
diff --git a/src/vga.c b/src/vga.c
index ec56b60..13013b2 100644
--- a/src/vga.c
+++ b/src/vga.c
@@ -10,16 +10,22 @@
static uint8_t buffer[320 * 200];
static uint8_t *screen = NULL;
+static uint8_t *target = NULL;
uint8_t open_framebuffer()
{
if (__djgpp_nearptr_enable() == 0)
return 0;
- screen = (uint8_t *)(0xa0000 + __djgpp_conventional_base);
+ target = screen = (uint8_t *)(0xa0000 + __djgpp_conventional_base);
return 1;
}
+void blit_target(uint8_t t)
+{
+ target = t ? buffer : screen;
+}
+
void close_framebuffer()
{
__djgpp_nearptr_disable();
@@ -91,94 +97,97 @@ void set_palette(const uint8_t *palette)
void blit(const uint8_t *sprite, const Rect *dst)
{
- uint8_t *dbuffer = buffer + dst->x + dst->y * 320;
+ uint8_t b;
+ uint8_t *dtarget = target + dst->x + dst->y * 320;
for (uint16_t j = dst->h; j > 0; j--)
{
for (uint16_t i = dst->w; i > 0; i--)
{
- uint8_t b = *sprite++;
+ b = *sprite++;
/* transparent */
if (b == TRANSPARENT)
{
- dbuffer++;
+ dtarget++;
continue;
}
- *dbuffer++ = b;
+ *dtarget++ = b;
}
- dbuffer += 320 - dst->w;
+ dtarget += 320 - dst->w;
}
}
void blit_c(const uint8_t *sprite, const Rect *dst, uint8_t c)
{
- uint8_t *dbuffer = buffer + dst->x + dst->y * 320;
+ uint8_t b;
+ uint8_t *dtarget = target + dst->x + dst->y * 320;
for (uint16_t j = dst->h; j > 0; j--)
{
for (uint16_t i = dst->w; i > 0; i--)
{
- uint8_t b = *sprite++;
+ b = *sprite++;
/* transparent */
if (b == TRANSPARENT)
{
- dbuffer++;
+ dtarget++;
continue;
}
- *dbuffer++ = b ? c : b;
+ *dtarget++ = b ? c : b;
}
- dbuffer += 320 - dst->w;
+ dtarget += 320 - dst->w;
}
}
void blitrc(const uint8_t *sprite, const Rect *src, const Rect *dst)
{
- uint8_t *dbuffer = buffer + dst->x + dst->y * 320;
+ uint8_t b;
+ uint8_t *dtarget = target + dst->x + dst->y * 320;
sprite += src->x + src->y * src->w;
for (uint16_t j = dst->h; j > 0; j--)
{
for (uint16_t i = dst->w; i > 0; i--)
{
- uint8_t b = *sprite++;
+ b = *sprite++;
/* transparent */
if (b == TRANSPARENT)
{
- dbuffer++;
+ dtarget++;
continue;
}
- *dbuffer++ = b;
+ *dtarget++ = b;
}
sprite += src->w - dst->w;
- dbuffer += 320 - dst->w;
+ dtarget += 320 - dst->w;
}
}
void blit_erase(uint8_t c)
{
- memset(buffer, c, 320 * 200);
+ memset(target, c, 320 * 200);
}
-void blit_update()
+void blit_copy_all()
{
memcpy(screen, buffer, 320 * 200);
}
-void read_buffer(uint8_t *dst, const Rect *src)
+void blit_copy(const Rect *dst)
{
- uint8_t *s = buffer + src->y * 320 + src->x;
+ uint8_t *src = buffer + dst->x + dst->y * 320;
+ uint8_t *dtarget = screen + dst->x + dst->y * 320;
- for (uint16_t j = src->h; j > 0; j--)
+ for (uint16_t j = dst->h; j > 0; j--)
{
- for (uint16_t i = src->w; i > 0; i--)
- *dst++ = *s++;
-
- s += 320 - src->w;
+ memcpy(dtarget, src, dst->w);
+ src += 320;
+ dtarget += 320;
}
}
diff --git a/src/vga.h b/src/vga.h
index 06a69df..2441e24 100644
--- a/src/vga.h
+++ b/src/vga.h
@@ -21,14 +21,20 @@ void wait_frames(uint16_t frames);
/* the palette is expected to be 8 bit per color, and will be converted to VGA's 6 bit per color */
void set_palette(const uint8_t *palette);
+#define TARGET_SCREEN 0
+#define TARGET_BUFFER 1
+
+void blit_target(uint8_t t);
+
void blit(const uint8_t *sprite, const Rect *dst);
/* used for text: skip transparent, ignore 0, replace any other color by c */
void blit_c(const uint8_t *sprite, const Rect *dst, uint8_t c);
/* in src w is sprite width, h is sprite height */
void blitrc(const uint8_t *sprite, const Rect *src, const Rect *dst);
void blit_erase(uint8_t c);
-void blit_update();
-/* read from back buffer into memory */
-void read_buffer(uint8_t *dst, const Rect *src);
+/* copy from back buffer to screen */
+void blit_copy_all();
+void blit_copy(const Rect *dst);
+
#endif /* _VGA_H */