From 903835a10a2dc0ef77dd3b6fbc74a893f0bcce13 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Sun, 25 Feb 2024 08:43:30 -0500 Subject: [PATCH] bullet -> enemy collision codepath works --- combat.c | 149 ++++++++++++++++++++++++++++++++++++++++ combat.h | 15 +++++ game.c | 202 +++++++++++++++++++++---------------------------------- game.h | 4 ++ makefile | 2 +- test.asm | 67 ------------------ 6 files changed, 245 insertions(+), 194 deletions(-) create mode 100644 combat.c create mode 100644 combat.h create mode 100644 game.h delete mode 100644 test.asm diff --git a/combat.c b/combat.c new file mode 100644 index 0000000..fb0d04e --- /dev/null +++ b/combat.c @@ -0,0 +1,149 @@ +#include + +#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; + } + } + } +} + diff --git a/combat.h b/combat.h new file mode 100644 index 0000000..6bda5a1 --- /dev/null +++ b/combat.h @@ -0,0 +1,15 @@ +void attemptToFireRabbitBullet( + struct RabbitPosition*, + struct RabbitWeaponry*, + struct BulletPosition[] +); + +void advanceRabbitBullets( + struct BulletPosition[], + struct RabbitWeaponry* +); + +void handleRabbitBulletToEnemyCollisions( + struct BulletPosition[], + struct EnemyPosition[] +); diff --git a/game.c b/game.c index 09bd0c0..57148d0 100644 --- a/game.c +++ b/game.c @@ -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; } diff --git a/game.h b/game.h new file mode 100644 index 0000000..f061979 --- /dev/null +++ b/game.h @@ -0,0 +1,4 @@ +#include "system/vga.h" + +extern struct CompiledSpriteRender rabbit, mouse, bullet, enemy; +extern struct SpriteBounds bounds; diff --git a/makefile b/makefile index 41c5bac..ef0bd1b 100644 --- a/makefile +++ b/makefile @@ -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 diff --git a/test.asm b/test.asm deleted file mode 100644 index 4665e94..0000000 --- a/test.asm +++ /dev/null @@ -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