this is becoming a game

This commit is contained in:
John Bintz 2024-02-25 16:59:04 -05:00
parent 7b85f11143
commit 3521491bcd
7 changed files with 272 additions and 35 deletions

113
combat.c
View File

@ -1,5 +1,6 @@
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include "game.h" #include "game.h"
@ -7,6 +8,23 @@
#include "const.h" #include "const.h"
#include "system/vga.h" #include "system/vga.h"
void setupBullet(struct BulletPosition *bullet, int x, int y, signed int doubleVelocityX, signed int doubleVelocityY, int velocity) {
bullet->isActive = 1;
bullet->willBeInactive = 0;
bullet->x = x;
bullet->y = y;
bullet->oldX = x;
bullet->oldY = y;
bullet->velocityXSteps[0] = doubleVelocityX / velocity;
bullet->velocityXSteps[1] = doubleVelocityX - bullet->velocityXSteps[0];
bullet->velocityYSteps[0] = doubleVelocityY / velocity;
bullet->velocityYSteps[1] = doubleVelocityY - bullet->velocityYSteps[0];
bullet->velocityStep = 0;
}
void attemptToFireRabbitBullet( void attemptToFireRabbitBullet(
struct RabbitPosition *rabbitPosition, struct RabbitPosition *rabbitPosition,
struct RabbitWeaponry *rabbitWeaponry, struct RabbitWeaponry *rabbitWeaponry,
@ -14,7 +32,6 @@ void attemptToFireRabbitBullet(
) { ) {
int okToFire = 0, availableBullet, i; int okToFire = 0, availableBullet, i;
signed int doubleVelocityX, doubleVelocityY; signed int doubleVelocityX, doubleVelocityY;
char buffer[20];
if (rabbitWeaponry->cooldown > 0) return; if (rabbitWeaponry->cooldown > 0) return;
@ -33,20 +50,14 @@ void attemptToFireRabbitBullet(
doubleVelocityX = sin(rabbitPosition->mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2); doubleVelocityX = sin(rabbitPosition->mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
doubleVelocityY = -cos(rabbitPosition->mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2); doubleVelocityY = -cos(rabbitPosition->mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
rabbitBulletPosition[availableBullet].isActive = 1; setupBullet(
rabbitBulletPosition[availableBullet].willBeInactive = 0; &rabbitBulletPosition[availableBullet],
rabbitBulletPosition[availableBullet].x = rabbitPosition->rabbitPosition[0]; rabbitPosition->rabbitPosition[0],
rabbitBulletPosition[availableBullet].y = rabbitPosition->rabbitPosition[1]; rabbitPosition->rabbitPosition[1],
rabbitBulletPosition[availableBullet].oldX = rabbitBulletPosition[availableBullet].x; doubleVelocityX,
rabbitBulletPosition[availableBullet].oldY = rabbitBulletPosition[availableBullet].y; doubleVelocityY,
RABBIT_BULLET_VELOCITY
rabbitBulletPosition[availableBullet].velocityXSteps[0] = doubleVelocityX / RABBIT_BULLET_VELOCITY; );
rabbitBulletPosition[availableBullet].velocityXSteps[1] = doubleVelocityX - rabbitBulletPosition[availableBullet].velocityXSteps[0];
rabbitBulletPosition[availableBullet].velocityYSteps[0] = doubleVelocityY / RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityYSteps[1] = doubleVelocityY - rabbitBulletPosition[availableBullet].velocityYSteps[0];
rabbitBulletPosition[availableBullet].velocityStep = 0;
} }
void advanceRabbitBullets( void advanceRabbitBullets(
@ -75,6 +86,77 @@ void advanceRabbitBullets(
if (rabbitWeaponry->cooldown > 0) rabbitWeaponry->cooldown--; if (rabbitWeaponry->cooldown > 0) rabbitWeaponry->cooldown--;
} }
void advanceEnemyBullets(
struct BulletPosition enemyBulletPosition[]
) {
int i;
for (i = 0; i < ENEMY_BULLET_LIMIT; ++i) {
if (!enemyBulletPosition[i].isActive) continue;
enemyBulletPosition[i].oldX = enemyBulletPosition[i].x;
enemyBulletPosition[i].oldY = enemyBulletPosition[i].y;
enemyBulletPosition[i].x += enemyBulletPosition[i].velocityXSteps[enemyBulletPosition[i].velocityStep];
enemyBulletPosition[i].y += enemyBulletPosition[i].velocityYSteps[enemyBulletPosition[i].velocityStep];
if (enemyBulletPosition[i].x < MOUSE_LIMIT_LEFT) enemyBulletPosition[i].willBeInactive = 1;
if (enemyBulletPosition[i].x >= MOUSE_LIMIT_RIGHT) enemyBulletPosition[i].willBeInactive = 1;
if (enemyBulletPosition[i].y < MOUSE_LIMIT_TOP) enemyBulletPosition[i].willBeInactive = 1;
if (enemyBulletPosition[i].y >= MOUSE_LIMIT_BOTTOM) enemyBulletPosition[i].willBeInactive = 1;
enemyBulletPosition[i].velocityStep = 1 - enemyBulletPosition[i].velocityStep;
}
}
void attemptToFireEnemyBullets(
struct EnemyPosition enemyPosition[],
struct BulletPosition enemyBulletPosition[],
struct RabbitPosition *rabbitPosition
) {
int availableBullets[ENEMY_BULLET_LIMIT];
int i, availableBullet, maxAvailableBulletIndex = 0;
float distanceX, distanceY, angle, doubleVelocityX, doubleVelocityY;
for (i = 0; i < ENEMY_BULLET_LIMIT; ++i) {
if (!enemyBulletPosition[i].isActive) {
availableBullets[maxAvailableBulletIndex++] = i;
}
}
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
if (!enemyPosition[i].isActive) continue;
enemyPosition[i].enemyFireDelayStep--;
if (enemyPosition[i].enemyFireDelayStep > 0) continue;
distanceX = rabbitPosition->rabbitPosition[0] - enemyPosition[i].enemyPosition[0];
distanceY = rabbitPosition->rabbitPosition[1] - enemyPosition[i].enemyPosition[1];
angle = atan2(distanceY, distanceX) * RAD2DEG + 90;
if (angle < 0) angle += 360;
doubleVelocityX = sin(angle * DEG2RAD) * (ENEMY_BULLET_VELOCITY * 2);
doubleVelocityY = -cos(angle * DEG2RAD) * (ENEMY_BULLET_VELOCITY * 2);
availableBullet = availableBullets[--maxAvailableBulletIndex];
setupBullet(
&enemyBulletPosition[availableBullet],
enemyPosition[i].enemyPosition[0],
enemyPosition[i].enemyPosition[1],
doubleVelocityX,
doubleVelocityY,
ENEMY_BULLET_VELOCITY
);
enemyPosition[i].enemyFireDelayStep = ENEMY_FIRE_MIN_DELAY + rand() % ENEMY_FIRE_VARIABLE_DELAY;
if (maxAvailableBulletIndex < 0) break;
}
}
struct CollisionDetection { struct CollisionDetection {
short int sourceSX, sourceSY, sourceEX, sourceEY; short int sourceSX, sourceSY, sourceEX, sourceEY;
short int targetSX, targetSY, targetEX, targetEY; short int targetSX, targetSY, targetEX, targetEY;
@ -218,6 +300,7 @@ void handleRabbitBulletToEnemyCollisions(
if (isCollision()) { if (isCollision()) {
enemyPosition[resolvedEnemyIdx].willBeInactive = 1; enemyPosition[resolvedEnemyIdx].willBeInactive = 1;
enemyPosition[resolvedBulletIdx].wasKilled = 1;
rabbitBulletPosition[resolvedBulletIdx].willBeInactive = 1; rabbitBulletPosition[resolvedBulletIdx].willBeInactive = 1;
break; break;
} }

View File

@ -24,3 +24,13 @@ void buildCollisionGrids(
struct RabbitPosition *rabbitPosition, struct RabbitPosition *rabbitPosition,
struct EnemyPosition enemyPosition[] struct EnemyPosition enemyPosition[]
); );
void attemptToFireEnemyBullets(
struct EnemyPosition[],
struct BulletPosition enemyBulletPosition[],
struct RabbitPosition*
);
void advanceEnemyBullets(
struct BulletPosition[]
);

14
const.h
View File

@ -11,7 +11,7 @@
#define RABBIT_MOTION_VELOCITY_DECAY (5) #define RABBIT_MOTION_VELOCITY_DECAY (5)
#define RABBIT_BULLET_LIMIT (12) #define RABBIT_BULLET_LIMIT (12)
#define RABBIT_BULLET_VELOCITY (RABBIT_MOTION_ACCELERATION + 1) #define RABBIT_BULLET_VELOCITY ((RABBIT_MOTION_ACCELERATION / 2) + 1)
#define RABBIT_BULLET_COOLDOWN (10) #define RABBIT_BULLET_COOLDOWN (10)
#define RABBIT_BULLET_HEIGHT_START (8) #define RABBIT_BULLET_HEIGHT_START (8)
@ -24,3 +24,15 @@
#define DEG2RAD (3.14159/180) #define DEG2RAD (3.14159/180)
#define ENEMY_MAX_COUNT (50) #define ENEMY_MAX_COUNT (50)
#define ENEMY_BULLET_LIMIT (ENEMY_MAX_COUNT / 2)
#define ENEMY_MOVE_SPEED (1)
#define ENEMY_MOVE_DELAY (3)
#define ENEMY_BULLET_VELOCITY (RABBIT_BULLET_VELOCITY - 2)
#define ENEMY_FIRE_MIN_DELAY (30)
#define ENEMY_FIRE_VARIABLE_DELAY (45)
#define BASE_ENEMY_SPAWN_COOLDOWN (30)
#define MINIMUM_ENEMY_SPAWN_COOLDOWN (3)
#define VARIABLE_ENEMY_SPAWN_COOLDOWN (10)

121
game.c
View File

@ -18,7 +18,7 @@
#include "combat.h" #include "combat.h"
// TODO: centralize these outside of game.c // TODO: centralize these outside of game.c
struct CompiledSpriteRender rabbit, mouse, bullet, enemy; struct CompiledSpriteRender rabbit, mouse, bullet, enemy, enemyBullet;
struct SpriteBounds bounds; struct SpriteBounds bounds;
struct BMPImage spritesheetImage; struct BMPImage spritesheetImage;
@ -35,6 +35,7 @@ struct RabbitPosition rabbitPosition = {
}; };
struct EnemyPosition enemyPosition[ENEMY_MAX_COUNT]; struct EnemyPosition enemyPosition[ENEMY_MAX_COUNT];
struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT]; struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT];
struct BulletPosition enemyBulletPosition[ENEMY_BULLET_LIMIT];
struct RabbitWeaponry rabbitWeaponry; struct RabbitWeaponry rabbitWeaponry;
struct SpawnPointRange { struct SpawnPointRange {
@ -42,8 +43,11 @@ struct SpawnPointRange {
int top, height; int top, height;
}; };
struct SpawnPointRange spawnPointRanges[1] = { struct SpawnPointRange spawnPointRanges[4] = {
{ .left = 130, .width = 50, .top = 50, .height = 100 } { .left = TILE_SIZE * 4, .width = TILE_SIZE * 2, .top = TILE_SIZE, .height = TILE_SIZE },
{ .left = TILE_SIZE * 4, .width = TILE_SIZE * 2, .top = (ARENA_HEIGHT_TILES - 2) * TILE_SIZE, .height = TILE_SIZE },
{ .left = TILE_SIZE, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 },
{ .left = (ARENA_WIDTH_TILES - 2) * TILE_SIZE, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 },
}; };
@ -58,6 +62,15 @@ void setupRabbitBullets() {
rabbitWeaponry.cooldown = 0; rabbitWeaponry.cooldown = 0;
} }
void setupEnemyBullets() {
int i;
for (i = 0; i < ENEMY_BULLET_LIMIT; ++i) {
enemyBulletPosition[i].isActive = 0;
enemyBulletPosition[i].willBeInactive = 0;
}
}
void setupEnemies() { void setupEnemies() {
int i; int i;
@ -67,11 +80,40 @@ void setupEnemies() {
} }
} }
int spawnCooldown = 0;
int difficulty = 0;
int kills = 0;
int difficultyBands[10] = { 10, 20, 30, 50, 80, 130, 210, 340, 550, 890 };
void handleEnemyKills() {
int i, hadKill;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
if (enemyPosition[i].wasKilled) {
enemyPosition[i].wasKilled = 0;
kills++;
hadKill = 1;
}
}
if (hadKill) {
for (i = 0; i < 10; ++i) {
if (kills > difficultyBands[i]) {
difficulty = i + 1;
}
}
}
}
void maybeSpawnEnemy() { void maybeSpawnEnemy() {
char canSpawn; char canSpawn;
int i, availableEnemy; int i, availableEnemy;
int spawnX, spawnY; int spawnX, spawnY;
if (spawnCooldown-- > 0) return;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) { for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
if (!enemyPosition[i].isActive) { if (!enemyPosition[i].isActive) {
availableEnemy = i; availableEnemy = i;
@ -82,14 +124,22 @@ void maybeSpawnEnemy() {
if (!canSpawn) return; if (!canSpawn) return;
spawnX = spawnPointRanges[0].left + rand() % spawnPointRanges[0].width; i = rand() % 4;
spawnY = spawnPointRanges[0].top + rand() % spawnPointRanges[0].height;
spawnX = spawnPointRanges[i].left + rand() % spawnPointRanges[i].width;
spawnY = spawnPointRanges[i].top + rand() % spawnPointRanges[i].height;
enemyPosition[availableEnemy].isActive = 1; enemyPosition[availableEnemy].isActive = 1;
enemyPosition[availableEnemy].willBeInactive = 0;
enemyPosition[availableEnemy].wasKilled = 0;
enemyPosition[availableEnemy].enemyMoveDelayStep = 0;
enemyPosition[availableEnemy].enemyFireDelayStep = ENEMY_FIRE_MIN_DELAY + rand() % ENEMY_FIRE_VARIABLE_DELAY;
enemyPosition[availableEnemy].enemyPosition[0] = spawnX; enemyPosition[availableEnemy].enemyPosition[0] = spawnX;
enemyPosition[availableEnemy].enemyPosition[1] = spawnY; enemyPosition[availableEnemy].enemyPosition[1] = spawnY;
enemyPosition[availableEnemy].oldEnemyPosition[0] = spawnX; enemyPosition[availableEnemy].oldEnemyPosition[0] = spawnX;
enemyPosition[availableEnemy].oldEnemyPosition[1] = spawnY; enemyPosition[availableEnemy].oldEnemyPosition[1] = spawnY;
spawnCooldown = (BASE_ENEMY_SPAWN_COOLDOWN - difficulty) + MINIMUM_ENEMY_SPAWN_COOLDOWN + rand() % VARIABLE_ENEMY_SPAWN_COOLDOWN;
} }
void setupEnemySprites() { void setupEnemySprites() {
@ -101,6 +151,15 @@ void setupEnemySprites() {
SPRITE_ENEMY_OFFSET_X, SPRITE_ENEMY_OFFSET_X,
SPRITE_ENEMY_OFFSET_Y SPRITE_ENEMY_OFFSET_Y
); );
buildCompiledSprite(
&sprite_bullet,
&enemyBullet,
SPRITE_BULLET_WIDTH,
SPRITE_BULLET_HEIGHT,
SPRITE_BULLET_OFFSET_X,
SPRITE_BULLET_OFFSET_Y
);
} }
void setupRabbitSprites() { void setupRabbitSprites() {
@ -173,6 +232,19 @@ void renderRabbitBullets() {
} }
} }
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 drawOnlyMouseArena() { void drawOnlyMouseArena() {
mouse.x = rabbitPosition.oldMousePosition[0]; mouse.x = rabbitPosition.oldMousePosition[0];
mouse.y = rabbitPosition.oldMousePosition[1]; mouse.y = rabbitPosition.oldMousePosition[1];
@ -226,6 +298,22 @@ void drawOnlyRabbitBulletArena() {
} }
} }
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;
getSpriteBounds(&enemyBullet, &bounds);
drawOnlyArena(&bounds);
if (enemyBulletPosition[i].willBeInactive) {
enemyBulletPosition[i].isActive = 0;
}
}
}
int setupGame() { int setupGame() {
FILE *fh; FILE *fh;
@ -262,6 +350,10 @@ void handleMovement() {
&rabbitPosition, &rabbitPosition,
&keyboardKeydownState &keyboardKeydownState
); );
handleEnemyMovement(
enemyPosition,
&rabbitPosition
);
captureAndLimitMousePosition( captureAndLimitMousePosition(
&rabbitPosition, &rabbitPosition,
&mouseStatus &mouseStatus
@ -281,11 +373,21 @@ void handleCombat() {
); );
} }
attemptToFireEnemyBullets(
enemyPosition,
enemyBulletPosition,
&rabbitPosition
);
advanceRabbitBullets( advanceRabbitBullets(
rabbitBulletPosition, rabbitBulletPosition,
&rabbitWeaponry &rabbitWeaponry
); );
advanceEnemyBullets(
enemyBulletPosition
);
buildCollisionGrids( buildCollisionGrids(
rabbitBulletPosition, rabbitBulletPosition,
&rabbitPosition, &rabbitPosition,
@ -308,6 +410,7 @@ void handleRedraw() {
drawOnlyEnemiesArena(); drawOnlyEnemiesArena();
drawOnlyMouseArena(); drawOnlyMouseArena();
drawOnlyRabbitBulletArena(); drawOnlyRabbitBulletArena();
drawOnlyEnemyBulletArena();
redrawArena(); redrawArena();
@ -315,6 +418,7 @@ void handleRedraw() {
renderEnemies(); renderEnemies();
renderMouse(); renderMouse();
renderRabbitBullets(); renderRabbitBullets();
renderEnemyBullets();
} }
/* /*
@ -333,20 +437,19 @@ int main(void) {
drawBuffer = getDrawBuffer(); drawBuffer = getDrawBuffer();
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
maybeSpawnEnemy();
}
while (keepRunning) { while (keepRunning) {
readMouse(&mouseStatus); readMouse(&mouseStatus);
populateKeyboardKeydownState(); populateKeyboardKeydownState();
maybeSpawnEnemy();
handleMovement(); handleMovement();
handleRedraw(); handleRedraw();
handleCombat(); handleCombat();
waitStartVbl(); waitStartVbl();
copyDrawBufferToDisplay(); copyDrawBufferToDisplay();
waitEndVbl(); waitEndVbl();
if (keyboardKeydownState.KEY_ESC) { keepRunning = 0; } if (keyboardKeydownState.KEY_ESC) { keepRunning = 0; }

2
game.h
View File

@ -1,4 +1,4 @@
#include "system/vga.h" #include "system/vga.h"
extern struct CompiledSpriteRender rabbit, mouse, bullet, enemy; extern struct CompiledSpriteRender rabbit, mouse, bullet, enemy, enemyBullet;
extern struct SpriteBounds bounds; extern struct SpriteBounds bounds;

View File

@ -1,4 +1,5 @@
#include <math.h> #include <math.h>
#include <stdlib.h>
#include "const.h" #include "const.h"
#include "movement.h" #include "movement.h"
@ -45,16 +46,39 @@ void calculateTargetAngle(
pos->mousePosition[0] = pos->rabbitPosition[0] + distanceX; pos->mousePosition[0] = pos->rabbitPosition[0] + distanceX;
pos->mousePosition[1] = pos->rabbitPosition[1] + distanceY; pos->mousePosition[1] = pos->rabbitPosition[1] + distanceY;
// get the shot angle
distanceX = pos->mouseDotPosition[0] - pos->rabbitPosition[0];
distanceY = pos->mouseDotPosition[1] - pos->rabbitPosition[1];
angle = atan2(distanceY, distanceX) * RAD2DEG + 90;
if (angle < 0) angle += 360;
pos->mouseAngle = angle; pos->mouseAngle = angle;
} }
#define SIGN(x) ((x > 0) - (x < 0))
void handleEnemyMovement(
struct EnemyPosition enemyPosition[],
struct RabbitPosition *pos
) {
int i, xDistance, yDistance;
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
if (!enemyPosition[i].isActive) continue;
enemyPosition[i].enemyMoveDelayStep += 1;
if (enemyPosition[i].enemyMoveDelayStep < ENEMY_MOVE_DELAY) continue;
enemyPosition[i].enemyMoveDelayStep = 0;
// which straight directon is the player closest to
xDistance = pos->rabbitPosition[0] - enemyPosition[i].enemyPosition[0];
yDistance = pos->rabbitPosition[1] - enemyPosition[i].enemyPosition[1];
enemyPosition[i].oldEnemyPosition[0] = enemyPosition[i].enemyPosition[0];
enemyPosition[i].oldEnemyPosition[1] = enemyPosition[i].enemyPosition[1];
if (abs(xDistance) > abs(yDistance)) {
enemyPosition[i].enemyPosition[0] += SIGN(xDistance) * ENEMY_MOVE_SPEED;
} else {
enemyPosition[i].enemyPosition[1] += SIGN(yDistance) * ENEMY_MOVE_SPEED;
}
}
}
void handleRabbitMovement( void handleRabbitMovement(
struct RabbitPosition *pos, struct RabbitPosition *pos,
struct KeyboardKeydownState *keyboardKeydownState struct KeyboardKeydownState *keyboardKeydownState

View File

@ -27,19 +27,24 @@ struct BulletPosition {
int x, y; int x, y;
int oldX, oldY; int oldX, oldY;
signed char velocityXSteps[2], velocityYSteps[2]; signed int velocityXSteps[2], velocityYSteps[2];
char velocityStep; int velocityStep;
}; };
struct EnemyPosition { struct EnemyPosition {
char isActive; char isActive;
char willBeInactive; char willBeInactive;
char wasKilled;
int enemyPosition[2]; int enemyPosition[2];
int oldEnemyPosition[2]; int oldEnemyPosition[2];
int enemyMoveDelayStep;
int enemyFireDelayStep;
}; };
void calculateTargetAngle(struct RabbitPosition*, struct MouseStatus*); void calculateTargetAngle(struct RabbitPosition*, struct MouseStatus*);
void captureAndLimitMousePosition(struct RabbitPosition*, struct MouseStatus*); void captureAndLimitMousePosition(struct RabbitPosition*, struct MouseStatus*);
void handleRabbitMovement(struct RabbitPosition*, struct KeyboardKeydownState*); void handleRabbitMovement(struct RabbitPosition*, struct KeyboardKeydownState*);
void handleEnemyMovement(struct EnemyPosition[], struct RabbitPosition*);