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

575 lines
12 KiB
C

#include <stddef.h>
#include <stdio.h>
#include <math.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>
#include "sprites.h"
#include "system/vga.h"
#include "system/keyboard.h"
#include "system/mouse_io.h"
#include "system/pc_stuff.h"
#include "bmpload.h"
#include "const.h"
#include "arena.h"
#include "movement.h"
#include "combat.h"
#include "game.h"
#include "spawn.h"
// TODO: centralize these outside of game.c
struct CompiledSpriteRender rabbit,
mouse,
bullet,
enemy,
enemyBullet,
shotgun,
shieldKiller;
struct SpriteBounds bounds;
struct BMPImage spritesheetImage;
struct VGAColor vgaColors[256];
struct MouseStatus mouseStatus;
struct RabbitPosition rabbitPosition = {
.rabbitPosition = { 60, 60 },
.rabbitLimits = {
{ 20, 20 },
{ (ARENA_WIDTH_TILES - 1) * TILE_SIZE - 2, (ARENA_HEIGHT_TILES - 1) * TILE_SIZE - 2 }
},
.mousePosition = { 0, 0 },
.rabbitVelocity = { 0, 0 },
.mouseDotPosition = { 0, 0 }
};
struct EnemyPosition enemyPosition[ENEMY_MAX_COUNT];
struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT];
struct BulletPosition enemyBulletPosition[ENEMY_BULLET_LIMIT];
struct RabbitWeaponry rabbitWeaponry;
struct PlayerPowerup playerPowerup;
void setupRabbitBullets() {
int i;
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
rabbitBulletPosition[i].isActive = 0;
rabbitBulletPosition[i].willBeInactive = 0;
}
rabbitWeaponry.cooldown = 0;
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
rabbitWeaponry.currentWeaponRemainingRounds = 0;
}
void setupPowerup() {
playerPowerup.x = 100;
playerPowerup.y = 100;
playerPowerup.cooldown = 20 + rand() % 10;
playerPowerup.type = POWERUP_TYPE_SHOTGUN;
playerPowerup.isActive = 0;
}
void setupEnemyBullets() {
int i;
for (i = 0; i < ENEMY_BULLET_LIMIT; ++i) {
enemyBulletPosition[i].isActive = 0;
enemyBulletPosition[i].willBeInactive = 0;
}
}
void setupEnemies() {
int i;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
enemyPosition[i].isActive = 0;
enemyPosition[i].willBeInactive = 0;
}
}
struct GlobalGameState globalGameState = {
.spawnCooldown = 0,
.difficulty = 0
};
int kills = 0;
int health = RABBIT_HEALTH_MAX;
int difficultyBands[10] = { 10, 20, 30, 50, 80, 130, 210, 340, 550, 890 };
void handleEnemyKills() {
int i, hadKill, currentKillCount;
currentKillCount = 0;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
if (enemyPosition[i].wasKilled) {
enemyPosition[i].wasKilled = 0;
kills++;
currentKillCount++;
health += ENEMY_KILL_HEALTH_GAIN;
if (health > RABBIT_HEALTH_MAX) health = RABBIT_HEALTH_MAX;
hadKill = 1;
}
}
kills += currentKillCount;
playerPowerup.cooldown -= currentKillCount;
if (playerPowerup.cooldown <= 0) {
playerPowerup.x = TILE_SIZE + rand() % ((ARENA_WIDTH_TILES - 2) * TILE_SIZE);
playerPowerup.y = TILE_SIZE + rand() % ((ARENA_HEIGHT_TILES - 2) * TILE_SIZE);
playerPowerup.isActive = 1;
playerPowerup.cooldown = POWERUP_RESPAWN_COOLDOWN_PER_LEVEL * globalGameState.difficulty +
rand() % (POWERUP_RESPAWN_COOLDOWN_PER_LEVEL * globalGameState.difficulty);
}
if (hadKill) {
for (i = 0; i < 10; ++i) {
if (kills > difficultyBands[i]) {
globalGameState.difficulty = i + 1;
}
}
}
}
void setupEnemySprites() {
buildCompiledSprite(
&sprite_enemy,
&enemy,
SPRITE_ENEMY_WIDTH,
SPRITE_ENEMY_HEIGHT,
SPRITE_ENEMY_OFFSET_X,
SPRITE_ENEMY_OFFSET_Y
);
buildCompiledSprite(
&sprite_bullet,
&enemyBullet,
SPRITE_BULLET_WIDTH,
SPRITE_BULLET_HEIGHT,
SPRITE_BULLET_OFFSET_X,
SPRITE_BULLET_OFFSET_Y
);
}
void setupRabbitSprites() {
buildCompiledSprite(
&sprite_rabbit,
&rabbit,
SPRITE_RABBIT_WIDTH,
SPRITE_RABBIT_HEIGHT,
SPRITE_RABBIT_OFFSET_X,
SPRITE_RABBIT_OFFSET_Y
);
buildCompiledSprite(
&sprite_mouse,
&mouse,
SPRITE_MOUSE_WIDTH,
SPRITE_MOUSE_HEIGHT,
SPRITE_MOUSE_OFFSET_X,
SPRITE_MOUSE_OFFSET_Y
);
buildCompiledSprite(
&sprite_bullet,
&bullet,
SPRITE_BULLET_WIDTH,
SPRITE_BULLET_HEIGHT,
SPRITE_BULLET_OFFSET_X,
SPRITE_BULLET_OFFSET_Y
);
}
void setupPowerupSprites() {
buildCompiledSprite(
&sprite_shotgun,
&shotgun,
SPRITE_SHOTGUN_WIDTH,
SPRITE_SHOTGUN_HEIGHT,
SPRITE_SHOTGUN_OFFSET_X,
SPRITE_SHOTGUN_OFFSET_Y
);
buildCompiledSprite(
&sprite_shieldKiller,
&shieldKiller,
SPRITE_SHIELDKILLER_WIDTH,
SPRITE_SHIELDKILLER_HEIGHT,
SPRITE_SHIELDKILLER_OFFSET_X,
SPRITE_SHIELDKILLER_OFFSET_Y
);
}
void renderMouse() {
mouse.x = rabbitPosition.mousePosition[0];
mouse.y = rabbitPosition.mousePosition[1];
drawCompiledSprite(&mouse);
drawPixel(rabbitPosition.mouseDotPosition[0], rabbitPosition.mouseDotPosition[1], 2);
}
void renderRabbit() {
rabbit.x = rabbitPosition.rabbitPosition[0];
rabbit.y = rabbitPosition.rabbitPosition[1];
drawCompiledSprite(&rabbit);
}
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);
}
}
void renderRabbitBullets() {
char i;
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
if (!rabbitBulletPosition[i].isActive) continue;
bullet.x = rabbitBulletPosition[i].x;
bullet.y = rabbitBulletPosition[i].y;
drawCompiledSprite(&bullet);
}
}
void renderEnemyBullets() {
char i;
for (i = 0; i < ENEMY_BULLET_LIMIT; ++i) {
if (!enemyBulletPosition[i].isActive) continue;
enemyBullet.x = enemyBulletPosition[i].x;
enemyBullet.y = enemyBulletPosition[i].y;
drawCompiledSprite(&enemyBullet);
}
}
void renderPowerup() {
if (!playerPowerup.isActive) return;
switch (playerPowerup.type) {
case POWERUP_TYPE_SHOTGUN:
shotgun.x = playerPowerup.x;
shotgun.y = playerPowerup.y;
drawCompiledSprite(&shotgun);
break;
case POWERUP_TYPE_SHIELD_KILLER:
shieldKiller.x = playerPowerup.x;
shieldKiller.y = playerPowerup.y;
drawCompiledSprite(&shieldKiller);
break;
}
}
void drawOnlyArenaForSprite(struct CompiledSpriteRender *sprite) {
getSpriteBounds(sprite, &bounds);
drawOnlyArena(&bounds);
}
void drawOnlyMouseArena() {
mouse.x = rabbitPosition.oldMousePosition[0];
mouse.y = rabbitPosition.oldMousePosition[1];
drawOnlyArenaForSprite(&mouse);
bounds.top = rabbitPosition.oldMouseDotPosition[1];
bounds.bottom = rabbitPosition.oldMouseDotPosition[1];
bounds.left = rabbitPosition.oldMouseDotPosition[0];
bounds.right = rabbitPosition.oldMouseDotPosition[0];
drawOnlyArena(&bounds);
}
void drawOnlyRabbitArena() {
rabbit.x = rabbitPosition.oldRabbitPosition[0];
rabbit.y = rabbitPosition.oldRabbitPosition[1];
drawOnlyArenaForSprite(&rabbit);
}
void drawOnlyPowerupArena() {
if (!playerPowerup.isActive) return;
switch (playerPowerup.type) {
case POWERUP_TYPE_SHOTGUN:
shotgun.x = playerPowerup.x;
shotgun.y = playerPowerup.y;
drawOnlyArenaForSprite(&shotgun);
break;
case POWERUP_TYPE_SHIELD_KILLER:
shieldKiller.x = playerPowerup.x;
shieldKiller.y = playerPowerup.y;
drawOnlyArenaForSprite(&shieldKiller);
break;
}
if (playerPowerup.willBeInactive) {
playerPowerup.isActive = 0;
}
}
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];
drawOnlyArenaForSprite(&enemy);
if (enemyPosition[i].willBeInactive) {
enemyPosition[i].isActive = 0;
}
}
}
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;
drawOnlyArenaForSprite(&bullet);
if (rabbitBulletPosition[i].willBeInactive) {
rabbitBulletPosition[i].isActive = 0;
}
}
}
void drawOnlyEnemyBulletArena() {
int i;
for (i = 0; i < ENEMY_BULLET_LIMIT; ++i) {
if (!enemyBulletPosition[i].isActive) continue;
enemyBullet.x = enemyBulletPosition[i].oldX;
enemyBullet.y = enemyBulletPosition[i].oldY;
drawOnlyArenaForSprite(&enemyBullet);
if (enemyBulletPosition[i].willBeInactive) {
enemyBulletPosition[i].isActive = 0;
}
}
}
int setupGame() {
FILE *fh;
installKeyboardHandler();
initializeDrawBuffer();
fh = fopen("sprtsht.bmp", "rb");
if (readBMPIntoNewMemory(fh, &spritesheetImage)) return 1;
fclose(fh);
spritesheetImage.transparentColor = 0;
setupWallSprites();
setupRabbitSprites();
setupRabbitBullets();
setupEnemies();
setupEnemyBullets();
setupEnemySprites();
setupPowerup();
setupPowerupSprites();
setVideoMode(VIDEO_MODE_VGA_256);
bmp256ColorPaletteToVGAColorPalette(&spritesheetImage, vgaColors);
setVGAColors(vgaColors, 256);
activateMouse(&mouseStatus);
buildArena();
clearArenaRedrawRequests();
srand(time(NULL));
return 0;
}
void handleMovement() {
handleRabbitMovement(
&rabbitPosition,
&keyboardKeydownState
);
handleEnemyMovement(
enemyPosition,
&rabbitPosition
);
captureAndLimitMousePosition(
&rabbitPosition,
&mouseStatus
);
calculateTargetAngle(
&rabbitPosition,
&mouseStatus
);
}
void handleCombat() {
int didHitRabbit;
if (mouseStatus.leftButtonDown) {
if (attemptToFireRabbitBullet(
&rabbitPosition,
&rabbitWeaponry,
rabbitBulletPosition
)) {
if (rabbitWeaponry.currentWeaponRemainingRounds > 0) {
rabbitWeaponry.currentWeaponRemainingRounds--;
if (rabbitWeaponry.currentWeaponRemainingRounds == 0) {
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
}
}
}
}
attemptToFireEnemyBullets(
enemyPosition,
enemyBulletPosition,
&rabbitPosition,
globalGameState.difficulty
);
advanceRabbitBullets(
rabbitBulletPosition,
&rabbitWeaponry
);
advanceEnemyBullets(
enemyBulletPosition
);
buildCollisionGrids(
rabbitBulletPosition,
enemyBulletPosition,
&rabbitPosition,
enemyPosition,
&playerPowerup
);
didHitRabbit = handleRabbitToEnemyCollisions(
&rabbitPosition,
enemyPosition
);
if (didHitRabbit) {
health -= ENEMY_COLLISION_DAMAGE * didHitRabbit;
}
didHitRabbit = handleEnemyBulletToRabbitCollisions(
enemyBulletPosition,
&rabbitPosition
);
if (didHitRabbit) {
health -= ENEMY_BULLET_DAMAGE * didHitRabbit;
}
handleRabbitBulletToEnemyCollisions(
rabbitBulletPosition,
enemyPosition
);
if (handleRabbitToPowerupCollision(&rabbitPosition, &playerPowerup)) {
playerPowerup.willBeInactive = 1;
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SPREAD_SHOT_GUN;
rabbitWeaponry.currentWeaponRemainingRounds = (globalGameState.difficulty + 1) * SHOTGUN_ROUNDS_PER_LEVEL;
}
}
void handleRedraw() {
drawOnlyRabbitArena();
drawOnlyEnemiesArena();
drawOnlyMouseArena();
drawOnlyRabbitBulletArena();
drawOnlyEnemyBulletArena();
drawOnlyPowerupArena();
redrawArena();
renderPowerup();
renderRabbit();
renderEnemies();
renderMouse();
renderRabbitBullets();
renderEnemyBullets();
}
/*
double speedCalcs[200];
int currentSpeedCalc = 0;
double averageSpeedCalc;
clock_t startTime;
*/
int main(void) {
byte *drawBuffer;
int keepRunning = 1;
int i;
char buffer[20];
if (setupGame()) return 1;
drawBuffer = getDrawBuffer();
while (keepRunning) {
readMouse(&mouseStatus);
populateKeyboardKeydownState();
maybeSpawnEnemy(
&globalGameState,
enemyPosition,
&rabbitPosition
);
handleMovement();
handleRedraw();
handleCombat();
handleEnemyKills();
sprintf(buffer, "Hit: %d", kills);
renderStringToDrawBuffer(buffer, 1, 0, 210, 20);
sprintf(buffer, "Health: %d ", health);
renderStringToDrawBuffer(buffer, 1, 0, 210, 30);
sprintf(buffer, "Rnds: %d ", rabbitWeaponry.currentWeaponRemainingRounds);
renderStringToDrawBuffer(buffer, 1, 0, 210, 40);
waitStartVbl();
copyDrawBufferToDisplay();
waitEndVbl();
if (keyboardKeydownState.KEY_ESC) { keepRunning = 0; }
}
freeBMP(&spritesheetImage);
setVideoMode(VIDEO_MODE_80x25_TEXT);
uninstallKeyboardHandler();
/*
averageSpeedCalc = 0;
for (currentSpeedCalc = 0; currentSpeedCalc < 200; ++currentSpeedCalc) {
averageSpeedCalc += speedCalcs[currentSpeedCalc];
}
averageSpeedCalc /= 200;
fprintf(stderr, "average: %f\n", averageSpeedCalc);
*/
return 0;
}