bullet -> enemy collision codepath works

This commit is contained in:
John Bintz 2024-02-25 08:43:30 -05:00
parent 35d7ba95b9
commit 903835a10a
6 changed files with 245 additions and 194 deletions

149
combat.c Normal file
View File

@ -0,0 +1,149 @@
#include <math.h>
#include "game.h"
#include "movement.h"
#include "const.h"
#include "system/vga.h"
void attemptToFireRabbitBullet(
struct RabbitPosition *rabbitPosition,
struct RabbitWeaponry *rabbitWeaponry,
struct BulletPosition rabbitBulletPosition[]
) {
int okToFire = 0, availableBullet, i;
signed char doubleVelocityX, doubleVelocityY;
if (rabbitWeaponry->cooldown > 0) return;
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
if (rabbitBulletPosition[i].isActive == 0) {
okToFire = 1;
availableBullet = i;
break;
}
}
if (!okToFire) return;
rabbitWeaponry->cooldown = RABBIT_BULLET_COOLDOWN;
doubleVelocityX = sin(rabbitPosition->mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
doubleVelocityY = -cos(rabbitPosition->mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
rabbitBulletPosition[availableBullet].isActive = 1;
rabbitBulletPosition[availableBullet].willBeInactive = 0;
rabbitBulletPosition[availableBullet].x = rabbitPosition->rabbitPosition[0];
rabbitBulletPosition[availableBullet].y = rabbitPosition->rabbitPosition[1] - RABBIT_BULLET_HEIGHT_START;
rabbitBulletPosition[availableBullet].oldX = rabbitBulletPosition[availableBullet].x;
rabbitBulletPosition[availableBullet].oldY = rabbitBulletPosition[availableBullet].y;
rabbitBulletPosition[availableBullet].velocityXSteps[0] = doubleVelocityX / RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityXSteps[1] = doubleVelocityX % RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityYSteps[0] = doubleVelocityY / RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityYSteps[1] = doubleVelocityY % RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityStep = 0;
}
void advanceRabbitBullets(
struct BulletPosition rabbitBulletPosition[],
struct RabbitWeaponry *rabbitWeaponry
) {
int i;
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
if (!rabbitBulletPosition[i].isActive) continue;
rabbitBulletPosition[i].oldX = rabbitBulletPosition[i].x;
rabbitBulletPosition[i].oldY = rabbitBulletPosition[i].y;
rabbitBulletPosition[i].x += rabbitBulletPosition[i].velocityXSteps[rabbitBulletPosition[i].velocityStep];
rabbitBulletPosition[i].y += rabbitBulletPosition[i].velocityYSteps[rabbitBulletPosition[i].velocityStep];
if (rabbitBulletPosition[i].x < MOUSE_LIMIT_LEFT) rabbitBulletPosition[i].willBeInactive = 1;
if (rabbitBulletPosition[i].x >= MOUSE_LIMIT_RIGHT) rabbitBulletPosition[i].willBeInactive = 1;
if (rabbitBulletPosition[i].y < MOUSE_LIMIT_TOP) rabbitBulletPosition[i].willBeInactive = 1;
if (rabbitBulletPosition[i].y >= MOUSE_LIMIT_BOTTOM) rabbitBulletPosition[i].willBeInactive = 1;
rabbitBulletPosition[i].velocityStep = 1 - rabbitBulletPosition[i].velocityStep;
}
if (rabbitWeaponry->cooldown > 0) rabbitWeaponry->cooldown--;
}
struct CollisionDetection {
short int sourceSX, sourceSY, sourceEX, sourceEY;
short int targetSX, targetSY, targetEX, targetEY;
};
struct CollisionDetection collisionDetection;
int isCollision() {
int result;
// first pass: are the hitboxes even close
result = !(
collisionDetection.sourceEY < collisionDetection.targetSY ||
collisionDetection.sourceEX < collisionDetection.targetSX ||
collisionDetection.targetEY < collisionDetection.sourceSY ||
collisionDetection.targetEX < collisionDetection.sourceSX
);
if (!result) return result;
// second pass: check against specific character shaped hitboxes
// these will be generated from the spritesheet generator
return result;
}
void populateSourceCollision(struct CompiledSpriteRender *sprite) {
getSpriteBounds(sprite, &bounds);
collisionDetection.sourceSX = bounds.left;
collisionDetection.sourceSY = bounds.top;
collisionDetection.sourceEX = bounds.right;
collisionDetection.sourceEY = bounds.bottom;
}
void populateTargetCollision(struct CompiledSpriteRender *sprite) {
getSpriteBounds(sprite, &bounds);
collisionDetection.targetSX = bounds.left;
collisionDetection.targetSY = bounds.top;
collisionDetection.targetEX = bounds.right;
collisionDetection.targetEY = bounds.bottom;
}
void handleRabbitBulletToEnemyCollisions(
struct BulletPosition rabbitBulletPosition[],
struct EnemyPosition enemyPosition[]
) {
int bulletIdx, enemyIdx;
// to start: brute force items
// possible performance improvements:
// * horizontal sweep against enemies to rule out enemies to check
for (bulletIdx = 0; bulletIdx < RABBIT_BULLET_LIMIT; ++bulletIdx) {
if (!rabbitBulletPosition[bulletIdx].isActive) continue;
if (rabbitBulletPosition[bulletIdx].willBeInactive) continue;
populateSourceCollision(&bullet);
for (enemyIdx = 0; enemyIdx < ENEMY_MAX_COUNT; ++enemyIdx) {
if (!enemyPosition[enemyIdx].isActive) continue;
if (enemyPosition[enemyIdx].willBeInactive) continue;
populateTargetCollision(&enemy);
if (isCollision()) {
enemyPosition[enemyIdx].willBeInactive = 1;
rabbitBulletPosition[bulletIdx].willBeInactive = 1;
break;
}
}
}
}

15
combat.h Normal file
View File

@ -0,0 +1,15 @@
void attemptToFireRabbitBullet(
struct RabbitPosition*,
struct RabbitWeaponry*,
struct BulletPosition[]
);
void advanceRabbitBullets(
struct BulletPosition[],
struct RabbitWeaponry*
);
void handleRabbitBulletToEnemyCollisions(
struct BulletPosition[],
struct EnemyPosition[]
);

202
game.c
View File

@ -14,11 +14,17 @@
#include "const.h"
#include "arena.h"
#include "movement.h"
#include "combat.h"
// TODO: centralize these outside of game.c
struct CompiledSpriteRender rabbit, mouse, bullet, enemy;
struct SpriteBounds bounds;
struct BMPImage spritesheetImage;
struct VGAColor vgaColors[256];
struct CompiledSpriteRender rabbit, mouse, bullet, enemy;
struct MouseStatus mouseStatus;
struct RabbitPosition rabbitPosition = {
.rabbitPosition = { 60, 60 },
.rabbitLimits = { { 20, 20 }, { 180, 180 } },
@ -26,12 +32,20 @@ struct RabbitPosition rabbitPosition = {
.rabbitVelocity = { 0, 0 },
.mouseDotPosition = { 0, 0 }
};
struct EnemyPosition enemyPosition[ENEMY_MAX_COUNT];
struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT];
struct RabbitWeaponry rabbitWeaponry;
struct SpawnPointRange {
int left, width;
int top, height;
};
struct SpawnPointRange spawnPointRanges[1] = {
{ .left = 50, .width = 30, .top = 50, .height = 30 }
};
void setupRabbitBullets() {
int i;
@ -52,91 +66,6 @@ void setupEnemies() {
}
}
void attemptToFireRabbitBullet() {
char okToFire = 0, availableBullet, i;
signed char doubleVelocityX, doubleVelocityY;
if (rabbitWeaponry.cooldown > 0) return;
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
if (rabbitBulletPosition[i].isActive == 0) {
okToFire = 1;
availableBullet = i;
break;
}
}
if (!okToFire) return;
rabbitWeaponry.cooldown = RABBIT_BULLET_COOLDOWN;
doubleVelocityX = sin(rabbitPosition.mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
doubleVelocityY = -cos(rabbitPosition.mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
rabbitBulletPosition[availableBullet].isActive = 1;
rabbitBulletPosition[availableBullet].willBeInactive = 0;
rabbitBulletPosition[availableBullet].x = rabbitPosition.rabbitPosition[0];
rabbitBulletPosition[availableBullet].y = rabbitPosition.rabbitPosition[1] - RABBIT_BULLET_HEIGHT_START;
rabbitBulletPosition[availableBullet].oldX = rabbitBulletPosition[availableBullet].x;
rabbitBulletPosition[availableBullet].oldY = rabbitBulletPosition[availableBullet].y;
rabbitBulletPosition[availableBullet].velocityXSteps[0] = doubleVelocityX / RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityXSteps[1] = doubleVelocityX % RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityYSteps[0] = doubleVelocityY / RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityYSteps[1] = doubleVelocityY % RABBIT_BULLET_VELOCITY;
rabbitBulletPosition[availableBullet].velocityStep = 0;
}
void advanceRabbitBullets() {
char i;
signed char velocityX, velocityY;
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
if (!rabbitBulletPosition[i].isActive) continue;
rabbitBulletPosition[i].oldX = rabbitBulletPosition[i].x;
rabbitBulletPosition[i].oldY = rabbitBulletPosition[i].y;
rabbitBulletPosition[i].x += rabbitBulletPosition[i].velocityXSteps[rabbitBulletPosition[i].velocityStep];
rabbitBulletPosition[i].y += rabbitBulletPosition[i].velocityYSteps[rabbitBulletPosition[i].velocityStep];
if (rabbitBulletPosition[i].x < MOUSE_LIMIT_LEFT) rabbitBulletPosition[i].willBeInactive = 1;
if (rabbitBulletPosition[i].x >= MOUSE_LIMIT_RIGHT) rabbitBulletPosition[i].willBeInactive = 1;
if (rabbitBulletPosition[i].y < MOUSE_LIMIT_TOP) rabbitBulletPosition[i].willBeInactive = 1;
if (rabbitBulletPosition[i].y >= MOUSE_LIMIT_BOTTOM) rabbitBulletPosition[i].willBeInactive = 1;
rabbitBulletPosition[i].velocityStep = 1 - rabbitBulletPosition[i].velocityStep;
}
if (rabbitWeaponry.cooldown > 0) rabbitWeaponry.cooldown--;
}
void detectPlayerBulletToEnemyCollisions() {
int bulletIdx, enemyIdx;
for (bulletIdx = 0; bulletIdx < RABBIT_BULLET_LIMIT; ++bulletIdx) {
if (!rabbitBulletPosition[bulletIdx].isActive) continue;
for (enemyIdx = 0; enemyIdx < ENEMY_MAX_COUNT; ++enemyIdx) {
if (!enemyPosition[enemyIdx].isActive) continue;
// bullets hit enemies if:
// * bulletY - bullet_height_start is within 3px of enemy base
// *
}
}
}
struct SpawnPointRange {
int left, width;
int top, height;
};
struct SpawnPointRange spawnPointRanges[1] = {
{ .left = 50, .width = 30, .top = 50, .height = 30 }
};
void maybeSpawnEnemy() {
char canSpawn;
@ -159,6 +88,8 @@ void maybeSpawnEnemy() {
enemyPosition[availableEnemy].isActive = 1;
enemyPosition[availableEnemy].enemyPosition[0] = spawnX;
enemyPosition[availableEnemy].enemyPosition[1] = spawnY;
enemyPosition[availableEnemy].oldEnemyPosition[0] = spawnX;
enemyPosition[availableEnemy].oldEnemyPosition[1] = spawnY;
}
void setupEnemySprites() {
@ -201,8 +132,6 @@ void setupRabbitSprites() {
);
}
struct MouseStatus mouseStatus;
void renderMouse() {
mouse.x = rabbitPosition.mousePosition[0];
mouse.y = rabbitPosition.mousePosition[1];
@ -244,8 +173,6 @@ void renderRabbitBullets() {
}
}
struct SpriteBounds bounds;
void drawOnlyMouseArena() {
mouse.x = rabbitPosition.oldMousePosition[0];
mouse.y = rabbitPosition.oldMousePosition[1];
@ -278,7 +205,7 @@ void drawOnlyEnemiesArena() {
drawOnlyArena(&bounds);
if (enemyPosition[i].willBeInactive) {
enemyPosition[i].willBeInactive = 0;
enemyPosition[i].isActive = 0;
}
}
}
@ -330,19 +257,66 @@ int setupGame() {
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
);
handleRabbitBulletToEnemyCollisions(
rabbitBulletPosition,
enemyPosition
);
}
void handleRedraw() {
drawOnlyRabbitArena();
drawOnlyEnemiesArena();
drawOnlyMouseArena();
drawOnlyRabbitBulletArena();
redrawArena();
renderRabbit();
renderEnemies();
renderMouse();
renderRabbitBullets();
}
/*
double speedCalcs[200];
int currentSpeedCalc = 0;
double averageSpeedCalc;
clock_t startTime;
// non cdecl is put params into eax, ebx, and ecx
*/
int main(void) {
byte *drawBuffer;
int keepRunning = 1;
void (*code)(byte *);
if (setupGame()) return 1;
drawBuffer = getDrawBuffer();
@ -352,39 +326,13 @@ int main(void) {
while (keepRunning) {
readMouse(&mouseStatus);
populateKeyboardKeydownState();
handleRabbitMovement(
&rabbitPosition,
&keyboardKeydownState
);
captureAndLimitMousePosition(
&rabbitPosition,
&mouseStatus
);
calculateTargetAngle(
&rabbitPosition,
&mouseStatus
);
if (mouseStatus.leftButtonDown) attemptToFireRabbitBullet();
advanceRabbitBullets();
drawOnlyRabbitArena();
drawOnlyEnemiesArena();
drawOnlyMouseArena();
drawOnlyRabbitBulletArena();
redrawArena();
renderRabbit();
renderEnemies();
renderMouse();
renderRabbitBullets();
handleMovement();
handleCombat();
handleRedraw();
waitStartVbl();
copyDrawBufferToDisplay();
waitEndVbl();
if (keyboardKeydownState.KEY_ESC) { keepRunning = 0; }
@ -395,6 +343,7 @@ int main(void) {
setVideoMode(VIDEO_MODE_80x25_TEXT);
uninstallKeyboardHandler();
/*
averageSpeedCalc = 0;
for (currentSpeedCalc = 0; currentSpeedCalc < 200; ++currentSpeedCalc) {
averageSpeedCalc += speedCalcs[currentSpeedCalc];
@ -403,6 +352,7 @@ int main(void) {
averageSpeedCalc /= 200;
fprintf(stderr, "average: %f\n", averageSpeedCalc);
*/
return 0;
}

4
game.h Normal file
View File

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

View File

@ -1,4 +1,4 @@
obj = game.o bmpload.o arena.o movement.o sprites.o
obj = game.o bmpload.o arena.o movement.o sprites.o combat.o
all: system/system.lib game.exe .SYMBOLIC

View File

@ -1,67 +0,0 @@
#PUBLIC doAThing_
#PUBLIC renderTopTile_
PUBLIC renderFloorTile_
.386
.model flat,c
.CODE
doAThing_:
push ebp
mov ebp, esp
mov eax, [ebp + 8]
pop ebp
ret
renderTopTile_:
push ebp
mov ebp, esp
push ebx
mov eax, [ebp + 8]
mov ebx, 20
_renderNextLine:
mov DWORD PTR [eax], 02020202h
mov DWORD PTR [eax + 4], 02020202h
mov DWORD PTR [eax + 8], 02020202h
mov DWORD PTR [eax + 12], 02020202h
mov DWORD PTR [eax + 16], 02020202h
add eax, 320
dec ebx
cmp ebx, 0
jne _renderNextLine
pop ebx
pop ebp
ret
renderFloorTile_:
push ebp
mov ebp, esp
push ebx
mov eax, [ebp + 8]
mov ebx, 20
_renderNextFloorLine:
mov DWORD PTR [eax], 04040404h
mov DWORD PTR [eax + 4], 04040404h
mov DWORD PTR [eax + 8], 04040404h
mov DWORD PTR [eax + 12], 04040404h
mov DWORD PTR [eax + 16], 04040404h
add eax, 320
dec ebx
cmp ebx, 0
jne _renderNextFloorLine
_done:
pop ebx
pop ebp
ret
end