#include #include #include #include "game.h" #include "movement.h" #include "const.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( struct RabbitPosition *rabbitPosition, struct RabbitWeaponry *rabbitWeaponry, struct BulletPosition rabbitBulletPosition[] ) { int okToFire = 0, availableBullet, i; signed int 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); setupBullet( &rabbitBulletPosition[availableBullet], rabbitPosition->rabbitPosition[0], rabbitPosition->rabbitPosition[1], doubleVelocityX, doubleVelocityY, RABBIT_BULLET_VELOCITY ); } 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--; } 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 difficulty ) { 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 - difficulty + rand() % ENEMY_FIRE_VARIABLE_DELAY; if (maxAvailableBulletIndex < 0) break; } } struct CollisionDetection { short int sourceSX, sourceSY, sourceEX, sourceEY; short int targetSX, targetSY, targetEX, targetEY; }; struct CollisionDetection collisionDetection; int isCollision() { if (collisionDetection.sourceEY < collisionDetection.targetSY) return 0; if (collisionDetection.sourceEX < collisionDetection.targetSX) return 0; if (collisionDetection.targetEY < collisionDetection.sourceSY) return 0; if (collisionDetection.targetEX < collisionDetection.sourceSX) return 0; return 1; } 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; } // We are hardcoding a 2x2 grid on the screen as our collision partitioning // scheme. int rabbitGrid[4]; int rabbitBulletGrid[4][RABBIT_BULLET_LIMIT]; int enemyGrid[4][ENEMY_MAX_COUNT]; int enemyBulletGrid[4][ENEMY_BULLET_LIMIT]; int rabbitBulletGridIndex[4], enemyGridIndex[4], enemyBulletGridIndex[4]; int gridPosition[4]; #define CALCULATE_GRID_POS(x,y) ((x / COLLISION_GRID_SIZE) + (y / COLLISION_GRID_SIZE) * 2) void determineGridPositionsForSprite(struct CompiledSpriteRender *sprite) { int i; getSpriteBounds(sprite, &bounds); for (i = 0; i < 4; ++i) { gridPosition[i] = 0; } gridPosition[CALCULATE_GRID_POS(bounds.left, bounds.top)] = 1; gridPosition[CALCULATE_GRID_POS(bounds.right, bounds.top)] = 1; gridPosition[CALCULATE_GRID_POS(bounds.left, bounds.bottom)] = 1; gridPosition[CALCULATE_GRID_POS(bounds.right, bounds.bottom)] = 1; } void buildCollisionGrids( struct BulletPosition rabbitBulletPosition[], struct BulletPosition enemyBulletPosition[], struct RabbitPosition *rabbitPosition, struct EnemyPosition enemyPosition[] ) { int grid, i; for (grid = 0; grid < 4; ++grid) { rabbitBulletGridIndex[grid] = 0; enemyBulletGridIndex[grid] = 0; enemyGridIndex[grid] = 0; } rabbit.x = rabbitPosition->rabbitPosition[0]; rabbit.y = rabbitPosition->rabbitPosition[1]; determineGridPositionsForSprite(&rabbit); for (grid = 0; grid < 4; ++grid) { rabbitGrid[grid] = gridPosition[grid]; } for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) { if (!rabbitBulletPosition[i].isActive) continue; if (rabbitBulletPosition[i].willBeInactive) continue; bullet.x = rabbitBulletPosition[i].x; bullet.y = rabbitBulletPosition[i].y; determineGridPositionsForSprite(&bullet); for (grid = 0; grid < 4; ++grid) { if (gridPosition[grid]) { rabbitBulletGrid[grid][rabbitBulletGridIndex[grid]++] = i; } } } for (i = 0; i < ENEMY_BULLET_LIMIT; ++i) { if (!enemyBulletPosition[i].isActive) continue; if (enemyBulletPosition[i].willBeInactive) continue; enemyBullet.x = enemyBulletPosition[i].x; enemyBullet.y = enemyBulletPosition[i].y; determineGridPositionsForSprite(&enemyBullet); for (grid = 0; grid < 4; ++grid) { if (gridPosition[grid]) { enemyBulletGrid[grid][enemyBulletGridIndex[grid]++] = i; } } } for (i = 0; i < ENEMY_MAX_COUNT; ++i) { if (!enemyPosition[i].isActive) continue; if (enemyPosition[i].willBeInactive) continue; enemy.x = enemyPosition[i].enemyPosition[0]; enemy.y = enemyPosition[i].enemyPosition[1]; determineGridPositionsForSprite(&enemy); for (grid = 0; grid < 4; ++grid) { if (gridPosition[grid]) { enemyGrid[grid][enemyGridIndex[grid]++] = i; } } } } int handleEnemyBulletToRabbitCollisions( struct BulletPosition enemyBulletPosition[], struct RabbitPosition *rabbitPosition ) { int bulletIdx, grid; int resolvedBulletIdx; int hitCount = 0; rabbit.x = rabbitPosition->rabbitPosition[0]; rabbit.y = rabbitPosition->rabbitPosition[1]; populateTargetCollision(&rabbit); for (grid = 0; grid < 4; ++grid) { if (!rabbitGrid[grid]) continue; for (bulletIdx = 0; bulletIdx < enemyBulletGridIndex[grid]; ++bulletIdx) { resolvedBulletIdx = enemyBulletGrid[grid][bulletIdx]; enemyBullet.x = enemyBulletPosition[resolvedBulletIdx].x; enemyBullet.y = enemyBulletPosition[resolvedBulletIdx].y; populateSourceCollision(&enemyBullet); if (isCollision()) { enemyBulletPosition[resolvedBulletIdx].willBeInactive = 1; hitCount++; break; } } } return hitCount; } void handleRabbitBulletToEnemyCollisions( struct BulletPosition rabbitBulletPosition[], struct EnemyPosition enemyPosition[] ) { int bulletIdx, enemyIdx, grid; int resolvedBulletIdx, resolvedEnemyIdx; for (grid = 0; grid < 4; ++grid) { if (enemyGridIndex[grid] == 0) continue; for (bulletIdx = 0; bulletIdx < rabbitBulletGridIndex[grid]; ++bulletIdx) { resolvedBulletIdx = rabbitBulletGrid[grid][bulletIdx]; bullet.x = rabbitBulletPosition[resolvedBulletIdx].x; bullet.y = rabbitBulletPosition[resolvedBulletIdx].y; populateSourceCollision(&bullet); for (enemyIdx = 0; enemyIdx < enemyGridIndex[grid]; ++enemyIdx) { resolvedEnemyIdx = enemyGrid[grid][enemyIdx]; enemy.x = enemyPosition[resolvedEnemyIdx].enemyPosition[0]; enemy.y = enemyPosition[resolvedEnemyIdx].enemyPosition[1]; populateTargetCollision(&enemy); if (isCollision()) { enemyPosition[resolvedEnemyIdx].willBeInactive = 1; enemyPosition[resolvedBulletIdx].wasKilled = 1; rabbitBulletPosition[resolvedBulletIdx].willBeInactive = 1; break; } } } } } int handleRabbitToEnemyCollisions( struct RabbitPosition *rabbitPosition, struct EnemyPosition enemyPosition[] ) { int enemyIdx, grid, resolvedEnemyIdx, hitCount = 0; // to start: brute force items // possible performance improvements: // * grid partitioning rabbit.x = rabbitPosition->rabbitPosition[0]; rabbit.y = rabbitPosition->rabbitPosition[1]; populateSourceCollision(&rabbit); for (grid = 0; grid < 4; ++grid) { if (!rabbitGrid[grid]) continue; if (enemyGridIndex[grid] == 0) continue; for (enemyIdx = 0; enemyIdx < enemyGridIndex[grid]; ++enemyIdx) { resolvedEnemyIdx = enemyGrid[grid][enemyIdx]; enemy.x = enemyPosition[resolvedEnemyIdx].enemyPosition[0]; enemy.y = enemyPosition[resolvedEnemyIdx].enemyPosition[1]; populateTargetCollision(&enemy); if (isCollision()) { enemyPosition[resolvedEnemyIdx].willBeInactive = 1; hitCount++; } } } return hitCount; }