dos-vga-arena-shooter-game/game.c

373 lines
7.6 KiB
C
Raw Normal View History

#include <stddef.h>
2024-02-15 13:34:50 +00:00
#include <stdio.h>
2024-02-20 11:48:12 +00:00
#include <math.h>
#include <conio.h>
2024-02-21 21:25:00 +00:00
#include <stdlib.h>
#include <time.h>
2024-02-15 13:34:50 +00:00
2024-02-24 12:36:18 +00:00
#include "sprites.h"
2024-02-20 13:00:13 +00:00
#include "system/vga.h"
#include "system/keyboard.h"
#include "system/mouse_io.h"
#include "system/pc_stuff.h"
2024-02-21 13:25:55 +00:00
2024-02-15 13:34:50 +00:00
#include "bmpload.h"
2024-02-20 17:51:59 +00:00
#include "const.h"
#include "arena.h"
2024-02-21 13:25:55 +00:00
#include "movement.h"
#include "combat.h"
// TODO: centralize these outside of game.c
struct CompiledSpriteRender rabbit, mouse, bullet, enemy;
struct SpriteBounds bounds;
2024-02-15 01:15:55 +00:00
2024-02-15 13:34:50 +00:00
struct BMPImage spritesheetImage;
struct VGAColor vgaColors[256];
struct MouseStatus mouseStatus;
2024-02-21 13:25:55 +00:00
struct RabbitPosition rabbitPosition = {
.rabbitPosition = { 60, 60 },
.rabbitLimits = { { 20, 20 }, { 180, 180 } },
.mousePosition = { 0, 0 },
.rabbitVelocity = { 0, 0 },
.mouseDotPosition = { 0, 0 }
};
2024-02-21 21:25:00 +00:00
struct EnemyPosition enemyPosition[ENEMY_MAX_COUNT];
2024-02-21 13:25:55 +00:00
struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT];
struct RabbitWeaponry rabbitWeaponry;
struct SpawnPointRange {
int left, width;
int top, height;
};
struct SpawnPointRange spawnPointRanges[1] = {
{ .left = 130, .width = 50, .top = 50, .height = 100 }
};
2024-02-21 13:25:55 +00:00
void setupRabbitBullets() {
2024-02-21 21:25:00 +00:00
int i;
2024-02-21 13:25:55 +00:00
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
rabbitBulletPosition[i].isActive = 0;
rabbitBulletPosition[i].willBeInactive = 0;
}
rabbitWeaponry.cooldown = 0;
}
2024-02-21 21:25:00 +00:00
void setupEnemies() {
int i;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
enemyPosition[i].isActive = 0;
enemyPosition[i].willBeInactive = 0;
}
}
void maybeSpawnEnemy() {
char canSpawn;
int i, availableEnemy;
int spawnX, spawnY;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
if (!enemyPosition[i].isActive) {
availableEnemy = i;
canSpawn = 1;
break;
}
}
if (!canSpawn) return;
spawnX = spawnPointRanges[0].left + rand() % spawnPointRanges[0].width;
spawnY = spawnPointRanges[0].top + rand() % spawnPointRanges[0].height;
enemyPosition[availableEnemy].isActive = 1;
enemyPosition[availableEnemy].enemyPosition[0] = spawnX;
enemyPosition[availableEnemy].enemyPosition[1] = spawnY;
enemyPosition[availableEnemy].oldEnemyPosition[0] = spawnX;
enemyPosition[availableEnemy].oldEnemyPosition[1] = spawnY;
2024-02-21 21:25:00 +00:00
}
void setupEnemySprites() {
buildCompiledSprite(
&sprite_enemy,
2024-02-21 21:25:00 +00:00
&enemy,
SPRITE_ENEMY_WIDTH,
SPRITE_ENEMY_HEIGHT,
SPRITE_ENEMY_OFFSET_X,
SPRITE_ENEMY_OFFSET_Y
2024-02-21 21:25:00 +00:00
);
}
2024-02-16 02:18:15 +00:00
void setupRabbitSprites() {
buildCompiledSprite(
&sprite_rabbit,
2024-02-16 02:18:15 +00:00
&rabbit,
SPRITE_RABBIT_WIDTH,
SPRITE_RABBIT_HEIGHT,
SPRITE_RABBIT_OFFSET_X,
SPRITE_RABBIT_OFFSET_Y
2024-02-16 02:18:15 +00:00
);
2024-02-20 11:48:12 +00:00
buildCompiledSprite(
&sprite_mouse,
2024-02-20 11:48:12 +00:00
&mouse,
SPRITE_MOUSE_WIDTH,
SPRITE_MOUSE_HEIGHT,
SPRITE_MOUSE_OFFSET_X,
SPRITE_MOUSE_OFFSET_Y
2024-02-20 11:48:12 +00:00
);
buildCompiledSprite(
&sprite_bullet,
2024-02-21 13:25:55 +00:00
&bullet,
SPRITE_BULLET_WIDTH,
SPRITE_BULLET_HEIGHT,
SPRITE_BULLET_OFFSET_X,
SPRITE_BULLET_OFFSET_Y
2024-02-21 13:25:55 +00:00
);
}
2024-02-16 02:18:15 +00:00
2024-02-20 17:39:28 +00:00
void renderMouse() {
2024-02-21 13:25:55 +00:00
mouse.x = rabbitPosition.mousePosition[0];
mouse.y = rabbitPosition.mousePosition[1];
2024-02-20 11:48:12 +00:00
drawCompiledSprite(&mouse);
2024-02-21 13:25:55 +00:00
drawPixel(rabbitPosition.mouseDotPosition[0], rabbitPosition.mouseDotPosition[1], 2);
2024-02-20 11:48:12 +00:00
}
void renderRabbit() {
2024-02-21 13:25:55 +00:00
rabbit.x = rabbitPosition.rabbitPosition[0];
rabbit.y = rabbitPosition.rabbitPosition[1];
2024-02-20 17:39:28 +00:00
drawCompiledSprite(&rabbit);
2024-02-16 02:18:15 +00:00
}
2024-02-21 21:25:00 +00:00
void renderEnemies() {
int i;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
if (!enemyPosition[i].isActive) continue;
enemy.x = enemyPosition[i].enemyPosition[0];
enemy.y = enemyPosition[i].enemyPosition[1];
drawCompiledSprite(&enemy);
2024-02-21 21:25:00 +00:00
}
}
2024-02-21 13:25:55 +00:00
void renderRabbitBullets() {
char i;
2024-02-16 02:18:15 +00:00
2024-02-21 13:25:55 +00:00
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
if (!rabbitBulletPosition[i].isActive) continue;
2024-02-16 02:18:15 +00:00
2024-02-21 13:25:55 +00:00
bullet.x = rabbitBulletPosition[i].x;
bullet.y = rabbitBulletPosition[i].y;
2024-02-16 02:18:15 +00:00
drawCompiledSprite(&bullet);
2024-02-16 02:18:15 +00:00
}
}
2024-02-20 11:48:12 +00:00
void drawOnlyMouseArena() {
2024-02-21 13:25:55 +00:00
mouse.x = rabbitPosition.oldMousePosition[0];
mouse.y = rabbitPosition.oldMousePosition[1];
2024-02-20 11:48:12 +00:00
getSpriteBounds(&mouse, &bounds);
2024-02-20 17:51:59 +00:00
drawOnlyArena(&bounds);
2024-02-20 17:39:28 +00:00
2024-02-21 13:25:55 +00:00
bounds.top = rabbitPosition.oldMouseDotPosition[1];
bounds.bottom = rabbitPosition.oldMouseDotPosition[1];
bounds.left = rabbitPosition.oldMouseDotPosition[0];
bounds.right = rabbitPosition.oldMouseDotPosition[0];
2024-02-20 17:51:59 +00:00
drawOnlyArena(&bounds);
2024-02-20 11:48:12 +00:00
}
void drawOnlyRabbitArena() {
2024-02-21 13:25:55 +00:00
rabbit.x = rabbitPosition.oldRabbitPosition[0];
rabbit.y = rabbitPosition.oldRabbitPosition[1];
2024-02-20 11:48:12 +00:00
getSpriteBounds(&rabbit, &bounds);
2024-02-20 17:51:59 +00:00
drawOnlyArena(&bounds);
2024-02-20 11:48:12 +00:00
}
2024-02-21 21:25:00 +00:00
void drawOnlyEnemiesArena() {
int i;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
if (!enemyPosition[i].isActive) continue;
enemy.x = enemyPosition[i].oldEnemyPosition[0];
enemy.y = enemyPosition[i].oldEnemyPosition[1];
getSpriteBounds(&enemy, &bounds);
drawOnlyArena(&bounds);
if (enemyPosition[i].willBeInactive) {
enemyPosition[i].isActive = 0;
2024-02-21 21:25:00 +00:00
}
}
}
2024-02-21 13:25:55 +00:00
void drawOnlyRabbitBulletArena() {
int i;
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
if (!rabbitBulletPosition[i].isActive) continue;
bullet.x = rabbitBulletPosition[i].oldX;
bullet.y = rabbitBulletPosition[i].oldY;
getSpriteBounds(&bullet, &bounds);
drawOnlyArena(&bounds);
if (rabbitBulletPosition[i].willBeInactive) {
rabbitBulletPosition[i].isActive = 0;
}
}
}
2024-02-21 21:25:00 +00:00
int setupGame() {
2024-02-15 13:34:50 +00:00
FILE *fh;
2024-02-15 01:15:55 +00:00
installKeyboardHandler();
2024-02-15 13:34:50 +00:00
initializeDrawBuffer();
fh = fopen("sprtsht.bmp", "rb");
if (readBMPIntoNewMemory(fh, &spritesheetImage)) return 1;
fclose(fh);
spritesheetImage.transparentColor = 0;
setupWallSprites();
2024-02-16 02:18:15 +00:00
setupRabbitSprites();
2024-02-21 13:25:55 +00:00
setupRabbitBullets();
2024-02-21 21:25:00 +00:00
setupEnemySprites();
2024-02-15 13:34:50 +00:00
setVideoMode(VIDEO_MODE_VGA_256);
bmp256ColorPaletteToVGAColorPalette(&spritesheetImage, vgaColors);
setVGAColors(vgaColors, 256);
2024-02-15 01:15:55 +00:00
2024-02-20 11:48:12 +00:00
activateMouse(&mouseStatus);
2024-02-16 02:18:15 +00:00
buildArena();
2024-02-21 13:25:55 +00:00
clearArenaRedrawRequests();
2024-02-15 13:34:50 +00:00
2024-02-21 21:25:00 +00:00
srand(time(NULL));
return 0;
}
void handleMovement() {
handleRabbitMovement(
&rabbitPosition,
&keyboardKeydownState
);
captureAndLimitMousePosition(
&rabbitPosition,
&mouseStatus
);
calculateTargetAngle(
&rabbitPosition,
&mouseStatus
);
}
void handleCombat() {
if (mouseStatus.leftButtonDown) {
attemptToFireRabbitBullet(
&rabbitPosition,
&rabbitWeaponry,
rabbitBulletPosition
);
}
advanceRabbitBullets(
rabbitBulletPosition,
&rabbitWeaponry
);
2024-02-25 20:48:19 +00:00
buildCollisionGrids(
rabbitBulletPosition,
&rabbitPosition,
enemyPosition
);
handleRabbitToEnemyCollisions(
&rabbitPosition,
enemyPosition
);
handleRabbitBulletToEnemyCollisions(
rabbitBulletPosition,
enemyPosition
);
}
void handleRedraw() {
drawOnlyRabbitArena();
drawOnlyEnemiesArena();
drawOnlyMouseArena();
drawOnlyRabbitBulletArena();
redrawArena();
renderRabbit();
renderEnemies();
renderMouse();
renderRabbitBullets();
}
/*
2024-02-21 21:25:00 +00:00
double speedCalcs[200];
int currentSpeedCalc = 0;
double averageSpeedCalc;
clock_t startTime;
*/
2024-02-21 21:25:00 +00:00
int main(void) {
2024-02-24 12:36:18 +00:00
byte *drawBuffer;
2024-02-21 21:25:00 +00:00
int keepRunning = 1;
int i;
2024-02-21 21:25:00 +00:00
if (setupGame()) return 1;
2024-02-24 12:36:18 +00:00
drawBuffer = getDrawBuffer();
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
maybeSpawnEnemy();
}
2024-02-21 21:25:00 +00:00
2024-02-16 02:18:15 +00:00
while (keepRunning) {
readMouse(&mouseStatus);
populateKeyboardKeydownState();
2024-02-21 13:25:55 +00:00
handleMovement();
handleRedraw();
2024-02-25 20:48:19 +00:00
handleCombat();
2024-02-20 13:20:35 +00:00
waitStartVbl();
2024-02-15 13:34:50 +00:00
copyDrawBufferToDisplay();
waitEndVbl();
2024-02-16 02:18:15 +00:00
2024-02-20 17:39:28 +00:00
if (keyboardKeydownState.KEY_ESC) { keepRunning = 0; }
2024-02-15 01:15:55 +00:00
}
2024-02-16 02:18:15 +00:00
freeBMP(&spritesheetImage);
setVideoMode(VIDEO_MODE_80x25_TEXT);
uninstallKeyboardHandler();
/*
2024-02-21 21:25:00 +00:00
averageSpeedCalc = 0;
for (currentSpeedCalc = 0; currentSpeedCalc < 200; ++currentSpeedCalc) {
averageSpeedCalc += speedCalcs[currentSpeedCalc];
}
averageSpeedCalc /= 200;
fprintf(stderr, "average: %f\n", averageSpeedCalc);
*/
2024-02-21 13:25:55 +00:00
2024-02-15 13:34:50 +00:00
return 0;
2024-02-15 01:15:55 +00:00
}