From d0ccb689a93bc3d2b50f2697c6927e1ac93b7625 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Mon, 4 Mar 2024 17:38:03 -0500 Subject: [PATCH] ready for push to public git? --- combat.c | 32 ++++++--- combat.h | 3 +- const.c | 11 ++- const.h | 26 ++++--- const_test.c | 7 +- game.c | 178 ++++++++++++++++++++++++++++++++++++++++------ game.h | 7 ++ movement.c | 127 ++++++++++++++++++++++++++++----- movement.h | 19 ++--- movement_test.c | 14 ++-- powerup.c | 11 ++- powerup_test.c | 16 ++--- spawn_test.c | 2 +- sprites.asm | 6 +- sprites.h | 4 +- spritesheet.yml | 4 +- system/keyboard.c | 3 + system/keyboard.h | 3 + system/vga.c | 63 +++++++++++++--- 19 files changed, 426 insertions(+), 110 deletions(-) diff --git a/combat.c b/combat.c index 2d9c0e8..5ca36d5 100644 --- a/combat.c +++ b/combat.c @@ -105,15 +105,15 @@ int attemptToFireRabbitBullet( ); } } else if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_BEAM_SHOT_GUN) { - // make sure three bullets are available + // make sure two bullets are available if (!maybeFireShotCount( rabbitBulletPosition, availableBullets, - 4 + 2 )) return 0; // if so, fire away - for (i = 0; i < 4; ++i) { + for (i = 0; i < 2; ++i) { beamOffsetX = sin(mouseAngle * DEG2RAD) * i * 2; beamOffsetY = -cos(mouseAngle * DEG2RAD) * i * 2; @@ -267,8 +267,8 @@ 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]; +enemyGridIndex[4], +enemyBulletGridIndex[4]; int gridPosition[4]; #define CALCULATE_GRID_POS(x,y) ((x / COLLISION_GRID_SIZE) + (y / COLLISION_GRID_SIZE) * 2) @@ -424,7 +424,8 @@ void knockbackEnemy( void handleRabbitBulletToEnemyCollisions( struct BulletPosition rabbitBulletPosition[], - struct EnemyPosition enemyPosition[] + struct EnemyPosition enemyPosition[], + struct RabbitWeaponry *rabbitWeaponry ) { int bulletIdx, enemyIdx, grid; int resolvedBulletIdx, resolvedEnemyIdx; @@ -447,7 +448,7 @@ void handleRabbitBulletToEnemyCollisions( populateTargetCollision(&enemy); if (isCollision()) { - enemyPosition[resolvedEnemyIdx].hitPoints--; + enemyPosition[resolvedEnemyIdx].hitPoints -= rabbitWeaponry->damage; if (enemyPosition[resolvedEnemyIdx].hitPoints < 1) { enemyPosition[resolvedEnemyIdx].willBeInactive = 1; enemyPosition[resolvedEnemyIdx].wasKilled = 1; @@ -543,11 +544,18 @@ void handleEnemyKills( struct RabbitWeaponry *rabbitWeaponry ) { int i, currentKillCount; + int originalDifficulty; + int healthIncrease; currentKillCount = processEnemyKillStates(enemyPosition); - globalGameState->health += ENEMY_KILL_HEALTH_GAIN * currentKillCount; - if (globalGameState->health > RABBIT_HEALTH_MAX) globalGameState->health = RABBIT_HEALTH_MAX; + healthIncrease = globalGameState->healthGainPerKill * currentKillCount; + if (healthIncrease > 0) { + healthIncrease = rand() % healthIncrease + 1; + + globalGameState->health += healthIncrease; + if (globalGameState->health > globalGameState->maxHealth) globalGameState->health = globalGameState->maxHealth; + } globalGameState->kills += currentKillCount; @@ -559,11 +567,17 @@ void handleEnemyKills( currentKillCount ); + originalDifficulty = globalGameState->difficulty; + for (i = 0; i < MAX_DIFFICULTY; ++i) { if (globalGameState->kills > difficultyBands[i]) { globalGameState->difficulty = i + 1; } } + + if (originalDifficulty != globalGameState->difficulty) { + globalGameState->coins += (globalGameState->difficulty - originalDifficulty); + } } } diff --git a/combat.h b/combat.h index 1c90ca2..9ea6b40 100644 --- a/combat.h +++ b/combat.h @@ -12,7 +12,8 @@ void advanceRabbitBullets( void handleRabbitBulletToEnemyCollisions( struct BulletPosition[], - struct EnemyPosition[] + struct EnemyPosition[], + struct RabbitWeaponry* ); int handleRabbitToEnemyCollisions( diff --git a/const.c b/const.c index c0312d1..c0455e1 100644 --- a/const.c +++ b/const.c @@ -3,6 +3,9 @@ int difficultyBands[MAX_DIFFICULTY]; 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; @@ -17,11 +20,15 @@ struct CompiledSpriteRender rabbit, void buildDifficultyBands() { int i; - float current = BASE_KILLS; + float current = 0; + float increaseBy = BASE_KILLS; for (i = 0; i < MAX_DIFFICULTY; ++i) { + current += increaseBy; + difficultyBands[i] = (int)current; - current *= KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER; + + increaseBy *= KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER; } } diff --git a/const.h b/const.h index 9839544..0b918e9 100644 --- a/const.h +++ b/const.h @@ -8,6 +8,11 @@ #define ARENA_HEIGHT_TILES (10) #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 RABBIT_MOTION_DRAG (2) @@ -33,22 +38,21 @@ #define DEG2RAD (3.14159/180) #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_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_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) +#define DIFFICULTY_SPAWN_COOLDOWN_REDUCTION (1) #define MINIMUM_ENEMY_SPAWN_COOLDOWN (3) #define VARIABLE_ENEMY_SPAWN_COOLDOWN (10) @@ -56,8 +60,8 @@ #define SHOTGUN_ROUNDS_PER_LEVEL (25) #define MAX_DIFFICULTY (40) -#define BASE_KILLS (30) -#define KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER (1.25) +#define BASE_KILLS (20) +#define KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER (1.06) #define HIT_POINT_DIFFICULTY_INCREASE_DELAY (4) #define WEAPON_TYPE_SINGLE_SHOT_GUN (0) @@ -69,10 +73,16 @@ #define KNOCKBACK_DISTANCE (3) +#define HEALTH_UPGRADE_GAIN (10) + extern int difficultyBands[]; 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, mouse, bullet, diff --git a/const_test.c b/const_test.c index 4c25a80..4d90f18 100644 --- a/const_test.c +++ b/const_test.c @@ -3,11 +3,12 @@ #include void TestBuildDifficultyBands(CuTest *tc) { + buildDifficultyBands(); - CuAssertIntEquals(tc, 30, difficultyBands[0]); - CuAssertIntEquals(tc, 37, difficultyBands[1]); - CuAssertIntEquals(tc, 46, difficultyBands[2]); + CuAssertIntEquals(tc, 20, difficultyBands[0]); + CuAssertIntEquals(tc, 41, difficultyBands[1]); + CuAssertIntEquals(tc, 3095, difficultyBands[MAX_DIFFICULTY - 1]); } void TestBuildHitPointRanges(CuTest *tc) { diff --git a/game.c b/game.c index e4c41c8..31c343b 100644 --- a/game.c +++ b/game.c @@ -45,7 +45,13 @@ struct GlobalGameState globalGameState = { .spawnCooldown = 0, .difficulty = 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() { @@ -59,6 +65,7 @@ void setupRabbitBullets() { rabbitWeaponry.cooldown = 0; rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN; rabbitWeaponry.currentWeaponRemainingRounds = 0; + rabbitWeaponry.damage = 1; } void setupPowerup() { @@ -160,7 +167,10 @@ void renderMouse() { mouse.x = rabbitPosition.mousePosition[0]; mouse.y = rabbitPosition.mousePosition[1]; drawCompiledSprite(&mouse); - drawPixel(rabbitPosition.mouseDotPosition[0], rabbitPosition.mouseDotPosition[1], 2); + + mouse.x = rabbitPosition.mouseDotPosition[0]; + mouse.y = rabbitPosition.mouseDotPosition[1]; + drawCompiledSprite(&mouse); } void renderRabbit() { @@ -234,11 +244,9 @@ void drawOnlyMouseArena() { mouse.y = rabbitPosition.oldMousePosition[1]; drawOnlyArenaForSprite(&mouse); - bounds.top = rabbitPosition.oldMouseDotPosition[1]; - bounds.bottom = rabbitPosition.oldMouseDotPosition[1]; - bounds.left = rabbitPosition.oldMouseDotPosition[0]; - bounds.right = rabbitPosition.oldMouseDotPosition[0]; - drawOnlyArena(&bounds); + mouse.x = rabbitPosition.oldMouseDotPosition[0]; + mouse.y = rabbitPosition.oldMouseDotPosition[1]; + drawOnlyArenaForSprite(&mouse); } void drawOnlyRabbitArena() { @@ -425,7 +433,8 @@ void handleCombat() { handleRabbitBulletToEnemyCollisions( rabbitBulletPosition, - enemyPosition + enemyPosition, + &rabbitWeaponry ); if (handleRabbitToPowerupCollision(&rabbitPosition, &playerPowerup)) { @@ -460,6 +469,123 @@ double averageSpeedCalc; 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) { byte *drawBuffer; int keepRunning = 1; @@ -480,27 +606,37 @@ int main(void) { enemyPosition, &rabbitPosition ); - - handleMovement(); - handleRedraw(); handleCombat(); + handleRedraw(); handleEnemyKills( enemyPosition, &globalGameState, &playerPowerup, &rabbitWeaponry ); + handleMovement(); - sprintf(buffer, "Hit: %d", globalGameState.kills); - renderStringToDrawBuffer(buffer, 1, 0, 210, 20); - sprintf(buffer, "Health: %d ", globalGameState.health); - renderStringToDrawBuffer(buffer, 1, 0, 210, 30); - sprintf(buffer, "Rnds: %d ", rabbitWeaponry.currentWeaponRemainingRounds); - renderStringToDrawBuffer(buffer, 1, 0, 210, 40); - sprintf(buffer, "Cool: %d ", playerPowerup.cooldown); - renderStringToDrawBuffer(buffer, 1, 0, 210, 50); - sprintf(buffer, "Lvl: %d ", globalGameState.difficulty); - renderStringToDrawBuffer(buffer, 1, 0, 210, 60); + calculateUpgradesAvailable(); + redrawGameStats(); + + if (keyboardKeydownState.KEY_P && upgradesAvailable.damage) { + globalGameState.coins -= damageUpgradeCosts[globalGameState.damageUpgradeLevel]; + globalGameState.damageUpgradeLevel++; + rabbitWeaponry.damage++; + } + + 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(); copyDrawBufferToDisplay(); diff --git a/game.h b/game.h index 8d22925..cd9b697 100644 --- a/game.h +++ b/game.h @@ -11,6 +11,13 @@ struct GlobalGameState { int spawnCooldown; int kills; int health; + int maxHealth; + int healthGainPerKill; + int coins; + + int damageUpgradeLevel; + int healthUpgradeLevel; + int healthGainUpgradeLevel; }; #endif diff --git a/movement.c b/movement.c index f56e284..8057531 100644 --- a/movement.c +++ b/movement.c @@ -9,6 +9,9 @@ #include "system/mouse_io.h" #include "system/pc_stuff.h" +#include "system/vga.h" + +#define SIGN(x) ((x > 0) - (x < 0)) void captureAndLimitMousePosition( struct RabbitPosition *pos, @@ -26,43 +29,131 @@ void captureAndLimitMousePosition( 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( struct RabbitPosition *pos ) { - int targetX, targetY; float distanceX, distanceY; float angle, fixHypotenuse, fixDistance; + struct TargetMousePosition targetMousePosition; // Position the cursor distanceX = pos->mouseDotPosition[0] - pos->rabbitPosition[0]; distanceY = pos->mouseDotPosition[1] - pos->rabbitPosition[1]; - angle = atan2(distanceY, distanceX) * RAD2DEG + 90; - if (angle < 0) angle += 360; + // 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; + if (angle < 0) angle += 360; + } - distanceX = sin(angle * DEG2RAD) * MOUSE_DISTANCE; - distanceY = -cos(angle * DEG2RAD) * MOUSE_DISTANCE; + pos->mouseAngle = angle; pos->oldMousePosition[0] = pos->mousePosition[0]; pos->oldMousePosition[1] = pos->mousePosition[1]; - targetX = pos->rabbitPosition[0] + distanceX; - targetY = pos->rabbitPosition[1] + distanceY; + targetMousePosition.x = pos->rabbitPosition[0]; + targetMousePosition.y = pos->rabbitPosition[1]; - if (targetX > (ARENA_WIDTH_TILES - 1) * TILE_SIZE) { - fixDistance = targetX - ((ARENA_WIDTH_TILES - 1) * TILE_SIZE); - fixHypotenuse = fixDistance / sin(angle * DEG2RAD); - targetX = (ARENA_WIDTH_TILES - 1) * TILE_SIZE; - targetY = pos->rabbitPosition[1] + -cos(angle * DEG2RAD) * -fixHypotenuse; - } + constrainMousePosition(pos, &targetMousePosition); - pos->mousePosition[0] = targetX; - pos->mousePosition[1] = targetY; - - pos->mouseAngle = angle; + pos->mousePosition[0] = targetMousePosition.x; + pos->mousePosition[1] = targetMousePosition.y; } -#define SIGN(x) ((x > 0) - (x < 0)) void handleEnemyMovement( struct EnemyPosition enemyPosition[], diff --git a/movement.h b/movement.h index e1b742f..90272bc 100644 --- a/movement.h +++ b/movement.h @@ -22,6 +22,7 @@ struct RabbitWeaponry { char cooldown; char currentWeapon; int currentWeaponRemainingRounds; + char damage; }; struct PlayerPowerup { @@ -48,20 +49,20 @@ struct BulletPosition { }; struct EnemyPosition { - char isActive; - - char willBeInactive; - char wasKilled; - - char hitPoints; - char hasLeftGate; - char gateExitedFrom; - + int hitPoints; int enemyPosition[2]; int oldEnemyPosition[2]; int enemyMoveDelayStep; int enemyFireDelayStep; + + char isActive; + + char willBeInactive; + char wasKilled; + + char hasLeftGate; + char gateExitedFrom; }; void calculateTargetAngle(struct RabbitPosition*); diff --git a/movement_test.c b/movement_test.c index 722e789..6b7c450 100644 --- a/movement_test.c +++ b/movement_test.c @@ -3,28 +3,28 @@ #include "const.h" -void TestCalculateTargetAngle_AgainstRightWall(CuTest *tc) { +void TestCalculateTargetAngle_AgainstTopRight(CuTest *tc) { struct RabbitPosition rabbitPosition; // mouse against very edge rabbitPosition.mouseDotPosition[0] = 199; - rabbitPosition.mouseDotPosition[1] = 30; + rabbitPosition.mouseDotPosition[1] = 0; // rabbit against wall - rabbitPosition.rabbitPosition[0] = 179; - rabbitPosition.rabbitPosition[1] = 80; + rabbitPosition.rabbitPosition[0] = 177; + rabbitPosition.rabbitPosition[1] = 25; calculateTargetAngle(&rabbitPosition); // mouse should not extend (tile width - mouse width) past player CuAssertIntEquals(tc, 180, rabbitPosition.mousePosition[0]); - CuAssertIntEquals(tc, 105, rabbitPosition.mousePosition[1]); - CuAssertIntEquals(tc, 21, rabbitPosition.mouseAngle); + CuAssertIntEquals(tc, 21, rabbitPosition.mousePosition[1]); + CuAssertIntEquals(tc, 41, rabbitPosition.mouseAngle); } CuSuite *MovementGetSuite() { CuSuite *suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, TestCalculateTargetAngle_AgainstRightWall); + SUITE_ADD_TEST(suite, TestCalculateTargetAngle_AgainstTopRight); return suite; } diff --git a/powerup.c b/powerup.c index f88e876..cd737b1 100644 --- a/powerup.c +++ b/powerup.c @@ -11,7 +11,7 @@ int determinePowerupCooldownTime(int difficulty) { 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 @@ -20,9 +20,8 @@ int determinePowerupCooldownTime(int difficulty) { int determineWeaponRounds(int difficulty) { if (difficulty > MAX_DIFFICULTY) exit(1); - return difficultyBands[difficulty] + - difficultyBands[difficulty] / 2 + - rand() % (difficultyBands[difficulty] / 2); + return difficultyBands[difficulty] / 2 + + rand() % (difficultyBands[difficulty] / 8); } void processPowerupCooldown( @@ -32,7 +31,7 @@ void processPowerupCooldown( int killCount ) { 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; if (playerPowerup->cooldown <= 0) { @@ -40,7 +39,7 @@ void processPowerupCooldown( playerPowerup->y = TILE_SIZE + rand() % ((ARENA_HEIGHT_TILES - 2) * TILE_SIZE); playerPowerup->isActive = 1; playerPowerup->willBeInactive = 0; - playerPowerup->type = 2; + playerPowerup->type = rand() % 2 + 1; playerPowerup->cooldown = determinePowerupCooldownTime(globalGameState->difficulty); } diff --git a/powerup_test.c b/powerup_test.c index b39d3d2..a8a1e58 100644 --- a/powerup_test.c +++ b/powerup_test.c @@ -10,17 +10,17 @@ void TestDeterminePowerupCooldownTime(CuTest *tc) { srand(1); - CuAssertIntEquals(tc, 38, determinePowerupCooldownTime(0)); - CuAssertIntEquals(tc, 60, determinePowerupCooldownTime(1)); - CuAssertIntEquals(tc, 85, determinePowerupCooldownTime(2)); + CuAssertIntEquals(tc, 18, determinePowerupCooldownTime(0)); + CuAssertIntEquals(tc, 28, determinePowerupCooldownTime(1)); + CuAssertIntEquals(tc, 33, determinePowerupCooldownTime(2)); } void TestDetermineWeaponRounds(CuTest *tc) { srand(1); - CuAssertIntEquals(tc, 53, determineWeaponRounds(0)); - CuAssertIntEquals(tc, 71, determineWeaponRounds(1)); - CuAssertIntEquals(tc, 85, determineWeaponRounds(2)); + CuAssertIntEquals(tc, 10, determineWeaponRounds(0)); + CuAssertIntEquals(tc, 23, determineWeaponRounds(1)); + CuAssertIntEquals(tc, 36, determineWeaponRounds(2)); } @@ -60,7 +60,7 @@ void TestProcessPowerupCooldown_WeaponActive(CuTest *tc) { 10 ); - CuAssertIntEquals(tc, 100, playerPowerup.cooldown); + CuAssertIntEquals(tc, 90, playerPowerup.cooldown); CuAssertIntEquals(tc, 0, playerPowerup.isActive); } @@ -108,7 +108,7 @@ void TestProcessPowerupCooldown_Triggered(CuTest *tc) { CuAssertIntEquals(tc, 0, playerPowerup.willBeInactive); // rand - CuAssertIntEquals(tc, 33, playerPowerup.cooldown); + CuAssertIntEquals(tc, 15, playerPowerup.cooldown); CuAssertIntEquals(tc, 58, playerPowerup.x); CuAssertIntEquals(tc, 178, playerPowerup.y); } diff --git a/spawn_test.c b/spawn_test.c index 00d49d0..8723fb6 100644 --- a/spawn_test.c +++ b/spawn_test.c @@ -52,7 +52,7 @@ void TestSpawnEnemy(CuTest *tc) { // randomized values CuAssertIntEquals(tc, 1, enemyPosition.hitPoints); - CuAssertIntEquals(tc, 103, enemyPosition.enemyFireDelayStep); + CuAssertIntEquals(tc, 108, enemyPosition.enemyFireDelayStep); // from spawndetails CuAssertIntEquals(tc, 1, enemyPosition.gateExitedFrom); diff --git a/sprites.asm b/sprites.asm index 9572287..0f9533b 100644 --- a/sprites.asm +++ b/sprites.asm @@ -1391,10 +1391,10 @@ sprite_bullet_: 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 + 1], 5 - mov BYTE PTR [eax + 320], 5 - mov BYTE PTR [eax + 321], 5 pop ebp ret diff --git a/sprites.h b/sprites.h index e55f026..01b32d2 100644 --- a/sprites.h +++ b/sprites.h @@ -66,9 +66,9 @@ extern void sprite_mouse(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) diff --git a/spritesheet.yml b/spritesheet.yml index 5c6dc23..abe61b4 100644 --- a/spritesheet.yml +++ b/spritesheet.yml @@ -26,8 +26,8 @@ files: dimensions: [8, 8] offset: [4, 4] bullet: - position: [16, 28] - dimensions: [4, 4] + position: [17, 29] + dimensions: [2, 2] offset: [1, 1] enemy: position: [0, 20] diff --git a/system/keyboard.c b/system/keyboard.c index b419678..7ad7a27 100644 --- a/system/keyboard.c +++ b/system/keyboard.c @@ -13,6 +13,9 @@ void populateKeyboardKeydownState() { keyboardKeydownState.KEY_A = keystateBits[3] & 0x40; keyboardKeydownState.KEY_S = keystateBits[3] & 0x80; 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; } diff --git a/system/keyboard.h b/system/keyboard.h index e6e2f2f..71cf8f8 100644 --- a/system/keyboard.h +++ b/system/keyboard.h @@ -10,6 +10,9 @@ struct KeyboardKeydownState { byte KEY_A; byte KEY_S; byte KEY_D; + byte KEY_I; + byte KEY_O; + byte KEY_P; byte KEY_ESC; }; diff --git a/system/vga.c b/system/vga.c index d017fea..02505ce 100644 --- a/system/vga.c +++ b/system/vga.c @@ -2,6 +2,7 @@ #include "pc_stuff.h" #include #include +#include #include #include #include @@ -128,26 +129,68 @@ void setVGAColors(struct VGAColor colors[], int totalColors) { #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) { char *bufferPos = buffer, currentCharacter, charRow; 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) { currentCharacter = *(bufferPos++); if (!currentCharacter) break; 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) { - if (charRow & (1 << fontX)) { - drawBuffer[(y + fontY) * VGA_DISPLAY_WIDTH + (i * 8 + x + fontX)] = color; - } else { - if (backgroundColor > -1) { - drawBuffer[(y + fontY) * VGA_DISPLAY_WIDTH + (i * 8 + x + fontX)] = backgroundColor; - } - } - } + plotCharRow(&charRowDetails); } + + charX += 8; } }