433 lines
13 KiB
C
433 lines
13 KiB
C
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#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->wallCooldown = 1;
|
|
|
|
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, i, mouseAngle;
|
|
signed int doubleVelocityX, doubleVelocityY;
|
|
int availableBullets[3];
|
|
|
|
if (rabbitWeaponry->cooldown > 0) return;
|
|
mouseAngle = rabbitPosition->mouseAngle;
|
|
|
|
rabbitWeaponry->cooldown = RABBIT_BULLET_COOLDOWN;
|
|
|
|
if (rabbitWeaponry->currentWeapon == SINGLE_SHOT_GUN) {
|
|
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
|
if (rabbitBulletPosition[i].isActive == 0) {
|
|
okToFire = 1;
|
|
availableBullets[0] = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!okToFire) return;
|
|
|
|
doubleVelocityX = sin(mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
|
|
doubleVelocityY = -cos(mouseAngle * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
|
|
|
|
setupBullet(
|
|
&rabbitBulletPosition[availableBullets[0]],
|
|
rabbitPosition->rabbitPosition[0],
|
|
rabbitPosition->rabbitPosition[1],
|
|
doubleVelocityX,
|
|
doubleVelocityY,
|
|
RABBIT_BULLET_VELOCITY
|
|
);
|
|
} else if (rabbitWeaponry->currentWeapon == SPREAD_SHOT_GUN) {
|
|
// make sure three bullets are available
|
|
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
|
if (rabbitBulletPosition[i].isActive == 0) {
|
|
availableBullets[okToFire++] = i;
|
|
if (okToFire == 3) break;
|
|
}
|
|
}
|
|
|
|
if (okToFire < 3) return;
|
|
|
|
// if so, fire away
|
|
for (i = 0; i < 3; ++i) {
|
|
doubleVelocityX = sin((mouseAngle + (i - 1) * RABBIT_BULLET_SHOTGUN_SPREAD) * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
|
|
doubleVelocityY = -cos((mouseAngle + (i - 1) * RABBIT_BULLET_SHOTGUN_SPREAD) * DEG2RAD) * (RABBIT_BULLET_VELOCITY * 2);
|
|
|
|
setupBullet(
|
|
&rabbitBulletPosition[availableBullets[i]],
|
|
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].wallCooldown == 0) {
|
|
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;
|
|
} else {
|
|
rabbitBulletPosition[i].wallCooldown--;
|
|
}
|
|
|
|
|
|
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].hitPoints--;
|
|
if (enemyPosition[resolvedEnemyIdx].hitPoints < 1) {
|
|
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;
|
|
}
|
|
|