diff --git a/combat.c b/combat.c index 2d4d26f..9eb800c 100644 --- a/combat.c +++ b/combat.c @@ -15,6 +15,7 @@ void setupBullet(struct BulletPosition *bullet, int x, int y, signed int doubleV bullet->y = y; bullet->oldX = x; bullet->oldY = y; + bullet->wallCooldown = 1; bullet->velocityXSteps[0] = doubleVelocityX / velocity; bullet->velocityXSteps[1] = doubleVelocityX - bullet->velocityXSteps[0]; @@ -35,29 +36,47 @@ void attemptToFireRabbitBullet( 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); + if (rabbitWeaponry->currentWeapon == SINGLE_SHOT_GUN) { + for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) { + if (rabbitBulletPosition[i].isActive == 0) { + okToFire = 1; + availableBullet = i; + break; + } + } - setupBullet( - &rabbitBulletPosition[availableBullet], - rabbitPosition->rabbitPosition[0], - rabbitPosition->rabbitPosition[1], - doubleVelocityX, - doubleVelocityY, - RABBIT_BULLET_VELOCITY - ); + if (!okToFire) return; + + 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 + ); + } else if (rabbitWeaponry->currentWeapon == SPREAD_SHOT_GUN) { + // make sure three bullets are available + // if so, fire away + for (i = -1; i <= 1; ++i) { + 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( @@ -75,10 +94,15 @@ void advanceRabbitBullets( 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; + 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; } @@ -348,8 +372,11 @@ void handleRabbitBulletToEnemyCollisions( populateTargetCollision(&enemy); if (isCollision()) { - enemyPosition[resolvedEnemyIdx].willBeInactive = 1; - enemyPosition[resolvedBulletIdx].wasKilled = 1; + enemyPosition[resolvedEnemyIdx].hitPoints--; + if (enemyPosition[resolvedEnemyIdx].hitPoints < 1) { + enemyPosition[resolvedEnemyIdx].willBeInactive = 1; + enemyPosition[resolvedBulletIdx].wasKilled = 1; + } rabbitBulletPosition[resolvedBulletIdx].willBeInactive = 1; break; } diff --git a/const.h b/const.h index a89aff7..afe0ff4 100644 --- a/const.h +++ b/const.h @@ -26,18 +26,19 @@ #define DEG2RAD (3.14159/180) #define ENEMY_MAX_COUNT (50) -#define ENEMY_BULLET_LIMIT (ENEMY_MAX_COUNT / 2) +#define ENEMY_BULLET_LIMIT (ENEMY_MAX_COUNT / 4) #define ENEMY_MOVE_SPEED (1) #define ENEMY_MOVE_DELAY (3) #define ENEMY_BULLET_VELOCITY (RABBIT_BULLET_VELOCITY - 2) -#define ENEMY_FIRE_MIN_DELAY (35) +#define ENEMY_FIRE_MIN_DELAY (45) #define ENEMY_FIRE_VARIABLE_DELAY (60) -#define ENEMY_BULLET_DAMAGE (4) -#define ENEMY_COLLISION_DAMAGE (8) +#define ENEMY_BULLET_DAMAGE (3) +#define ENEMY_COLLISION_DAMAGE (6) #define ENEMY_KILL_HEALTH_GAIN (1) +#define ENEMY_HIT_POINT_DIFFICULTY_INCREASE_EVERY (4) #define BASE_ENEMY_SPAWN_COOLDOWN (30) #define DIFFICULTY_SPAWN_COOLDOWN_REDUCTION (2) diff --git a/game.c b/game.c index dea9033..4e1cd49 100644 --- a/game.c +++ b/game.c @@ -16,7 +16,7 @@ #include "arena.h" #include "movement.h" #include "combat.h" - +#include "game.h" // TODO: centralize these outside of game.c struct CompiledSpriteRender rabbit, mouse, bullet, enemy, enemyBullet; @@ -29,7 +29,10 @@ struct MouseStatus mouseStatus; struct RabbitPosition rabbitPosition = { .rabbitPosition = { 60, 60 }, - .rabbitLimits = { { 20, 20 }, { 180, 180 } }, + .rabbitLimits = { + { 20, 20 }, + { (ARENA_WIDTH_TILES - 1) * TILE_SIZE - 2, (ARENA_HEIGHT_TILES - 1) * TILE_SIZE - 2 } + }, .mousePosition = { 0, 0 }, .rabbitVelocity = { 0, 0 }, .mouseDotPosition = { 0, 0 } @@ -39,19 +42,17 @@ struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT]; struct BulletPosition enemyBulletPosition[ENEMY_BULLET_LIMIT]; struct RabbitWeaponry rabbitWeaponry; -struct SpawnPointRange { - int left, width; - int top, height; -}; - struct SpawnPointRange spawnPointRanges[4] = { - { .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 }, + // top + { .left = TILE_SIZE * 4, .width = TILE_SIZE * 2, .top = TILE_SIZE - 8, .height = TILE_SIZE }, + // right + { .left = (ARENA_WIDTH_TILES - 1) * TILE_SIZE - 8, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 }, + // bottom + { .left = TILE_SIZE * 4, .width = TILE_SIZE * 2, .top = (ARENA_HEIGHT_TILES - 1) * TILE_SIZE - 8, .height = TILE_SIZE }, + // left + { .left = TILE_SIZE - 8, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 }, }; - void setupRabbitBullets() { int i; @@ -61,6 +62,7 @@ void setupRabbitBullets() { } rabbitWeaponry.cooldown = 0; + rabbitWeaponry.currentWeapon = SPREAD_SHOT_GUN; } void setupEnemyBullets() { @@ -113,8 +115,10 @@ void handleEnemyKills() { void maybeSpawnEnemy() { char canSpawn; - int i, availableEnemy; - int spawnX, spawnY; + int i, gate, availableEnemy, gatePlayerIsFacing; + int spawnX, spawnY, spawnTry; + + char buffer[20]; if (spawnCooldown-- > 0) return; @@ -128,14 +132,27 @@ void maybeSpawnEnemy() { if (!canSpawn) return; - i = rand() % 4; + // determine which gate the player is at and reduce the likelihood of + // spawning from that gate + // * 8 chances to spawn [0, 1] for each gate + // * if the gate the player is looking at gets [0], reroll + // * try three times max to limit cost of calculating + for (spawnTry = 0; spawnTry < 3; ++spawnTry) { + i = rand() % 12; + gate = i / 3; - spawnX = spawnPointRanges[i].left + rand() % spawnPointRanges[i].width; - spawnY = spawnPointRanges[i].top + rand() % spawnPointRanges[i].height; + if (gatePlayerIsFacing == gate && i % 3 != 0) continue; + } + + spawnX = spawnPointRanges[gate].left + rand() % spawnPointRanges[gate].width; + spawnY = spawnPointRanges[gate].top + rand() % spawnPointRanges[gate].height; enemyPosition[availableEnemy].isActive = 1; enemyPosition[availableEnemy].willBeInactive = 0; enemyPosition[availableEnemy].wasKilled = 0; + enemyPosition[availableEnemy].hitPoints = 1 + difficulty / ENEMY_HIT_POINT_DIFFICULTY_INCREASE_EVERY; + enemyPosition[availableEnemy].hasLeftGate = 0; + enemyPosition[availableEnemy].gateExitedFrom = gate; enemyPosition[availableEnemy].enemyMoveDelayStep = 0; enemyPosition[availableEnemy].enemyFireDelayStep = ENEMY_FIRE_MIN_DELAY + rand() % ENEMY_FIRE_VARIABLE_DELAY; enemyPosition[availableEnemy].enemyPosition[0] = spawnX; diff --git a/game.h b/game.h index 1ceb6bf..2137668 100644 --- a/game.h +++ b/game.h @@ -1,4 +1,15 @@ +#ifndef __GAME_H__ +#define __GAME_H__ + #include "system/vga.h" +struct SpawnPointRange { + int left, width; + int top, height; +}; + extern struct CompiledSpriteRender rabbit, mouse, bullet, enemy, enemyBullet; extern struct SpriteBounds bounds; +extern struct SpawnPointRange spawnPointRanges[]; + +#endif diff --git a/movement.c b/movement.c index 3fc3e7e..db2a833 100644 --- a/movement.c +++ b/movement.c @@ -1,6 +1,7 @@ #include #include +#include "game.h" #include "const.h" #include "movement.h" #include "system/mouse_io.h" @@ -55,6 +56,7 @@ void handleEnemyMovement( struct EnemyPosition enemyPosition[], struct RabbitPosition *pos ) { + char hasExitedGate; int i, xDistance, yDistance; for (i = 0; i < ENEMY_MAX_COUNT; ++i) { @@ -64,17 +66,70 @@ void handleEnemyMovement( 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; + if (enemyPosition[i].hasLeftGate) { + // which straight directon is the player closest to + xDistance = pos->rabbitPosition[0] - enemyPosition[i].enemyPosition[0]; + yDistance = pos->rabbitPosition[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; + } } else { - enemyPosition[i].enemyPosition[1] += SIGN(yDistance) * ENEMY_MOVE_SPEED; + switch (enemyPosition[i].gateExitedFrom) { + // top + case 0: + xDistance = 0; + yDistance = ENEMY_MOVE_SPEED; + break; + // right + case 1: + xDistance = -ENEMY_MOVE_SPEED; + yDistance = 0; + break; + // bottom + case 2: + xDistance = 0; + yDistance = -ENEMY_MOVE_SPEED; + break; + // left + case 3: + xDistance = ENEMY_MOVE_SPEED; + yDistance = 0; + break; + } + + enemyPosition[i].enemyPosition[0] += xDistance; + enemyPosition[i].enemyPosition[1] += yDistance; + + switch (enemyPosition[i].gateExitedFrom) { + case 0: + hasExitedGate = ( + enemyPosition[i].enemyPosition[1] > spawnPointRanges[0].top + spawnPointRanges[0].height + ); + break; + case 1: + hasExitedGate = ( + enemyPosition[i].enemyPosition[0] < spawnPointRanges[1].left + ); + break; + case 2: + hasExitedGate = ( + enemyPosition[i].enemyPosition[1] < spawnPointRanges[2].top + ); + break; + case 3: + hasExitedGate = ( + enemyPosition[i].enemyPosition[0] > spawnPointRanges[3].left + spawnPointRanges[3].width + ); + break; + } + + enemyPosition[i].hasLeftGate = hasExitedGate; } } } diff --git a/movement.h b/movement.h index 214f5e9..f11b6f5 100644 --- a/movement.h +++ b/movement.h @@ -1,3 +1,6 @@ +#ifndef __MOVEMENT_H__ +#define __MOVEMENT_H__ + #include "system/keyboard.h" struct RabbitPosition { @@ -15,8 +18,12 @@ struct RabbitPosition { int mouseAngle; }; +#define SINGLE_SHOT_GUN (0) +#define SPREAD_SHOT_GUN (1) + struct RabbitWeaponry { char cooldown; + char currentWeapon; }; struct BulletPosition { @@ -27,6 +34,8 @@ struct BulletPosition { int x, y; int oldX, oldY; + int wallCooldown; + signed int velocityXSteps[2], velocityYSteps[2]; int velocityStep; }; @@ -37,6 +46,10 @@ struct EnemyPosition { char willBeInactive; char wasKilled; + char hitPoints; + char hasLeftGate; + char gateExitedFrom; + int enemyPosition[2]; int oldEnemyPosition[2]; @@ -48,3 +61,5 @@ void calculateTargetAngle(struct RabbitPosition*, struct MouseStatus*); void captureAndLimitMousePosition(struct RabbitPosition*, struct MouseStatus*); void handleRabbitMovement(struct RabbitPosition*, struct KeyboardKeydownState*); void handleEnemyMovement(struct EnemyPosition[], struct RabbitPosition*); + +#endif