From 8871729a65615df0eab213bbbf942abe75771704 Mon Sep 17 00:00:00 2001 From: "Juan J. Martinez" Date: Sat, 8 Jul 2023 22:35:16 +0100 Subject: Avoid drawing the whole screen on each frame This allows supporting 386DX "just about" (there will be flickering). --- README.md | 2 +- data/stage.json | 67 --------------------------------------------------------- src/Makefile | 2 +- src/entities.c | 31 +++++++++++++++----------- src/entities.h | 7 +++--- src/game.c | 54 ++++++++++++++++++++++------------------------ src/map.c | 15 +++++-------- src/menu.c | 8 ++++--- src/pickup.c | 6 +++--- src/player.c | 48 +++++++++++++++++++++++++---------------- src/sound.c | 25 --------------------- src/sound.h | 3 --- src/tmonster.c | 4 ++-- src/vga.c | 59 +++++++++++++++++++++++++++++--------------------- src/vga.h | 12 ++++++++--- 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 @@ -42,34 +42,6 @@ "x":16, "y":128 }, - { - "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, @@ -92,17 +64,6 @@ "x":80, "y":128 }, - { - "height":16, - "id":10, - "name":"Pickaxe", - "rotation":0, - "type":"", - "visible":true, - "width":16, - "x":56, - "y":104 - }, { "height":16, "id":13, @@ -114,17 +75,6 @@ "x":120, "y":152 }, - { - "height":16, - "id":11, - "name":"Bonus", - "rotation":0, - "type":"", - "visible":true, - "width":16, - "x":80, - "y":104 - }, { "height":16, "id":12, @@ -153,23 +103,6 @@ "x":192, "y":152 }, - { - "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, 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 */ -- cgit v1.2.3