ready for push to public git?

This commit is contained in:
John Bintz 2024-03-04 17:38:03 -05:00
parent a144f4fc47
commit d0ccb689a9
19 changed files with 426 additions and 110 deletions

View File

@ -105,15 +105,15 @@ int attemptToFireRabbitBullet(
); );
} }
} else if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_BEAM_SHOT_GUN) { } else if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_BEAM_SHOT_GUN) {
// make sure three bullets are available // make sure two bullets are available
if (!maybeFireShotCount( if (!maybeFireShotCount(
rabbitBulletPosition, rabbitBulletPosition,
availableBullets, availableBullets,
4 2
)) return 0; )) return 0;
// if so, fire away // if so, fire away
for (i = 0; i < 4; ++i) { for (i = 0; i < 2; ++i) {
beamOffsetX = sin(mouseAngle * DEG2RAD) * i * 2; beamOffsetX = sin(mouseAngle * DEG2RAD) * i * 2;
beamOffsetY = -cos(mouseAngle * DEG2RAD) * i * 2; beamOffsetY = -cos(mouseAngle * DEG2RAD) * i * 2;
@ -424,7 +424,8 @@ void knockbackEnemy(
void handleRabbitBulletToEnemyCollisions( void handleRabbitBulletToEnemyCollisions(
struct BulletPosition rabbitBulletPosition[], struct BulletPosition rabbitBulletPosition[],
struct EnemyPosition enemyPosition[] struct EnemyPosition enemyPosition[],
struct RabbitWeaponry *rabbitWeaponry
) { ) {
int bulletIdx, enemyIdx, grid; int bulletIdx, enemyIdx, grid;
int resolvedBulletIdx, resolvedEnemyIdx; int resolvedBulletIdx, resolvedEnemyIdx;
@ -447,7 +448,7 @@ void handleRabbitBulletToEnemyCollisions(
populateTargetCollision(&enemy); populateTargetCollision(&enemy);
if (isCollision()) { if (isCollision()) {
enemyPosition[resolvedEnemyIdx].hitPoints--; enemyPosition[resolvedEnemyIdx].hitPoints -= rabbitWeaponry->damage;
if (enemyPosition[resolvedEnemyIdx].hitPoints < 1) { if (enemyPosition[resolvedEnemyIdx].hitPoints < 1) {
enemyPosition[resolvedEnemyIdx].willBeInactive = 1; enemyPosition[resolvedEnemyIdx].willBeInactive = 1;
enemyPosition[resolvedEnemyIdx].wasKilled = 1; enemyPosition[resolvedEnemyIdx].wasKilled = 1;
@ -543,11 +544,18 @@ void handleEnemyKills(
struct RabbitWeaponry *rabbitWeaponry struct RabbitWeaponry *rabbitWeaponry
) { ) {
int i, currentKillCount; int i, currentKillCount;
int originalDifficulty;
int healthIncrease;
currentKillCount = processEnemyKillStates(enemyPosition); currentKillCount = processEnemyKillStates(enemyPosition);
globalGameState->health += ENEMY_KILL_HEALTH_GAIN * currentKillCount; healthIncrease = globalGameState->healthGainPerKill * currentKillCount;
if (globalGameState->health > RABBIT_HEALTH_MAX) globalGameState->health = RABBIT_HEALTH_MAX; if (healthIncrease > 0) {
healthIncrease = rand() % healthIncrease + 1;
globalGameState->health += healthIncrease;
if (globalGameState->health > globalGameState->maxHealth) globalGameState->health = globalGameState->maxHealth;
}
globalGameState->kills += currentKillCount; globalGameState->kills += currentKillCount;
@ -559,11 +567,17 @@ void handleEnemyKills(
currentKillCount currentKillCount
); );
originalDifficulty = globalGameState->difficulty;
for (i = 0; i < MAX_DIFFICULTY; ++i) { for (i = 0; i < MAX_DIFFICULTY; ++i) {
if (globalGameState->kills > difficultyBands[i]) { if (globalGameState->kills > difficultyBands[i]) {
globalGameState->difficulty = i + 1; globalGameState->difficulty = i + 1;
} }
} }
if (originalDifficulty != globalGameState->difficulty) {
globalGameState->coins += (globalGameState->difficulty - originalDifficulty);
}
} }
} }

View File

@ -12,7 +12,8 @@ void advanceRabbitBullets(
void handleRabbitBulletToEnemyCollisions( void handleRabbitBulletToEnemyCollisions(
struct BulletPosition[], struct BulletPosition[],
struct EnemyPosition[] struct EnemyPosition[],
struct RabbitWeaponry*
); );
int handleRabbitToEnemyCollisions( int handleRabbitToEnemyCollisions(

11
const.c
View File

@ -3,6 +3,9 @@
int difficultyBands[MAX_DIFFICULTY]; int difficultyBands[MAX_DIFFICULTY];
int hitPointRanges[MAX_DIFFICULTY][4]; int hitPointRanges[MAX_DIFFICULTY][4];
int damageUpgradeCosts[3] = { 5, 11, 18 };
int healthUpgradeCosts[3] = { 4, 9, 15 };
int healthGainUpgradeCosts[3] = { 6, 13, 21 };
struct SpriteBounds bounds; struct SpriteBounds bounds;
@ -17,11 +20,15 @@ struct CompiledSpriteRender rabbit,
void buildDifficultyBands() { void buildDifficultyBands() {
int i; int i;
float current = BASE_KILLS; float current = 0;
float increaseBy = BASE_KILLS;
for (i = 0; i < MAX_DIFFICULTY; ++i) { for (i = 0; i < MAX_DIFFICULTY; ++i) {
current += increaseBy;
difficultyBands[i] = (int)current; difficultyBands[i] = (int)current;
current *= KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER;
increaseBy *= KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER;
} }
} }

26
const.h
View File

@ -8,6 +8,11 @@
#define ARENA_HEIGHT_TILES (10) #define ARENA_HEIGHT_TILES (10)
#define COLLISION_GRID_SIZE (ARENA_WIDTH_TILES / 2 * TILE_SIZE) #define COLLISION_GRID_SIZE (ARENA_WIDTH_TILES / 2 * TILE_SIZE)
#define ARENA_LEFT_EDGE (TILE_SIZE)
#define ARENA_TOP_EDGE (TILE_SIZE)
#define ARENA_RIGHT_EDGE (ARENA_WIDTH_TILES - 1) * TILE_SIZE
#define ARENA_BOTTOM_EDGE (ARENA_WIDTH_TILES - 1) * TILE_SIZE
#define MOUSE_DISTANCE (32) #define MOUSE_DISTANCE (32)
#define RABBIT_MOTION_DRAG (2) #define RABBIT_MOTION_DRAG (2)
@ -33,22 +38,21 @@
#define DEG2RAD (3.14159/180) #define DEG2RAD (3.14159/180)
#define ENEMY_MAX_COUNT (50) #define ENEMY_MAX_COUNT (50)
#define ENEMY_BULLET_LIMIT (ENEMY_MAX_COUNT / 4) #define ENEMY_BULLET_LIMIT (ENEMY_MAX_COUNT / 5)
#define ENEMY_MOVE_SPEED (1) #define ENEMY_MOVE_SPEED (1)
#define ENEMY_MOVE_DELAY (3) #define ENEMY_MOVE_DELAY (3)
#define ENEMY_BULLET_VELOCITY (RABBIT_BULLET_VELOCITY - 2) #define ENEMY_BULLET_VELOCITY (RABBIT_BULLET_VELOCITY - 1)
#define ENEMY_FIRE_MIN_DELAY (45) #define ENEMY_FIRE_MIN_DELAY (50)
#define ENEMY_FIRE_VARIABLE_DELAY (60) #define ENEMY_FIRE_VARIABLE_DELAY (60)
#define ENEMY_BULLET_DAMAGE (3) #define ENEMY_BULLET_DAMAGE (3)
#define ENEMY_COLLISION_DAMAGE (6) #define ENEMY_COLLISION_DAMAGE (6)
#define ENEMY_KILL_HEALTH_GAIN (1)
#define ENEMY_HIT_POINT_DIFFICULTY_INCREASE_EVERY (4) #define ENEMY_HIT_POINT_DIFFICULTY_INCREASE_EVERY (4)
#define BASE_ENEMY_SPAWN_COOLDOWN (30) #define BASE_ENEMY_SPAWN_COOLDOWN (30)
#define DIFFICULTY_SPAWN_COOLDOWN_REDUCTION (2) #define DIFFICULTY_SPAWN_COOLDOWN_REDUCTION (1)
#define MINIMUM_ENEMY_SPAWN_COOLDOWN (3) #define MINIMUM_ENEMY_SPAWN_COOLDOWN (3)
#define VARIABLE_ENEMY_SPAWN_COOLDOWN (10) #define VARIABLE_ENEMY_SPAWN_COOLDOWN (10)
@ -56,8 +60,8 @@
#define SHOTGUN_ROUNDS_PER_LEVEL (25) #define SHOTGUN_ROUNDS_PER_LEVEL (25)
#define MAX_DIFFICULTY (40) #define MAX_DIFFICULTY (40)
#define BASE_KILLS (30) #define BASE_KILLS (20)
#define KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER (1.25) #define KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER (1.06)
#define HIT_POINT_DIFFICULTY_INCREASE_DELAY (4) #define HIT_POINT_DIFFICULTY_INCREASE_DELAY (4)
#define WEAPON_TYPE_SINGLE_SHOT_GUN (0) #define WEAPON_TYPE_SINGLE_SHOT_GUN (0)
@ -69,10 +73,16 @@
#define KNOCKBACK_DISTANCE (3) #define KNOCKBACK_DISTANCE (3)
#define HEALTH_UPGRADE_GAIN (10)
extern int difficultyBands[]; extern int difficultyBands[];
extern int hitPointRanges[MAX_DIFFICULTY][4]; extern int hitPointRanges[MAX_DIFFICULTY][4];
extern struct SpriteBounds bounds;
extern int damageUpgradeCosts[];
extern int healthUpgradeCosts[];
extern int healthGainUpgradeCosts[];
extern struct SpriteBounds bounds;
extern struct CompiledSpriteRender rabbit, extern struct CompiledSpriteRender rabbit,
mouse, mouse,
bullet, bullet,

View File

@ -3,11 +3,12 @@
#include <stdio.h> #include <stdio.h>
void TestBuildDifficultyBands(CuTest *tc) { void TestBuildDifficultyBands(CuTest *tc) {
buildDifficultyBands(); buildDifficultyBands();
CuAssertIntEquals(tc, 30, difficultyBands[0]); CuAssertIntEquals(tc, 20, difficultyBands[0]);
CuAssertIntEquals(tc, 37, difficultyBands[1]); CuAssertIntEquals(tc, 41, difficultyBands[1]);
CuAssertIntEquals(tc, 46, difficultyBands[2]); CuAssertIntEquals(tc, 3095, difficultyBands[MAX_DIFFICULTY - 1]);
} }
void TestBuildHitPointRanges(CuTest *tc) { void TestBuildHitPointRanges(CuTest *tc) {

178
game.c
View File

@ -45,7 +45,13 @@ struct GlobalGameState globalGameState = {
.spawnCooldown = 0, .spawnCooldown = 0,
.difficulty = 0, .difficulty = 0,
.kills = 0, .kills = 0,
.health = RABBIT_HEALTH_MAX .coins = 0,
.health = RABBIT_HEALTH_MAX,
.maxHealth = RABBIT_HEALTH_MAX,
.healthGainPerKill = 1,
.damageUpgradeLevel = 0,
.healthUpgradeLevel = 0,
.healthGainUpgradeLevel = 0
}; };
void setupRabbitBullets() { void setupRabbitBullets() {
@ -59,6 +65,7 @@ void setupRabbitBullets() {
rabbitWeaponry.cooldown = 0; rabbitWeaponry.cooldown = 0;
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN; rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
rabbitWeaponry.currentWeaponRemainingRounds = 0; rabbitWeaponry.currentWeaponRemainingRounds = 0;
rabbitWeaponry.damage = 1;
} }
void setupPowerup() { void setupPowerup() {
@ -160,7 +167,10 @@ void renderMouse() {
mouse.x = rabbitPosition.mousePosition[0]; mouse.x = rabbitPosition.mousePosition[0];
mouse.y = rabbitPosition.mousePosition[1]; mouse.y = rabbitPosition.mousePosition[1];
drawCompiledSprite(&mouse); drawCompiledSprite(&mouse);
drawPixel(rabbitPosition.mouseDotPosition[0], rabbitPosition.mouseDotPosition[1], 2);
mouse.x = rabbitPosition.mouseDotPosition[0];
mouse.y = rabbitPosition.mouseDotPosition[1];
drawCompiledSprite(&mouse);
} }
void renderRabbit() { void renderRabbit() {
@ -234,11 +244,9 @@ void drawOnlyMouseArena() {
mouse.y = rabbitPosition.oldMousePosition[1]; mouse.y = rabbitPosition.oldMousePosition[1];
drawOnlyArenaForSprite(&mouse); drawOnlyArenaForSprite(&mouse);
bounds.top = rabbitPosition.oldMouseDotPosition[1]; mouse.x = rabbitPosition.oldMouseDotPosition[0];
bounds.bottom = rabbitPosition.oldMouseDotPosition[1]; mouse.y = rabbitPosition.oldMouseDotPosition[1];
bounds.left = rabbitPosition.oldMouseDotPosition[0]; drawOnlyArenaForSprite(&mouse);
bounds.right = rabbitPosition.oldMouseDotPosition[0];
drawOnlyArena(&bounds);
} }
void drawOnlyRabbitArena() { void drawOnlyRabbitArena() {
@ -425,7 +433,8 @@ void handleCombat() {
handleRabbitBulletToEnemyCollisions( handleRabbitBulletToEnemyCollisions(
rabbitBulletPosition, rabbitBulletPosition,
enemyPosition enemyPosition,
&rabbitWeaponry
); );
if (handleRabbitToPowerupCollision(&rabbitPosition, &playerPowerup)) { if (handleRabbitToPowerupCollision(&rabbitPosition, &playerPowerup)) {
@ -460,6 +469,123 @@ double averageSpeedCalc;
clock_t startTime; clock_t startTime;
*/ */
struct PriorStats {
int kills;
int health;
int remainingRounds;
int coins;
int damageUpgradeAvailable;
int healthUpgradeAvailable;
int healthGainUpgradeAvailable;
} priorStats = {
.kills = -1,
.health = -1,
.remainingRounds = -1,
.coins = -1,
.damageUpgradeAvailable = -1,
.healthUpgradeAvailable = -1,
.healthGainUpgradeAvailable = -1
};
struct UpgradesAvailable {
char damage;
char health;
char healthGain;
} upgradesAvailable = {
.damage = 0,
.health = 0,
.healthGain = 0,
};
void calculateUpgradesAvailable() {
if (globalGameState.damageUpgradeLevel < 3) {
upgradesAvailable.damage = globalGameState.coins >= damageUpgradeCosts[globalGameState.damageUpgradeLevel];
} else {
upgradesAvailable.damage = 0;
}
if (globalGameState.healthUpgradeLevel < 3) {
upgradesAvailable.health = globalGameState.coins >= healthUpgradeCosts[globalGameState.healthUpgradeLevel];
} else {
upgradesAvailable.health = 0;
}
if (globalGameState.healthGainUpgradeLevel < 3) {
upgradesAvailable.healthGain = globalGameState.coins >= healthGainUpgradeCosts[globalGameState.healthGainUpgradeLevel];
} else {
upgradesAvailable.healthGain = 0;
}
}
void redrawGameStats() {
char buffer[20];
if (priorStats.kills != globalGameState.kills) {
sprintf(buffer, "Hit: %d", globalGameState.kills);
renderStringToDrawBuffer(buffer, 1, 0, 210, 20);
priorStats.kills = globalGameState.kills;
}
if (priorStats.health != globalGameState.health) {
sprintf(buffer, "Health: %d ", globalGameState.health);
renderStringToDrawBuffer(buffer, 1, 0, 210, 30);
priorStats.health = globalGameState.health;
}
if (priorStats.remainingRounds != rabbitWeaponry.currentWeaponRemainingRounds) {
sprintf(buffer, "Rnds: %d ", rabbitWeaponry.currentWeaponRemainingRounds);
renderStringToDrawBuffer(buffer, 1, 0, 210, 40);
priorStats.remainingRounds = rabbitWeaponry.currentWeaponRemainingRounds;
}
if (priorStats.coins != globalGameState.coins) {
sprintf(buffer, "Coins: %d ", globalGameState.coins);
renderStringToDrawBuffer(buffer, 1, 0, 210, 50);
priorStats.coins = globalGameState.coins;
}
if (priorStats.damageUpgradeAvailable != upgradesAvailable.damage) {
if (upgradesAvailable.damage) {
sprintf(buffer, "[P] Dmg up");
} else {
sprintf(buffer, " ");
}
renderStringToDrawBuffer(buffer, 1, 0, 210, 100);
priorStats.damageUpgradeAvailable = upgradesAvailable.damage;
}
if (priorStats.healthUpgradeAvailable != upgradesAvailable.health) {
if (upgradesAvailable.health) {
sprintf(buffer, "[O] Health up");
} else {
sprintf(buffer, " ");
}
renderStringToDrawBuffer(buffer, 1, 0, 210, 110);
priorStats.healthUpgradeAvailable = upgradesAvailable.health;
}
if (priorStats.healthGainUpgradeAvailable != upgradesAvailable.healthGain) {
if (upgradesAvailable.healthGain) {
sprintf(buffer, "[I] HGain up");
} else {
sprintf(buffer, " ");
}
renderStringToDrawBuffer(buffer, 1, 0, 210, 120);
priorStats.healthGainUpgradeAvailable = upgradesAvailable.healthGain;
}
}
int main(void) { int main(void) {
byte *drawBuffer; byte *drawBuffer;
int keepRunning = 1; int keepRunning = 1;
@ -480,27 +606,37 @@ int main(void) {
enemyPosition, enemyPosition,
&rabbitPosition &rabbitPosition
); );
handleMovement();
handleRedraw();
handleCombat(); handleCombat();
handleRedraw();
handleEnemyKills( handleEnemyKills(
enemyPosition, enemyPosition,
&globalGameState, &globalGameState,
&playerPowerup, &playerPowerup,
&rabbitWeaponry &rabbitWeaponry
); );
handleMovement();
sprintf(buffer, "Hit: %d", globalGameState.kills); calculateUpgradesAvailable();
renderStringToDrawBuffer(buffer, 1, 0, 210, 20); redrawGameStats();
sprintf(buffer, "Health: %d ", globalGameState.health);
renderStringToDrawBuffer(buffer, 1, 0, 210, 30); if (keyboardKeydownState.KEY_P && upgradesAvailable.damage) {
sprintf(buffer, "Rnds: %d ", rabbitWeaponry.currentWeaponRemainingRounds); globalGameState.coins -= damageUpgradeCosts[globalGameState.damageUpgradeLevel];
renderStringToDrawBuffer(buffer, 1, 0, 210, 40); globalGameState.damageUpgradeLevel++;
sprintf(buffer, "Cool: %d ", playerPowerup.cooldown); rabbitWeaponry.damage++;
renderStringToDrawBuffer(buffer, 1, 0, 210, 50); }
sprintf(buffer, "Lvl: %d ", globalGameState.difficulty);
renderStringToDrawBuffer(buffer, 1, 0, 210, 60); if (keyboardKeydownState.KEY_O && upgradesAvailable.health) {
globalGameState.coins -= healthUpgradeCosts[globalGameState.healthUpgradeLevel];
globalGameState.healthUpgradeLevel++;
globalGameState.maxHealth += HEALTH_UPGRADE_GAIN;
globalGameState.health = globalGameState.maxHealth;
}
if (keyboardKeydownState.KEY_I && upgradesAvailable.healthGain) {
globalGameState.coins -= healthGainUpgradeCosts[globalGameState.healthGainUpgradeLevel];
globalGameState.healthGainUpgradeLevel++;
globalGameState.healthGainPerKill++;
}
waitStartVbl(); waitStartVbl();
copyDrawBufferToDisplay(); copyDrawBufferToDisplay();

7
game.h
View File

@ -11,6 +11,13 @@ struct GlobalGameState {
int spawnCooldown; int spawnCooldown;
int kills; int kills;
int health; int health;
int maxHealth;
int healthGainPerKill;
int coins;
int damageUpgradeLevel;
int healthUpgradeLevel;
int healthGainUpgradeLevel;
}; };
#endif #endif

View File

@ -9,6 +9,9 @@
#include "system/mouse_io.h" #include "system/mouse_io.h"
#include "system/pc_stuff.h" #include "system/pc_stuff.h"
#include "system/vga.h"
#define SIGN(x) ((x > 0) - (x < 0))
void captureAndLimitMousePosition( void captureAndLimitMousePosition(
struct RabbitPosition *pos, struct RabbitPosition *pos,
@ -26,43 +29,131 @@ void captureAndLimitMousePosition(
if (pos->mouseDotPosition[1] >= MOUSE_LIMIT_BOTTOM) pos->mouseDotPosition[1] = MOUSE_LIMIT_BOTTOM; if (pos->mouseDotPosition[1] >= MOUSE_LIMIT_BOTTOM) pos->mouseDotPosition[1] = MOUSE_LIMIT_BOTTOM;
} }
// now this is ray casting
struct TargetMousePosition {
int x, y;
};
void constrainMousePosition(
struct RabbitPosition *pos,
struct TargetMousePosition *target
) {
int i, didChange, which;
int currentX, currentY;
float xCrossover, yCrossover;
float maxDeltaX, maxDeltaY, fixHypotenuse, fixDistance;
float currentDeltaX, currentDeltaY;
float adjustedDeltaX, adjustedDeltaY;
int considerX, considerY;
maxDeltaX = sin(pos->mouseAngle * DEG2RAD) * MOUSE_DISTANCE;
maxDeltaY = -cos(pos->mouseAngle * DEG2RAD) * MOUSE_DISTANCE;
currentX = target->x + maxDeltaX;
currentY = target->y + maxDeltaY;
if (maxDeltaX > 0) {
currentDeltaX = ARENA_RIGHT_EDGE - target->x;
} else {
currentDeltaX = target->x - ARENA_LEFT_EDGE;
}
if (maxDeltaY > 0) {
currentDeltaY = ARENA_BOTTOM_EDGE - target->y;
} else {
currentDeltaY = target->y - ARENA_TOP_EDGE;
}
which = -1;
considerY = currentY > ARENA_BOTTOM_EDGE || currentY < ARENA_TOP_EDGE;
considerX = currentX > ARENA_RIGHT_EDGE || currentX < ARENA_LEFT_EDGE;
if (considerX || considerY) {
if (considerX && considerY) {
which = currentDeltaY < currentDeltaX;
} else {
which = considerY;
}
}
switch (which) {
case 0: // x
if (maxDeltaX > 0) {
adjustedDeltaX = ARENA_RIGHT_EDGE - target->x;
} else {
adjustedDeltaX = -(target->x - ARENA_LEFT_EDGE);
}
adjustedDeltaY = (adjustedDeltaX / sin(pos->mouseAngle * DEG2RAD)) * -cos(pos->mouseAngle * DEG2RAD);
if (fabs(adjustedDeltaY) > currentDeltaY) {
adjustedDeltaY = SIGN(adjustedDeltaY) * currentDeltaY;
}
break;
case 1: // y
if (maxDeltaY > 0) {
adjustedDeltaY = ARENA_BOTTOM_EDGE - target->y;
} else {
adjustedDeltaY = -(target->y - ARENA_TOP_EDGE);
}
adjustedDeltaX = (adjustedDeltaY / -cos(pos->mouseAngle * DEG2RAD)) * sin(pos->mouseAngle * DEG2RAD);
if (fabs(adjustedDeltaX) > currentDeltaX) {
adjustedDeltaX = SIGN(adjustedDeltaX) * currentDeltaX;
}
break;
}
if (which != -1) {
currentX = target->x + adjustedDeltaX;
currentY = target->y + adjustedDeltaY;
}
target->x = currentX;
target->y = currentY;
}
void calculateTargetAngle( void calculateTargetAngle(
struct RabbitPosition *pos struct RabbitPosition *pos
) { ) {
int targetX, targetY;
float distanceX, distanceY; float distanceX, distanceY;
float angle, fixHypotenuse, fixDistance; float angle, fixHypotenuse, fixDistance;
struct TargetMousePosition targetMousePosition;
// Position the cursor // Position the cursor
distanceX = pos->mouseDotPosition[0] - pos->rabbitPosition[0]; distanceX = pos->mouseDotPosition[0] - pos->rabbitPosition[0];
distanceY = pos->mouseDotPosition[1] - pos->rabbitPosition[1]; distanceY = pos->mouseDotPosition[1] - pos->rabbitPosition[1];
// edge case -- top left corner
if (
pos->rabbitPosition[0] == ARENA_LEFT_EDGE &&
pos->rabbitPosition[1] == ARENA_TOP_EDGE &&
distanceX == 0 &&
distanceY == 0
) {
angle = 315;
} else {
angle = atan2(distanceY, distanceX) * RAD2DEG + 90; angle = atan2(distanceY, distanceX) * RAD2DEG + 90;
if (angle < 0) angle += 360; if (angle < 0) angle += 360;
}
distanceX = sin(angle * DEG2RAD) * MOUSE_DISTANCE; pos->mouseAngle = angle;
distanceY = -cos(angle * DEG2RAD) * MOUSE_DISTANCE;
pos->oldMousePosition[0] = pos->mousePosition[0]; pos->oldMousePosition[0] = pos->mousePosition[0];
pos->oldMousePosition[1] = pos->mousePosition[1]; pos->oldMousePosition[1] = pos->mousePosition[1];
targetX = pos->rabbitPosition[0] + distanceX; targetMousePosition.x = pos->rabbitPosition[0];
targetY = pos->rabbitPosition[1] + distanceY; targetMousePosition.y = pos->rabbitPosition[1];
if (targetX > (ARENA_WIDTH_TILES - 1) * TILE_SIZE) { constrainMousePosition(pos, &targetMousePosition);
fixDistance = targetX - ((ARENA_WIDTH_TILES - 1) * TILE_SIZE);
fixHypotenuse = fixDistance / sin(angle * DEG2RAD); pos->mousePosition[0] = targetMousePosition.x;
targetX = (ARENA_WIDTH_TILES - 1) * TILE_SIZE; pos->mousePosition[1] = targetMousePosition.y;
targetY = pos->rabbitPosition[1] + -cos(angle * DEG2RAD) * -fixHypotenuse;
} }
pos->mousePosition[0] = targetX;
pos->mousePosition[1] = targetY;
pos->mouseAngle = angle;
}
#define SIGN(x) ((x > 0) - (x < 0))
void handleEnemyMovement( void handleEnemyMovement(
struct EnemyPosition enemyPosition[], struct EnemyPosition enemyPosition[],

View File

@ -22,6 +22,7 @@ struct RabbitWeaponry {
char cooldown; char cooldown;
char currentWeapon; char currentWeapon;
int currentWeaponRemainingRounds; int currentWeaponRemainingRounds;
char damage;
}; };
struct PlayerPowerup { struct PlayerPowerup {
@ -48,20 +49,20 @@ struct BulletPosition {
}; };
struct EnemyPosition { struct EnemyPosition {
char isActive; int hitPoints;
char willBeInactive;
char wasKilled;
char hitPoints;
char hasLeftGate;
char gateExitedFrom;
int enemyPosition[2]; int enemyPosition[2];
int oldEnemyPosition[2]; int oldEnemyPosition[2];
int enemyMoveDelayStep; int enemyMoveDelayStep;
int enemyFireDelayStep; int enemyFireDelayStep;
char isActive;
char willBeInactive;
char wasKilled;
char hasLeftGate;
char gateExitedFrom;
}; };
void calculateTargetAngle(struct RabbitPosition*); void calculateTargetAngle(struct RabbitPosition*);

View File

@ -3,28 +3,28 @@
#include "const.h" #include "const.h"
void TestCalculateTargetAngle_AgainstRightWall(CuTest *tc) { void TestCalculateTargetAngle_AgainstTopRight(CuTest *tc) {
struct RabbitPosition rabbitPosition; struct RabbitPosition rabbitPosition;
// mouse against very edge // mouse against very edge
rabbitPosition.mouseDotPosition[0] = 199; rabbitPosition.mouseDotPosition[0] = 199;
rabbitPosition.mouseDotPosition[1] = 30; rabbitPosition.mouseDotPosition[1] = 0;
// rabbit against wall // rabbit against wall
rabbitPosition.rabbitPosition[0] = 179; rabbitPosition.rabbitPosition[0] = 177;
rabbitPosition.rabbitPosition[1] = 80; rabbitPosition.rabbitPosition[1] = 25;
calculateTargetAngle(&rabbitPosition); calculateTargetAngle(&rabbitPosition);
// mouse should not extend (tile width - mouse width) past player // mouse should not extend (tile width - mouse width) past player
CuAssertIntEquals(tc, 180, rabbitPosition.mousePosition[0]); CuAssertIntEquals(tc, 180, rabbitPosition.mousePosition[0]);
CuAssertIntEquals(tc, 105, rabbitPosition.mousePosition[1]); CuAssertIntEquals(tc, 21, rabbitPosition.mousePosition[1]);
CuAssertIntEquals(tc, 21, rabbitPosition.mouseAngle); CuAssertIntEquals(tc, 41, rabbitPosition.mouseAngle);
} }
CuSuite *MovementGetSuite() { CuSuite *MovementGetSuite() {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, TestCalculateTargetAngle_AgainstRightWall); SUITE_ADD_TEST(suite, TestCalculateTargetAngle_AgainstTopRight);
return suite; return suite;
} }

View File

@ -11,7 +11,7 @@
int determinePowerupCooldownTime(int difficulty) { int determinePowerupCooldownTime(int difficulty) {
if (difficulty > MAX_DIFFICULTY) exit(1); if (difficulty > MAX_DIFFICULTY) exit(1);
return difficultyBands[difficulty] + rand() % difficultyBands[difficulty]; return difficulty * 10 + rand() % 20;
} }
// if every shot lands, you should run out slightly before the next powerup // if every shot lands, you should run out slightly before the next powerup
@ -20,9 +20,8 @@ int determinePowerupCooldownTime(int difficulty) {
int determineWeaponRounds(int difficulty) { int determineWeaponRounds(int difficulty) {
if (difficulty > MAX_DIFFICULTY) exit(1); if (difficulty > MAX_DIFFICULTY) exit(1);
return difficultyBands[difficulty] + return difficultyBands[difficulty] / 2 +
difficultyBands[difficulty] / 2 + rand() % (difficultyBands[difficulty] / 8);
rand() % (difficultyBands[difficulty] / 2);
} }
void processPowerupCooldown( void processPowerupCooldown(
@ -32,7 +31,7 @@ void processPowerupCooldown(
int killCount int killCount
) { ) {
if (playerPowerup->isActive) return; if (playerPowerup->isActive) return;
if (rabbitWeaponry->currentWeapon != WEAPON_TYPE_SINGLE_SHOT_GUN) return; //if (rabbitWeaponry->currentWeapon != WEAPON_TYPE_SINGLE_SHOT_GUN) return;
playerPowerup->cooldown -= killCount; playerPowerup->cooldown -= killCount;
if (playerPowerup->cooldown <= 0) { if (playerPowerup->cooldown <= 0) {
@ -40,7 +39,7 @@ void processPowerupCooldown(
playerPowerup->y = TILE_SIZE + rand() % ((ARENA_HEIGHT_TILES - 2) * TILE_SIZE); playerPowerup->y = TILE_SIZE + rand() % ((ARENA_HEIGHT_TILES - 2) * TILE_SIZE);
playerPowerup->isActive = 1; playerPowerup->isActive = 1;
playerPowerup->willBeInactive = 0; playerPowerup->willBeInactive = 0;
playerPowerup->type = 2; playerPowerup->type = rand() % 2 + 1;
playerPowerup->cooldown = determinePowerupCooldownTime(globalGameState->difficulty); playerPowerup->cooldown = determinePowerupCooldownTime(globalGameState->difficulty);
} }

View File

@ -10,17 +10,17 @@
void TestDeterminePowerupCooldownTime(CuTest *tc) { void TestDeterminePowerupCooldownTime(CuTest *tc) {
srand(1); srand(1);
CuAssertIntEquals(tc, 38, determinePowerupCooldownTime(0)); CuAssertIntEquals(tc, 18, determinePowerupCooldownTime(0));
CuAssertIntEquals(tc, 60, determinePowerupCooldownTime(1)); CuAssertIntEquals(tc, 28, determinePowerupCooldownTime(1));
CuAssertIntEquals(tc, 85, determinePowerupCooldownTime(2)); CuAssertIntEquals(tc, 33, determinePowerupCooldownTime(2));
} }
void TestDetermineWeaponRounds(CuTest *tc) { void TestDetermineWeaponRounds(CuTest *tc) {
srand(1); srand(1);
CuAssertIntEquals(tc, 53, determineWeaponRounds(0)); CuAssertIntEquals(tc, 10, determineWeaponRounds(0));
CuAssertIntEquals(tc, 71, determineWeaponRounds(1)); CuAssertIntEquals(tc, 23, determineWeaponRounds(1));
CuAssertIntEquals(tc, 85, determineWeaponRounds(2)); CuAssertIntEquals(tc, 36, determineWeaponRounds(2));
} }
@ -60,7 +60,7 @@ void TestProcessPowerupCooldown_WeaponActive(CuTest *tc) {
10 10
); );
CuAssertIntEquals(tc, 100, playerPowerup.cooldown); CuAssertIntEquals(tc, 90, playerPowerup.cooldown);
CuAssertIntEquals(tc, 0, playerPowerup.isActive); CuAssertIntEquals(tc, 0, playerPowerup.isActive);
} }
@ -108,7 +108,7 @@ void TestProcessPowerupCooldown_Triggered(CuTest *tc) {
CuAssertIntEquals(tc, 0, playerPowerup.willBeInactive); CuAssertIntEquals(tc, 0, playerPowerup.willBeInactive);
// rand // rand
CuAssertIntEquals(tc, 33, playerPowerup.cooldown); CuAssertIntEquals(tc, 15, playerPowerup.cooldown);
CuAssertIntEquals(tc, 58, playerPowerup.x); CuAssertIntEquals(tc, 58, playerPowerup.x);
CuAssertIntEquals(tc, 178, playerPowerup.y); CuAssertIntEquals(tc, 178, playerPowerup.y);
} }

View File

@ -52,7 +52,7 @@ void TestSpawnEnemy(CuTest *tc) {
// randomized values // randomized values
CuAssertIntEquals(tc, 1, enemyPosition.hitPoints); CuAssertIntEquals(tc, 1, enemyPosition.hitPoints);
CuAssertIntEquals(tc, 103, enemyPosition.enemyFireDelayStep); CuAssertIntEquals(tc, 108, enemyPosition.enemyFireDelayStep);
// from spawndetails // from spawndetails
CuAssertIntEquals(tc, 1, enemyPosition.gateExitedFrom); CuAssertIntEquals(tc, 1, enemyPosition.gateExitedFrom);

View File

@ -1391,10 +1391,10 @@ sprite_bullet_:
mov ebp, esp mov ebp, esp
mov BYTE PTR [eax + -321], 5
mov BYTE PTR [eax + -320], 5
mov BYTE PTR [eax + -1], 5
mov BYTE PTR [eax + 0], 5 mov BYTE PTR [eax + 0], 5
mov BYTE PTR [eax + 1], 5
mov BYTE PTR [eax + 320], 5
mov BYTE PTR [eax + 321], 5
pop ebp pop ebp
ret ret

View File

@ -66,9 +66,9 @@ extern void sprite_mouse(byte *);
extern void sprite_bullet(byte *); extern void sprite_bullet(byte *);
#define SPRITE_BULLET_WIDTH (4) #define SPRITE_BULLET_WIDTH (2)
#define SPRITE_BULLET_HEIGHT (4) #define SPRITE_BULLET_HEIGHT (2)
#define SPRITE_BULLET_OFFSET_X (1) #define SPRITE_BULLET_OFFSET_X (1)

View File

@ -26,8 +26,8 @@ files:
dimensions: [8, 8] dimensions: [8, 8]
offset: [4, 4] offset: [4, 4]
bullet: bullet:
position: [16, 28] position: [17, 29]
dimensions: [4, 4] dimensions: [2, 2]
offset: [1, 1] offset: [1, 1]
enemy: enemy:
position: [0, 20] position: [0, 20]

View File

@ -13,6 +13,9 @@ void populateKeyboardKeydownState() {
keyboardKeydownState.KEY_A = keystateBits[3] & 0x40; keyboardKeydownState.KEY_A = keystateBits[3] & 0x40;
keyboardKeydownState.KEY_S = keystateBits[3] & 0x80; keyboardKeydownState.KEY_S = keystateBits[3] & 0x80;
keyboardKeydownState.KEY_D = keystateBits[4] & 0x01; keyboardKeydownState.KEY_D = keystateBits[4] & 0x01;
keyboardKeydownState.KEY_I = keystateBits[2] & 0x80;
keyboardKeydownState.KEY_O = keystateBits[3] & 0x01;
keyboardKeydownState.KEY_P = keystateBits[3] & 0x02;
keyboardKeydownState.KEY_ESC = keystateBits[0] & 0x02; keyboardKeydownState.KEY_ESC = keystateBits[0] & 0x02;
} }

View File

@ -10,6 +10,9 @@ struct KeyboardKeydownState {
byte KEY_A; byte KEY_A;
byte KEY_S; byte KEY_S;
byte KEY_D; byte KEY_D;
byte KEY_I;
byte KEY_O;
byte KEY_P;
byte KEY_ESC; byte KEY_ESC;
}; };

View File

@ -2,6 +2,7 @@
#include "pc_stuff.h" #include "pc_stuff.h"
#include <malloc.h> #include <malloc.h>
#include <conio.h> #include <conio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
@ -128,26 +129,68 @@ void setVGAColors(struct VGAColor colors[], int totalColors) {
#define RENDER_STRING_FAILSAFE (80) #define RENDER_STRING_FAILSAFE (80)
struct CharRowDetails {
char c;
char color;
char backgroundColor;
char writeBackgroundColor;
char* pos;
} charRowDetails;
// Write this in inline assembler for practice!
void plotCharRow();
#pragma aux plotCharRow = \
"mov al, [edi]" \
"mov ah, 8" \
"mov bl, [edi+1]" \
"mov bh, [edi+2]" \
"mov cl, [edi+3]" \
"mov edi, [edi + 4]" \
"nextBit: mov ch, al" \
"and ch,1" \
"cmp ch,1" \
"jne background" \
"mov [edi], bl" \
"jmp maybeKeepGoing" \
"background: cmp cl,0" \
"je maybeKeepGoing" \
"mov [edi], bh" \
"maybeKeepGoing: ror al, 1" \
"inc edi" \
"dec ah" \
"jnz nextBit" \
parm [edi] \
modify [eax ebx ecx edi];
void renderStringToDrawBuffer(char* buffer, int color, int backgroundColor, int x, int y) { void renderStringToDrawBuffer(char* buffer, int color, int backgroundColor, int x, int y) {
char *bufferPos = buffer, currentCharacter, charRow; char *bufferPos = buffer, currentCharacter, charRow;
int i, fontX, fontY, currentX = x; int i, fontX, fontY, currentX = x;
int charX, fontYs[8];
charRowDetails.color = color;
if (backgroundColor == -1) {
charRowDetails.backgroundColor = 0;
}
charRowDetails.writeBackgroundColor = backgroundColor != -1;
for (fontY = 0; fontY < 8; ++fontY) {
fontYs[fontY] = (y + fontY) * VGA_DISPLAY_WIDTH;
}
charX = x;
for (i = 0; i < RENDER_STRING_FAILSAFE; ++i) { for (i = 0; i < RENDER_STRING_FAILSAFE; ++i) {
currentCharacter = *(bufferPos++); currentCharacter = *(bufferPos++);
if (!currentCharacter) break; if (!currentCharacter) break;
for (fontY = 0; fontY < 8; ++fontY) { for (fontY = 0; fontY < 8; ++fontY) {
charRow = font8x8_basic[currentCharacter][fontY]; charRowDetails.c = font8x8_basic[currentCharacter][fontY];
charRowDetails.pos = drawBuffer + fontYs[fontY] + charX;
for (fontX = 0; fontX < 8; ++fontX) { plotCharRow(&charRowDetails);
if (charRow & (1 << fontX)) { }
drawBuffer[(y + fontY) * VGA_DISPLAY_WIDTH + (i * 8 + x + fontX)] = color;
} else { charX += 8;
if (backgroundColor > -1) {
drawBuffer[(y + fontY) * VGA_DISPLAY_WIDTH + (i * 8 + x + fontX)] = backgroundColor;
}
}
}
}
} }
} }