Compare commits
10 Commits
4cc709c2a0
...
e140686bac
Author | SHA1 | Date | |
---|---|---|---|
e140686bac | |||
e8ac1cc4e8 | |||
d0ccb689a9 | |||
a144f4fc47 | |||
a9ec7c7eac | |||
7d227606d8 | |||
6e15ee3d82 | |||
64de129fc4 | |||
dd4fdad4be | |||
dd52b6da67 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,3 +10,4 @@
|
||||
*.bak
|
||||
*.lib
|
||||
capture/
|
||||
*.zip
|
||||
|
111
README.md
Normal file
111
README.md
Normal file
@ -0,0 +1,111 @@
|
||||
# DOS VGA Arena Shooter Game
|
||||
|
||||
A Smash TV/Robotron-like game to help me learn a whole bunch of things
|
||||
about game programming, DOS & PC programming, x86 assembler & C, and VGA graphics!
|
||||
|
||||
<center>
|
||||
<iframe title="DOS Arena Game -- Version 1" width="560" height="315" src="https://makertube.net/videos/embed/027d4ca0-f9ed-4f0d-b47e-15b92a197b87" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups"></iframe>
|
||||
</center>
|
||||
|
||||
## Play!
|
||||
|
||||
The latest build of the game is under Releases.
|
||||
[Send any feedback you wish](https://theindustriousrabbit.com/about) -- it's
|
||||
appreciated! -- but I really don't know when I'll get back to working on this.
|
||||
|
||||
## Setup
|
||||
|
||||
Builds are done on a host Linux system with Open Watcom 2.0 installed to
|
||||
a local directory. On my machine, that's `~/Applications/open-watcom-v2`.
|
||||
Tests and game are run in DOSBox-X.
|
||||
|
||||
### Clone and build Open Watcom 2 for DOS cross-compilation
|
||||
|
||||
You'll need to build Open Watcom 2 from source to get DOS compilation on Linux.
|
||||
[Follow the directions on their GitHub wiki](https://github.com/open-watcom/open-watcom-v2/wiki/Build).
|
||||
|
||||
You may need DOSBox installed for the Open Watcom 2 build.
|
||||
|
||||
### Modify `setup.sh` to use your local Open Watcom install
|
||||
|
||||
Changing the path of `export WATCOM` should be enough
|
||||
|
||||
### Install Ruby and the RMagick gem
|
||||
|
||||
The spritesheet builder uses Ruby and RMagick to produce the x86 assembler
|
||||
spritesheets from `spritesheet.bmp`.
|
||||
|
||||
### Install DOSBox-X
|
||||
|
||||
I used [the Flatpak version](https://flathub.org/apps/com.dosbox_x.DOSBox-X) and created a wrapper script in my `PATH`:
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
|
||||
flatpak run com.dosbox_x.DOSBox-X "$@"
|
||||
```
|
||||
|
||||
### Try building the game & tests
|
||||
|
||||
`bin/game` builds and runs `game.exe` and `bin/test` builds and runs the
|
||||
unit tests.
|
||||
|
||||
### Make a release
|
||||
|
||||
`bin/release` makes `release.zip` which contains the game and the DOS/4GW
|
||||
wrapper.
|
||||
|
||||
## What's in here?
|
||||
|
||||
### System access code
|
||||
|
||||
Everything in the `system` folder is about directly accessing the PC hardware:
|
||||
|
||||
* `keyboard.c` accepts keyboard input using a keyboard interrupt.
|
||||
* `mouse_io.c` accepts mouse input.
|
||||
* `pc_stuff.c` does various PC things with interrupts.
|
||||
* `vga.c` accesses the VGA card and has drawing routines built for
|
||||
32-bit protected mode code and using the 320x200x256 chained VGA mode (mode 10h).
|
||||
|
||||
### Unit tests
|
||||
|
||||
I'm using the [CuTest](https://github.com/ennorehling/cutest) library to write basic unit tests for the game.
|
||||
I wanted to try out C unit testing and see what libraries could work on
|
||||
retro machines, and CuTest fit the bill. The tests run directly in DOS.
|
||||
|
||||
### Compiled sprites
|
||||
|
||||
I automated the pipeline to building compiled sprites from a bitmap file.
|
||||
Originally, I was doing memory copies from that bitmap file, and no matter
|
||||
how much I optimized them, they were slow slow slow. Having Ruby build the
|
||||
sprites as a series of `mov` instructions sped up the game immensely.
|
||||
|
||||
### Inline assembler
|
||||
|
||||
There's some non-trivial inline assembler in `system/vga.c` for drawing
|
||||
font glyphs. https://github.com/dhepper/font8x8 is the source of the
|
||||
font. I probably could have gotten the performance fast enough in pure C,
|
||||
but I really wanted to try some inline assembler.
|
||||
|
||||
### Bitmap loading code
|
||||
|
||||
Before switching to compiled sprites, I wrote code to load BMP files to
|
||||
a memory location of your choosing, as well as to extract the palette for
|
||||
feeding to the VGA card.
|
||||
|
||||
### Open Watcom `wmake` Makefiles
|
||||
|
||||
The `Makefile`s are not sophisticated, but it took me quite a while to
|
||||
research Open Watcom's slightly different syntax for `Makefiles`. The GNU
|
||||
make docs didn't help too much.
|
||||
|
||||
## License
|
||||
|
||||
MIT License on the code. If you use any code verbatim, or do anything else with this,
|
||||
let me know!
|
||||
|
||||
CuTest has its own license.
|
||||
|
||||
Any art that's not the spritesheet is (C) John Bintz, all rights reserved. That
|
||||
should only be the `chicken.bmp` file that's used as an example for
|
||||
bitmap loading code.
|
@ -39,10 +39,15 @@ MovBytePtr = Data.define(:offset, :data)
|
||||
ASM_TEMPLATE = <<~ASM
|
||||
<% sprites.each do |sprite| %>
|
||||
PUBLIC <%= sprite.exported_name %><% end %>
|
||||
PUBLIC _palette
|
||||
|
||||
.386
|
||||
.model flat,c
|
||||
|
||||
.DATA
|
||||
_palette:
|
||||
<%= palette.flat_map { |color| color.to_asm_struct }.join("\n") %>
|
||||
|
||||
.CODE
|
||||
|
||||
<% sprites.each do |sprite| %>
|
||||
@ -60,11 +65,16 @@ ASM_TEMPLATE = <<~ASM
|
||||
end
|
||||
ASM
|
||||
|
||||
C_TEMPLATE = <<~C
|
||||
H_TEMPLATE = <<~H
|
||||
#ifndef __SPRITES_H__
|
||||
#define __SPRITES_H__
|
||||
|
||||
#include "types.h"
|
||||
#include "system/vga.h"
|
||||
|
||||
#define PALETTE_COLOR_COUNT (<%= palette.length %>)
|
||||
|
||||
extern struct VGAColor palette[<%= palette.length %>];
|
||||
|
||||
<% sprites.each do |sprite| %>
|
||||
extern void <%= sprite.function_name %>(byte *);
|
||||
@ -74,7 +84,7 @@ C_TEMPLATE = <<~C
|
||||
<% end %>
|
||||
|
||||
#endif
|
||||
C
|
||||
H
|
||||
|
||||
class AssemblerSprite
|
||||
def initialize(
|
||||
@ -144,9 +154,33 @@ screen = Screen.new(
|
||||
|
||||
sprite_data = []
|
||||
|
||||
palette = nil
|
||||
|
||||
VGAColor = Data.define(:red, :green, :blue) do
|
||||
def to_asm_struct
|
||||
[
|
||||
" BYTE #{red}",
|
||||
" BYTE #{green}",
|
||||
" BYTE #{blue}"
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
data["files"].each do |spritesheet|
|
||||
image = Magick::Image.read(spritesheet["file"]).first
|
||||
|
||||
unless palette
|
||||
palette = image.colors.times.map do |i|
|
||||
pixel = Magick::Pixel.from_color(image.colormap(i))
|
||||
|
||||
VGAColor.new(
|
||||
red: pixel.red >> 10,
|
||||
green: pixel.green >> 10,
|
||||
blue: pixel.blue >> 10
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
spritesheet["sprites"].each do |name, details|
|
||||
sprite_data << SpriteData.new(
|
||||
image:,
|
||||
@ -179,12 +213,13 @@ sprites = sprite_data.map do |sd|
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
File.open('sprites.asm', 'w') do |fh|
|
||||
fh.puts ERB.new(ASM_TEMPLATE).result_with_hash(sprites:)
|
||||
end
|
||||
|
||||
File.open('sprites.h', 'w') do |fh|
|
||||
fh.puts ERB.new(C_TEMPLATE).result_with_hash(sprites:)
|
||||
fh.puts ERB.new(H_TEMPLATE).result_with_hash(sprites:, palette:)
|
||||
end
|
||||
|
||||
puts "sprites.{asm,h} written"
|
||||
|
3
bin/game
Executable file
3
bin/game
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
wmake clean && wmake && dosbox-x -nolog game.exe
|
5
bin/release
Executable file
5
bin/release
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
wmake clean && wmake
|
||||
cp $WATCOM/binw/dos4gw.exe .
|
||||
zip -u release.zip game.exe dos4gw.exe
|
4
bin/test
Executable file
4
bin/test
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
wmake clean && wmake test && dosbox-x -c 132x60 -nolog test.exe
|
||||
|
@ -1,7 +0,0 @@
|
||||
wcc386.exe -q bmp_loader.c
|
||||
wcc386.exe -q mouse_io.c
|
||||
wcc386.exe -q pc_stuff.c
|
||||
wcc386.exe -q vga.c
|
||||
wcc386.exe -q keyboard.c
|
||||
wcl386.exe -q game.c bmp_load.obj mouse_io.obj pc_stuff.obj vga.obj keyboard.obj
|
||||
|
185
combat.c
185
combat.c
@ -2,11 +2,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "game.h"
|
||||
#include "system/vga.h"
|
||||
|
||||
#include "game.h"
|
||||
#include "powerup.h"
|
||||
#include "movement.h"
|
||||
#include "const.h"
|
||||
#include "system/vga.h"
|
||||
|
||||
void setupBullet(
|
||||
struct BulletPosition *bullet,
|
||||
@ -38,13 +39,33 @@ void setupBullet(
|
||||
bullet->velocityStep = 0;
|
||||
}
|
||||
|
||||
int maybeFireShotCount(
|
||||
struct BulletPosition rabbitBulletPosition[],
|
||||
int availableBullets[],
|
||||
int count
|
||||
) {
|
||||
int i, remaining = count;
|
||||
|
||||
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
||||
if (rabbitBulletPosition[i].isActive == 0) {
|
||||
availableBullets[count - remaining] = i;
|
||||
|
||||
remaining--;
|
||||
if (remaining == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
return remaining == 0;
|
||||
}
|
||||
|
||||
int attemptToFireRabbitBullet(
|
||||
struct RabbitPosition *rabbitPosition,
|
||||
struct RabbitWeaponry *rabbitWeaponry,
|
||||
struct BulletPosition rabbitBulletPosition[]
|
||||
) {
|
||||
int okToFire = 0, i, mouseAngle;
|
||||
int availableBullets[3];
|
||||
int availableBullets[4];
|
||||
float beamOffsetX, beamOffsetY;
|
||||
|
||||
if (rabbitWeaponry->cooldown > 0) return 0;
|
||||
mouseAngle = rabbitPosition->mouseAngle;
|
||||
@ -52,15 +73,11 @@ int attemptToFireRabbitBullet(
|
||||
rabbitWeaponry->cooldown = RABBIT_BULLET_COOLDOWN;
|
||||
|
||||
if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_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 0;
|
||||
if (!maybeFireShotCount(
|
||||
rabbitBulletPosition,
|
||||
availableBullets,
|
||||
1
|
||||
)) return 0;
|
||||
|
||||
setupBullet(
|
||||
&rabbitBulletPosition[availableBullets[0]],
|
||||
@ -71,14 +88,11 @@ int attemptToFireRabbitBullet(
|
||||
);
|
||||
} else if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_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 0;
|
||||
if (!maybeFireShotCount(
|
||||
rabbitBulletPosition,
|
||||
availableBullets,
|
||||
3
|
||||
)) return 0;
|
||||
|
||||
// if so, fire away
|
||||
for (i = 0; i < 3; ++i) {
|
||||
@ -90,6 +104,27 @@ int attemptToFireRabbitBullet(
|
||||
RABBIT_BULLET_VELOCITY
|
||||
);
|
||||
}
|
||||
} else if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_BEAM_SHOT_GUN) {
|
||||
// make sure two bullets are available
|
||||
if (!maybeFireShotCount(
|
||||
rabbitBulletPosition,
|
||||
availableBullets,
|
||||
2
|
||||
)) return 0;
|
||||
|
||||
// if so, fire away
|
||||
for (i = 0; i < 2; ++i) {
|
||||
beamOffsetX = sin(mouseAngle * DEG2RAD) * i * 2;
|
||||
beamOffsetY = -cos(mouseAngle * DEG2RAD) * i * 2;
|
||||
|
||||
setupBullet(
|
||||
&rabbitBulletPosition[availableBullets[i]],
|
||||
rabbitPosition->rabbitPosition[0] + beamOffsetX,
|
||||
rabbitPosition->rabbitPosition[1] + beamOffsetY,
|
||||
mouseAngle,
|
||||
RABBIT_BULLET_VELOCITY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -232,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)
|
||||
@ -261,6 +296,7 @@ void buildCollisionGrids(
|
||||
struct PlayerPowerup *playerPowerup
|
||||
) {
|
||||
int grid, i;
|
||||
struct CompiledSpriteRender *which;
|
||||
|
||||
for (grid = 0; grid < 4; ++grid) {
|
||||
rabbitBulletGridIndex[grid] = 0;
|
||||
@ -278,17 +314,17 @@ void buildCollisionGrids(
|
||||
|
||||
switch (playerPowerup->type) {
|
||||
case POWERUP_TYPE_SHOTGUN:
|
||||
shotgun.x = playerPowerup->x;
|
||||
shotgun.y = playerPowerup->y;
|
||||
determineGridPositionsForSprite(&shotgun);
|
||||
which = &shotgun;
|
||||
break;
|
||||
case POWERUP_TYPE_SHIELD_KILLER:
|
||||
shieldKiller.x = playerPowerup->x;
|
||||
shieldKiller.y = playerPowerup->y;
|
||||
determineGridPositionsForSprite(&shieldKiller);
|
||||
case POWERUP_TYPE_BEAM_WEAPON:
|
||||
which = &beam;
|
||||
break;
|
||||
}
|
||||
|
||||
which->x = playerPowerup->x;
|
||||
which->y = playerPowerup->y;
|
||||
determineGridPositionsForSprite(which);
|
||||
|
||||
for (grid = 0; grid < 4; ++grid) {
|
||||
powerupGrid[grid] = gridPosition[grid];
|
||||
}
|
||||
@ -374,8 +410,6 @@ int handleEnemyBulletToRabbitCollisions(
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
#define KNOCKBACK_DISTANCE (3)
|
||||
|
||||
void knockbackEnemy(
|
||||
struct EnemyPosition *enemy,
|
||||
int angle
|
||||
@ -390,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;
|
||||
@ -413,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;
|
||||
@ -463,6 +498,8 @@ int handleRabbitToPowerupCollision(
|
||||
struct RabbitPosition *rabbitPosition,
|
||||
struct PlayerPowerup *playerPowerup
|
||||
) {
|
||||
struct CompiledSpriteRender *which;
|
||||
|
||||
if (!playerPowerup->isActive) return 0;
|
||||
|
||||
rabbit.x = rabbitPosition->rabbitPosition[0];
|
||||
@ -471,16 +508,84 @@ int handleRabbitToPowerupCollision(
|
||||
|
||||
switch (playerPowerup->type) {
|
||||
case POWERUP_TYPE_SHOTGUN:
|
||||
shotgun.x = playerPowerup->x;
|
||||
shotgun.y = playerPowerup->y;
|
||||
populateTargetCollision(&shotgun);
|
||||
which = &shotgun;
|
||||
break;
|
||||
case POWERUP_TYPE_SHIELD_KILLER:
|
||||
shieldKiller.x = playerPowerup->x;
|
||||
shieldKiller.y = playerPowerup->y;
|
||||
populateTargetCollision(&shieldKiller);
|
||||
case POWERUP_TYPE_BEAM_WEAPON:
|
||||
which = &beam;
|
||||
break;
|
||||
}
|
||||
|
||||
which->x = playerPowerup->x;
|
||||
which->y = playerPowerup->y;
|
||||
populateTargetCollision(which);
|
||||
|
||||
return isCollision();
|
||||
}
|
||||
|
||||
int processEnemyKillStates(
|
||||
struct EnemyPosition enemyPosition[]
|
||||
) {
|
||||
int i, currentKillCount = 0;
|
||||
|
||||
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
|
||||
if (enemyPosition[i].wasKilled) {
|
||||
enemyPosition[i].wasKilled = 0;
|
||||
currentKillCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return currentKillCount;
|
||||
}
|
||||
|
||||
void handleEnemyKills(
|
||||
struct EnemyPosition enemyPosition[],
|
||||
struct GlobalGameState *globalGameState,
|
||||
struct PlayerPowerup *playerPowerup,
|
||||
struct RabbitWeaponry *rabbitWeaponry
|
||||
) {
|
||||
int i, currentKillCount;
|
||||
int originalDifficulty;
|
||||
int healthIncrease;
|
||||
|
||||
currentKillCount = processEnemyKillStates(enemyPosition);
|
||||
|
||||
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;
|
||||
|
||||
if (currentKillCount > 0) {
|
||||
processPowerupCooldown(
|
||||
playerPowerup,
|
||||
globalGameState,
|
||||
rabbitWeaponry,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fireCurrentWeaponOnce(struct RabbitWeaponry *rabbitWeaponry) {
|
||||
if (rabbitWeaponry->currentWeaponRemainingRounds > 0) {
|
||||
rabbitWeaponry->currentWeaponRemainingRounds--;
|
||||
if (rabbitWeaponry->currentWeaponRemainingRounds == 0) {
|
||||
rabbitWeaponry->currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
combat.h
18
combat.h
@ -12,7 +12,8 @@ void advanceRabbitBullets(
|
||||
|
||||
void handleRabbitBulletToEnemyCollisions(
|
||||
struct BulletPosition[],
|
||||
struct EnemyPosition[]
|
||||
struct EnemyPosition[],
|
||||
struct RabbitWeaponry*
|
||||
);
|
||||
|
||||
int handleRabbitToEnemyCollisions(
|
||||
@ -48,3 +49,18 @@ int handleRabbitToPowerupCollision(
|
||||
struct RabbitPosition *rabbitPosition,
|
||||
struct PlayerPowerup *playerPowerup
|
||||
);
|
||||
|
||||
void handleEnemyKills(
|
||||
struct EnemyPosition enemyPosition[],
|
||||
struct GlobalGameState *globalGameState,
|
||||
struct PlayerPowerup *playerPowerup,
|
||||
struct RabbitWeaponry *rabbitWeaponry
|
||||
);
|
||||
|
||||
void fireCurrentWeaponOnce(struct RabbitWeaponry*);
|
||||
|
||||
int maybeFireShotCount(
|
||||
struct BulletPosition rabbitBulletPosition[],
|
||||
int availableBullets[],
|
||||
int count
|
||||
);
|
||||
|
159
combat_test.c
Normal file
159
combat_test.c
Normal file
@ -0,0 +1,159 @@
|
||||
#include "cutest-1.5/CuTest.h"
|
||||
#include "combat.h"
|
||||
|
||||
#include "game.h"
|
||||
#include "movement.h"
|
||||
#include "const.h"
|
||||
#include "powerup.h"
|
||||
|
||||
int processEnemyKillStates(struct EnemyPosition[]);
|
||||
|
||||
void TestProcessEmenyKillStates(CuTest *tc) {
|
||||
struct EnemyPosition enemyPosition[ENEMY_MAX_COUNT];
|
||||
int i, result;
|
||||
|
||||
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
|
||||
enemyPosition[i].wasKilled = 0;
|
||||
}
|
||||
|
||||
enemyPosition[0].wasKilled = 1;
|
||||
enemyPosition[ENEMY_MAX_COUNT - 1].wasKilled = 1;
|
||||
|
||||
result = processEnemyKillStates(enemyPosition);
|
||||
|
||||
CuAssertIntEquals(tc, 2, result);
|
||||
}
|
||||
|
||||
void TestFireCurrentWeaponOnce_NoRoundsRemaining(CuTest *tc) {
|
||||
struct RabbitWeaponry rabbitWeaponry;
|
||||
|
||||
rabbitWeaponry.currentWeaponRemainingRounds = 0;
|
||||
rabbitWeaponry.currentWeapon = 99;
|
||||
|
||||
fireCurrentWeaponOnce(&rabbitWeaponry);
|
||||
|
||||
CuAssertIntEquals(tc, 99, rabbitWeaponry.currentWeapon);
|
||||
CuAssertIntEquals(tc, 0, rabbitWeaponry.currentWeaponRemainingRounds);
|
||||
}
|
||||
|
||||
void TestFireCurrentWeaponOnce_OneRoundRemaining(CuTest *tc) {
|
||||
struct RabbitWeaponry rabbitWeaponry;
|
||||
|
||||
rabbitWeaponry.currentWeaponRemainingRounds = 1;
|
||||
rabbitWeaponry.currentWeapon = 99;
|
||||
|
||||
fireCurrentWeaponOnce(&rabbitWeaponry);
|
||||
|
||||
CuAssertIntEquals(tc, WEAPON_TYPE_SINGLE_SHOT_GUN, rabbitWeaponry.currentWeapon);
|
||||
CuAssertIntEquals(tc, 0, rabbitWeaponry.currentWeaponRemainingRounds);
|
||||
}
|
||||
|
||||
void TestFireCurrentWeaponOnce_TwoRoundsRemaining(CuTest *tc) {
|
||||
struct RabbitWeaponry rabbitWeaponry;
|
||||
|
||||
rabbitWeaponry.currentWeaponRemainingRounds = 2;
|
||||
rabbitWeaponry.currentWeapon = 99;
|
||||
|
||||
fireCurrentWeaponOnce(&rabbitWeaponry);
|
||||
|
||||
CuAssertIntEquals(tc, 99, rabbitWeaponry.currentWeapon);
|
||||
CuAssertIntEquals(tc, 1, rabbitWeaponry.currentWeaponRemainingRounds);
|
||||
}
|
||||
|
||||
void TestMaybeFireShotCount_NotOK(CuTest *tc) {
|
||||
struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT];
|
||||
int availableBullets[1] = { -1 };
|
||||
int i, result;
|
||||
|
||||
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
||||
rabbitBulletPosition[i].isActive = 1;
|
||||
}
|
||||
|
||||
result = maybeFireShotCount(
|
||||
rabbitBulletPosition,
|
||||
availableBullets,
|
||||
1
|
||||
);
|
||||
|
||||
CuAssertIntEquals(tc, 0, result);
|
||||
CuAssertIntEquals(tc, availableBullets[0], -1);
|
||||
}
|
||||
|
||||
void TestMaybeFireShotCount_OK(CuTest *tc) {
|
||||
struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT];
|
||||
int availableBullets[1] = { -1 };
|
||||
int i, result;
|
||||
|
||||
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
||||
rabbitBulletPosition[i].isActive = 1;
|
||||
}
|
||||
|
||||
rabbitBulletPosition[4].isActive = 0;
|
||||
|
||||
result = maybeFireShotCount(
|
||||
rabbitBulletPosition,
|
||||
availableBullets,
|
||||
1
|
||||
);
|
||||
|
||||
CuAssertIntEquals(tc, 1, result);
|
||||
CuAssertIntEquals(tc, availableBullets[0], 4);
|
||||
}
|
||||
|
||||
void TestMaybeFireShotCount_NotEnoughAvailable(CuTest *tc) {
|
||||
struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT];
|
||||
int availableBullets[2] = { -1, -1 };
|
||||
int i, result;
|
||||
|
||||
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
||||
rabbitBulletPosition[i].isActive = 1;
|
||||
}
|
||||
|
||||
rabbitBulletPosition[4].isActive = 0;
|
||||
|
||||
result = maybeFireShotCount(
|
||||
rabbitBulletPosition,
|
||||
availableBullets,
|
||||
2
|
||||
);
|
||||
|
||||
CuAssertIntEquals(tc, 0, result);
|
||||
CuAssertIntEquals(tc, 4, availableBullets[0]);
|
||||
CuAssertIntEquals(tc, -1, availableBullets[1]);
|
||||
}
|
||||
|
||||
void TestMaybeFireShotCount_EnoughAvailable(CuTest *tc) {
|
||||
struct BulletPosition rabbitBulletPosition[RABBIT_BULLET_LIMIT];
|
||||
int availableBullets[2] = { -1, -1 };
|
||||
int i, result;
|
||||
|
||||
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
||||
rabbitBulletPosition[i].isActive = 1;
|
||||
}
|
||||
|
||||
rabbitBulletPosition[4].isActive = 0;
|
||||
rabbitBulletPosition[6].isActive = 0;
|
||||
|
||||
result = maybeFireShotCount(
|
||||
rabbitBulletPosition,
|
||||
availableBullets,
|
||||
2
|
||||
);
|
||||
|
||||
CuAssertIntEquals(tc, 1, result);
|
||||
CuAssertIntEquals(tc, 4, availableBullets[0]);
|
||||
CuAssertIntEquals(tc, 6, availableBullets[1]);
|
||||
}
|
||||
|
||||
CuSuite *CombatGetSuite() {
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, TestProcessEmenyKillStates);
|
||||
SUITE_ADD_TEST(suite, TestFireCurrentWeaponOnce_NoRoundsRemaining);
|
||||
SUITE_ADD_TEST(suite, TestFireCurrentWeaponOnce_OneRoundRemaining);
|
||||
SUITE_ADD_TEST(suite, TestFireCurrentWeaponOnce_TwoRoundsRemaining);
|
||||
SUITE_ADD_TEST(suite, TestMaybeFireShotCount_NotOK);
|
||||
SUITE_ADD_TEST(suite, TestMaybeFireShotCount_OK);
|
||||
SUITE_ADD_TEST(suite, TestMaybeFireShotCount_NotEnoughAvailable);
|
||||
SUITE_ADD_TEST(suite, TestMaybeFireShotCount_EnoughAvailable);
|
||||
return suite;
|
||||
}
|
59
const.c
Normal file
59
const.c
Normal file
@ -0,0 +1,59 @@
|
||||
#include "system/vga.h"
|
||||
#include "const.h"
|
||||
|
||||
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;
|
||||
|
||||
// TODO: centralize these outside of game.c
|
||||
struct CompiledSpriteRender rabbit,
|
||||
mouse,
|
||||
bullet,
|
||||
enemy,
|
||||
enemyBullet,
|
||||
shotgun,
|
||||
beam;
|
||||
|
||||
void buildDifficultyBands() {
|
||||
int i;
|
||||
float current = 0;
|
||||
float increaseBy = BASE_KILLS;
|
||||
|
||||
for (i = 0; i < MAX_DIFFICULTY; ++i) {
|
||||
current += increaseBy;
|
||||
|
||||
difficultyBands[i] = (int)current;
|
||||
|
||||
increaseBy *= KILLS_NEEDED_FOR_NEXT_LEVEL_MULTIPLIER;
|
||||
}
|
||||
}
|
||||
|
||||
void buildHitPointRages() {
|
||||
int currentRange[4] = { 1, 1, 1, 1 };
|
||||
int i, j, countOfCurrent = 0, tmp;
|
||||
|
||||
for (i = 0; i < MAX_DIFFICULTY; ++i) {
|
||||
for (j = 0; j < 4; ++j) {
|
||||
hitPointRanges[i][j] = currentRange[j];
|
||||
}
|
||||
|
||||
countOfCurrent++;
|
||||
if (countOfCurrent == HIT_POINT_DIFFICULTY_INCREASE_DELAY) {
|
||||
countOfCurrent = 0;
|
||||
|
||||
currentRange[3]++;
|
||||
|
||||
for (j = 3; j > 0; --j) {
|
||||
if (currentRange[j] > currentRange[j - 1]) {
|
||||
tmp = currentRange[j - 1];
|
||||
currentRange[j - 1] = currentRange[j];
|
||||
currentRange[j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
57
const.h
57
const.h
@ -1,13 +1,23 @@
|
||||
#ifndef __CONST_H__
|
||||
#define __CONST_H__
|
||||
|
||||
#include "system/vga.h"
|
||||
|
||||
#define TILE_SIZE (20)
|
||||
#define ARENA_WIDTH_TILES (10)
|
||||
#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)
|
||||
#define RABBIT_MOTION_ACCELERATION (5)
|
||||
#define RABBIT_MOTION_MAX_SPEED (12)
|
||||
#define RABBIT_MOTION_MAX_SPEED (10)
|
||||
#define RABBIT_MOTION_VELOCITY_DECAY (5)
|
||||
|
||||
#define RABBIT_BULLET_LIMIT (9)
|
||||
@ -28,25 +38,60 @@
|
||||
#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)
|
||||
|
||||
#define POWERUP_RESPAWN_COOLDOWN_PER_LEVEL (10)
|
||||
|
||||
#define SHOTGUN_ROUNDS_PER_LEVEL (25)
|
||||
#define MAX_DIFFICULTY (40)
|
||||
#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)
|
||||
#define WEAPON_TYPE_SPREAD_SHOT_GUN (1)
|
||||
#define WEAPON_TYPE_BEAM_SHOT_GUN (2)
|
||||
|
||||
#define POWERUP_TYPE_SHOTGUN (1)
|
||||
#define POWERUP_TYPE_BEAM_WEAPON (2)
|
||||
|
||||
#define KNOCKBACK_DISTANCE (3)
|
||||
|
||||
#define HEALTH_UPGRADE_GAIN (10)
|
||||
|
||||
extern int difficultyBands[];
|
||||
extern int hitPointRanges[MAX_DIFFICULTY][4];
|
||||
|
||||
extern int damageUpgradeCosts[];
|
||||
extern int healthUpgradeCosts[];
|
||||
extern int healthGainUpgradeCosts[];
|
||||
|
||||
extern struct SpriteBounds bounds;
|
||||
extern struct CompiledSpriteRender rabbit,
|
||||
mouse,
|
||||
bullet,
|
||||
enemy,
|
||||
enemyBullet,
|
||||
shotgun,
|
||||
beam;
|
||||
|
||||
void buildDifficultyBands();
|
||||
void buildHitPointRages();
|
||||
|
||||
#endif
|
||||
|
28
const_test.c
Normal file
28
const_test.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include "cutest-1.5/CuTest.h"
|
||||
#include "const.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void TestBuildDifficultyBands(CuTest *tc) {
|
||||
|
||||
buildDifficultyBands();
|
||||
|
||||
CuAssertIntEquals(tc, 20, difficultyBands[0]);
|
||||
CuAssertIntEquals(tc, 41, difficultyBands[1]);
|
||||
CuAssertIntEquals(tc, 3095, difficultyBands[MAX_DIFFICULTY - 1]);
|
||||
}
|
||||
|
||||
void TestBuildHitPointRanges(CuTest *tc) {
|
||||
buildHitPointRages();
|
||||
|
||||
CuAssertIntEquals(tc, 1, hitPointRanges[0][0]);
|
||||
CuAssertIntEquals(tc, 1, hitPointRanges[1][0]);
|
||||
CuAssertIntEquals(tc, 2, hitPointRanges[4][0]);
|
||||
CuAssertIntEquals(tc, 1, hitPointRanges[4][3]);
|
||||
}
|
||||
|
||||
CuSuite *ConstGetSuite() {
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, TestBuildDifficultyBands);
|
||||
SUITE_ADD_TEST(suite, TestBuildHitPointRanges);
|
||||
return suite;
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
@ -11,7 +14,7 @@
|
||||
* CuStr
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
char* CuStrAlloc(int size)
|
||||
char* CuStrAlloc(size_t size)
|
||||
{
|
||||
char* newStr = (char*) malloc( sizeof(char) * (size) );
|
||||
return newStr;
|
||||
@ -19,7 +22,7 @@ char* CuStrAlloc(int size)
|
||||
|
||||
char* CuStrCopy(const char* old)
|
||||
{
|
||||
int len = strlen(old);
|
||||
size_t len = strlen(old);
|
||||
char* newStr = CuStrAlloc(len + 1);
|
||||
strcpy(newStr, old);
|
||||
return newStr;
|
||||
@ -54,7 +57,7 @@ void CuStringDelete(CuString *str)
|
||||
free(str);
|
||||
}
|
||||
|
||||
void CuStringResize(CuString* str, int newSize)
|
||||
void CuStringResize(CuString* str, size_t newSize)
|
||||
{
|
||||
str->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize);
|
||||
str->size = newSize;
|
||||
@ -62,7 +65,7 @@ void CuStringResize(CuString* str, int newSize)
|
||||
|
||||
void CuStringAppend(CuString* str, const char* text)
|
||||
{
|
||||
int length;
|
||||
size_t length;
|
||||
|
||||
if (text == NULL) {
|
||||
text = "NULL";
|
||||
@ -93,9 +96,9 @@ void CuStringAppendFormat(CuString* str, const char* format, ...)
|
||||
CuStringAppend(str, buf);
|
||||
}
|
||||
|
||||
void CuStringInsert(CuString* str, const char* text, int pos)
|
||||
void CuStringInsert(CuString* str, const char* text, size_t pos)
|
||||
{
|
||||
int length = strlen(text);
|
||||
size_t length = strlen(text);
|
||||
if (pos > str->length)
|
||||
pos = str->length;
|
||||
if (str->length + length + 1 >= str->size)
|
||||
@ -114,7 +117,7 @@ void CuTestInit(CuTest* t, const char* name, TestFunction function)
|
||||
t->name = CuStrCopy(name);
|
||||
t->failed = 0;
|
||||
t->ran = 0;
|
||||
t->message = NULL;
|
||||
t->message = NULL;
|
||||
t->function = function;
|
||||
t->jumpBuf = NULL;
|
||||
}
|
||||
@ -129,6 +132,7 @@ CuTest* CuTestNew(const char* name, TestFunction function)
|
||||
void CuTestDelete(CuTest *t)
|
||||
{
|
||||
if (!t) return;
|
||||
CuStringDelete(t->message);
|
||||
free(t->name);
|
||||
free(t);
|
||||
}
|
||||
@ -153,7 +157,9 @@ static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* str
|
||||
CuStringInsert(string, buf, 0);
|
||||
|
||||
tc->failed = 1;
|
||||
tc->message = string->buffer;
|
||||
free(tc->message);
|
||||
tc->message = CuStringNew();
|
||||
CuStringAppend(tc->message, string->buffer);
|
||||
if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0);
|
||||
}
|
||||
|
||||
@ -327,7 +333,7 @@ void CuSuiteDetails(CuSuite* testSuite, CuString* details)
|
||||
{
|
||||
failCount++;
|
||||
CuStringAppendFormat(details, "%d) %s: %s\n",
|
||||
failCount, testCase->name, testCase->message);
|
||||
failCount, testCase->name, testCase->message->buffer);
|
||||
}
|
||||
}
|
||||
CuStringAppend(details, "\n!!!FAILURES!!!\n");
|
||||
|
@ -3,12 +3,13 @@
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define CUTEST_VERSION "CuTest 1.5"
|
||||
#define CUTEST_VERSION "CuTest 1.5c"
|
||||
|
||||
/* CuString */
|
||||
|
||||
char* CuStrAlloc(int size);
|
||||
char* CuStrAlloc(size_t size);
|
||||
char* CuStrCopy(const char* old);
|
||||
|
||||
#define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE)))
|
||||
@ -19,8 +20,8 @@ char* CuStrCopy(const char* old);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int length;
|
||||
int size;
|
||||
size_t length;
|
||||
size_t size;
|
||||
char* buffer;
|
||||
} CuString;
|
||||
|
||||
@ -30,8 +31,8 @@ void CuStringRead(CuString* str, const char* path);
|
||||
void CuStringAppend(CuString* str, const char* text);
|
||||
void CuStringAppendChar(CuString* str, char ch);
|
||||
void CuStringAppendFormat(CuString* str, const char* format, ...);
|
||||
void CuStringInsert(CuString* str, const char* text, int pos);
|
||||
void CuStringResize(CuString* str, int newSize);
|
||||
void CuStringInsert(CuString* str, const char* text, size_t pos);
|
||||
void CuStringResize(CuString* str, size_t newSize);
|
||||
void CuStringDelete(CuString* str);
|
||||
|
||||
/* CuTest */
|
||||
@ -46,7 +47,7 @@ struct CuTest
|
||||
TestFunction function;
|
||||
int failed;
|
||||
int ran;
|
||||
const char* message;
|
||||
CuString *message;
|
||||
jmp_buf *jumpBuf;
|
||||
};
|
||||
|
||||
@ -86,8 +87,8 @@ void CuAssertPtrEquals_LineMsg(CuTest* tc,
|
||||
#define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac))
|
||||
#define CuAssertPtrEquals_Msg(tc,ms,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac))
|
||||
|
||||
#define CuAssertPtrNotNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer unexpected",(p != NULL))
|
||||
#define CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),(p != NULL))
|
||||
#define CuAssertPtrNotNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer unexpected",((p) != NULL))
|
||||
#define CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),((p) != NULL))
|
||||
|
||||
/* CuSuite */
|
||||
|
||||
|
322
game.c
322
game.c
@ -18,16 +18,7 @@
|
||||
#include "combat.h"
|
||||
#include "game.h"
|
||||
#include "spawn.h"
|
||||
|
||||
// TODO: centralize these outside of game.c
|
||||
struct CompiledSpriteRender rabbit,
|
||||
mouse,
|
||||
bullet,
|
||||
enemy,
|
||||
enemyBullet,
|
||||
shotgun,
|
||||
shieldKiller;
|
||||
struct SpriteBounds bounds;
|
||||
#include "powerup.h"
|
||||
|
||||
struct BMPImage spritesheetImage;
|
||||
struct VGAColor vgaColors[256];
|
||||
@ -50,6 +41,19 @@ struct BulletPosition enemyBulletPosition[ENEMY_BULLET_LIMIT];
|
||||
struct RabbitWeaponry rabbitWeaponry;
|
||||
struct PlayerPowerup playerPowerup;
|
||||
|
||||
struct GlobalGameState globalGameState = {
|
||||
.spawnCooldown = 0,
|
||||
.difficulty = 0,
|
||||
.kills = 0,
|
||||
.coins = 0,
|
||||
.health = RABBIT_HEALTH_MAX,
|
||||
.maxHealth = RABBIT_HEALTH_MAX,
|
||||
.healthGainPerKill = 1,
|
||||
.damageUpgradeLevel = 0,
|
||||
.healthUpgradeLevel = 0,
|
||||
.healthGainUpgradeLevel = 0
|
||||
};
|
||||
|
||||
void setupRabbitBullets() {
|
||||
int i;
|
||||
|
||||
@ -61,12 +65,13 @@ void setupRabbitBullets() {
|
||||
rabbitWeaponry.cooldown = 0;
|
||||
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
|
||||
rabbitWeaponry.currentWeaponRemainingRounds = 0;
|
||||
rabbitWeaponry.damage = 1;
|
||||
}
|
||||
|
||||
void setupPowerup() {
|
||||
playerPowerup.x = 100;
|
||||
playerPowerup.y = 100;
|
||||
playerPowerup.cooldown = 20 + rand() % 10;
|
||||
playerPowerup.cooldown = determinePowerupCooldownTime(globalGameState.difficulty);
|
||||
playerPowerup.type = POWERUP_TYPE_SHOTGUN;
|
||||
playerPowerup.isActive = 0;
|
||||
}
|
||||
@ -89,54 +94,6 @@ void setupEnemies() {
|
||||
}
|
||||
}
|
||||
|
||||
struct GlobalGameState globalGameState = {
|
||||
.spawnCooldown = 0,
|
||||
.difficulty = 0
|
||||
};
|
||||
|
||||
int kills = 0;
|
||||
int health = RABBIT_HEALTH_MAX;
|
||||
|
||||
int difficultyBands[10] = { 10, 20, 30, 50, 80, 130, 210, 340, 550, 890 };
|
||||
|
||||
void handleEnemyKills() {
|
||||
int i, hadKill, currentKillCount;
|
||||
|
||||
currentKillCount = 0;
|
||||
|
||||
for (i = 0; i < ENEMY_MAX_COUNT; ++i) {
|
||||
if (enemyPosition[i].wasKilled) {
|
||||
enemyPosition[i].wasKilled = 0;
|
||||
kills++;
|
||||
currentKillCount++;
|
||||
health += ENEMY_KILL_HEALTH_GAIN;
|
||||
if (health > RABBIT_HEALTH_MAX) health = RABBIT_HEALTH_MAX;
|
||||
|
||||
hadKill = 1;
|
||||
}
|
||||
}
|
||||
|
||||
kills += currentKillCount;
|
||||
|
||||
playerPowerup.cooldown -= currentKillCount;
|
||||
if (playerPowerup.cooldown <= 0) {
|
||||
playerPowerup.x = TILE_SIZE + rand() % ((ARENA_WIDTH_TILES - 2) * TILE_SIZE);
|
||||
playerPowerup.y = TILE_SIZE + rand() % ((ARENA_HEIGHT_TILES - 2) * TILE_SIZE);
|
||||
playerPowerup.isActive = 1;
|
||||
|
||||
playerPowerup.cooldown = POWERUP_RESPAWN_COOLDOWN_PER_LEVEL * globalGameState.difficulty +
|
||||
rand() % (POWERUP_RESPAWN_COOLDOWN_PER_LEVEL * globalGameState.difficulty);
|
||||
}
|
||||
|
||||
if (hadKill) {
|
||||
for (i = 0; i < 10; ++i) {
|
||||
if (kills > difficultyBands[i]) {
|
||||
globalGameState.difficulty = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setupEnemySprites() {
|
||||
buildCompiledSprite(
|
||||
&sprite_enemy,
|
||||
@ -197,12 +154,12 @@ void setupPowerupSprites() {
|
||||
);
|
||||
|
||||
buildCompiledSprite(
|
||||
&sprite_shieldKiller,
|
||||
&shieldKiller,
|
||||
SPRITE_SHIELDKILLER_WIDTH,
|
||||
SPRITE_SHIELDKILLER_HEIGHT,
|
||||
SPRITE_SHIELDKILLER_OFFSET_X,
|
||||
SPRITE_SHIELDKILLER_OFFSET_Y
|
||||
&sprite_beam,
|
||||
&beam,
|
||||
SPRITE_BEAM_WIDTH,
|
||||
SPRITE_BEAM_HEIGHT,
|
||||
SPRITE_BEAM_OFFSET_X,
|
||||
SPRITE_BEAM_OFFSET_Y
|
||||
);
|
||||
}
|
||||
|
||||
@ -210,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() {
|
||||
@ -256,20 +216,22 @@ void renderEnemyBullets() {
|
||||
}
|
||||
|
||||
void renderPowerup() {
|
||||
struct CompiledSpriteRender *which;
|
||||
|
||||
if (!playerPowerup.isActive) return;
|
||||
|
||||
switch (playerPowerup.type) {
|
||||
case POWERUP_TYPE_SHOTGUN:
|
||||
shotgun.x = playerPowerup.x;
|
||||
shotgun.y = playerPowerup.y;
|
||||
drawCompiledSprite(&shotgun);
|
||||
which = &shotgun;
|
||||
break;
|
||||
case POWERUP_TYPE_SHIELD_KILLER:
|
||||
shieldKiller.x = playerPowerup.x;
|
||||
shieldKiller.y = playerPowerup.y;
|
||||
drawCompiledSprite(&shieldKiller);
|
||||
case POWERUP_TYPE_BEAM_WEAPON:
|
||||
which = &beam;
|
||||
break;
|
||||
}
|
||||
|
||||
which->x = playerPowerup.x;
|
||||
which->y = playerPowerup.y;
|
||||
drawCompiledSprite(which);
|
||||
}
|
||||
|
||||
void drawOnlyArenaForSprite(struct CompiledSpriteRender *sprite) {
|
||||
@ -282,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() {
|
||||
@ -296,21 +256,23 @@ void drawOnlyRabbitArena() {
|
||||
}
|
||||
|
||||
void drawOnlyPowerupArena() {
|
||||
struct CompiledSpriteRender *which;
|
||||
|
||||
if (!playerPowerup.isActive) return;
|
||||
|
||||
switch (playerPowerup.type) {
|
||||
case POWERUP_TYPE_SHOTGUN:
|
||||
shotgun.x = playerPowerup.x;
|
||||
shotgun.y = playerPowerup.y;
|
||||
drawOnlyArenaForSprite(&shotgun);
|
||||
which = &shotgun;
|
||||
break;
|
||||
case POWERUP_TYPE_SHIELD_KILLER:
|
||||
shieldKiller.x = playerPowerup.x;
|
||||
shieldKiller.y = playerPowerup.y;
|
||||
drawOnlyArenaForSprite(&shieldKiller);
|
||||
case POWERUP_TYPE_BEAM_WEAPON:
|
||||
which = &beam;
|
||||
break;
|
||||
}
|
||||
|
||||
which->x = playerPowerup.x;
|
||||
which->y = playerPowerup.y;
|
||||
drawOnlyArenaForSprite(which);
|
||||
|
||||
if (playerPowerup.willBeInactive) {
|
||||
playerPowerup.isActive = 0;
|
||||
}
|
||||
@ -368,11 +330,8 @@ int setupGame() {
|
||||
installKeyboardHandler();
|
||||
initializeDrawBuffer();
|
||||
|
||||
fh = fopen("sprtsht.bmp", "rb");
|
||||
if (readBMPIntoNewMemory(fh, &spritesheetImage)) return 1;
|
||||
fclose(fh);
|
||||
|
||||
spritesheetImage.transparentColor = 0;
|
||||
buildDifficultyBands();
|
||||
buildHitPointRages();
|
||||
|
||||
setupWallSprites();
|
||||
setupRabbitSprites();
|
||||
@ -384,8 +343,8 @@ int setupGame() {
|
||||
setupPowerupSprites();
|
||||
|
||||
setVideoMode(VIDEO_MODE_VGA_256);
|
||||
bmp256ColorPaletteToVGAColorPalette(&spritesheetImage, vgaColors);
|
||||
setVGAColors(vgaColors, 256);
|
||||
|
||||
setVGAColors(palette, PALETTE_COLOR_COUNT);
|
||||
|
||||
activateMouse(&mouseStatus);
|
||||
|
||||
@ -411,8 +370,7 @@ void handleMovement() {
|
||||
&mouseStatus
|
||||
);
|
||||
calculateTargetAngle(
|
||||
&rabbitPosition,
|
||||
&mouseStatus
|
||||
&rabbitPosition
|
||||
);
|
||||
}
|
||||
|
||||
@ -425,12 +383,9 @@ void handleCombat() {
|
||||
&rabbitWeaponry,
|
||||
rabbitBulletPosition
|
||||
)) {
|
||||
if (rabbitWeaponry.currentWeaponRemainingRounds > 0) {
|
||||
rabbitWeaponry.currentWeaponRemainingRounds--;
|
||||
if (rabbitWeaponry.currentWeaponRemainingRounds == 0) {
|
||||
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
|
||||
}
|
||||
}
|
||||
fireCurrentWeaponOnce(
|
||||
&rabbitWeaponry
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,7 +419,7 @@ void handleCombat() {
|
||||
);
|
||||
|
||||
if (didHitRabbit) {
|
||||
health -= ENEMY_COLLISION_DAMAGE * didHitRabbit;
|
||||
globalGameState.health -= ENEMY_COLLISION_DAMAGE * didHitRabbit;
|
||||
}
|
||||
|
||||
didHitRabbit = handleEnemyBulletToRabbitCollisions(
|
||||
@ -473,18 +428,19 @@ void handleCombat() {
|
||||
);
|
||||
|
||||
if (didHitRabbit) {
|
||||
health -= ENEMY_BULLET_DAMAGE * didHitRabbit;
|
||||
globalGameState.health -= ENEMY_BULLET_DAMAGE * didHitRabbit;
|
||||
}
|
||||
|
||||
handleRabbitBulletToEnemyCollisions(
|
||||
rabbitBulletPosition,
|
||||
enemyPosition
|
||||
enemyPosition,
|
||||
&rabbitWeaponry
|
||||
);
|
||||
|
||||
if (handleRabbitToPowerupCollision(&rabbitPosition, &playerPowerup)) {
|
||||
playerPowerup.willBeInactive = 1;
|
||||
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SPREAD_SHOT_GUN;
|
||||
rabbitWeaponry.currentWeaponRemainingRounds = (globalGameState.difficulty + 1) * SHOTGUN_ROUNDS_PER_LEVEL;
|
||||
rabbitWeaponry.currentWeapon = playerPowerup.type;
|
||||
rabbitWeaponry.currentWeaponRemainingRounds = determineWeaponRounds(globalGameState.difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
@ -513,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;
|
||||
@ -533,18 +606,37 @@ int main(void) {
|
||||
enemyPosition,
|
||||
&rabbitPosition
|
||||
);
|
||||
|
||||
handleMovement();
|
||||
handleRedraw();
|
||||
handleCombat();
|
||||
handleEnemyKills();
|
||||
handleRedraw();
|
||||
handleEnemyKills(
|
||||
enemyPosition,
|
||||
&globalGameState,
|
||||
&playerPowerup,
|
||||
&rabbitWeaponry
|
||||
);
|
||||
handleMovement();
|
||||
|
||||
sprintf(buffer, "Hit: %d", kills);
|
||||
renderStringToDrawBuffer(buffer, 1, 0, 210, 20);
|
||||
sprintf(buffer, "Health: %d ", health);
|
||||
renderStringToDrawBuffer(buffer, 1, 0, 210, 30);
|
||||
sprintf(buffer, "Rnds: %d ", rabbitWeaponry.currentWeaponRemainingRounds);
|
||||
renderStringToDrawBuffer(buffer, 1, 0, 210, 40);
|
||||
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();
|
||||
|
9
game.h
9
game.h
@ -9,6 +9,15 @@ extern struct SpriteBounds bounds;
|
||||
struct GlobalGameState {
|
||||
int difficulty;
|
||||
int spawnCooldown;
|
||||
int kills;
|
||||
int health;
|
||||
int maxHealth;
|
||||
int healthGainPerKill;
|
||||
int coins;
|
||||
|
||||
int damageUpgradeLevel;
|
||||
int healthUpgradeLevel;
|
||||
int healthGainUpgradeLevel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,2 +0,0 @@
|
||||
* Preserve video mode
|
||||
* Rename files to 8.3
|
11
makefile
11
makefile
@ -1,4 +1,5 @@
|
||||
obj = sprites.o game.o bmpload.o arena.o movement.o combat.o spawn.o
|
||||
obj = sprites.o game.o bmpload.o arena.o movement.o combat.o spawn.o const.o powerup.o
|
||||
test_obj = test.o spawn.o spawn_test.o powerup.o powerup_test.o const.o const_test.o combat.o combat_test.o movement.o movement_test.o
|
||||
|
||||
all: system/system.lib game.exe test .SYMBOLIC
|
||||
|
||||
@ -19,10 +20,14 @@ sprites.asm: sprtsht.bmp spritesheet.yml
|
||||
game.exe: $(obj) system/system.lib
|
||||
wcl386 -q -fe=game -bt=dos -l=dos4g $(obj) system/system.lib
|
||||
|
||||
test: test.c spawn_test.c spawn.c .SYMBOLIC
|
||||
wcl386 -fe=test -bt=dos -l=dos4g test.c spawn.c spawn_test.c cutest-1.5/CuTest.c
|
||||
test: $(test_obj) system/system.lib .SYMBOLIC
|
||||
wcl386 -fe=test -bt=dos -l=dos4g $(test_obj) cutest-1.5/CuTest.c system/system.lib
|
||||
|
||||
clean: .SYMBOLIC
|
||||
rm -f game.exe
|
||||
rm -f test.exe
|
||||
rm *.o
|
||||
rm *.err
|
||||
rm system/*.o
|
||||
rm system/*.lib
|
||||
rm system/*.err
|
||||
|
124
movement.c
124
movement.c
@ -1,4 +1,5 @@
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "game.h"
|
||||
@ -8,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,
|
||||
@ -25,33 +29,131 @@ void captureAndLimitMousePosition(
|
||||
if (pos->mouseDotPosition[1] >= MOUSE_LIMIT_BOTTOM) pos->mouseDotPosition[1] = MOUSE_LIMIT_BOTTOM;
|
||||
}
|
||||
|
||||
void calculateTargetAngle(
|
||||
// now this is ray casting
|
||||
struct TargetMousePosition {
|
||||
int x, y;
|
||||
};
|
||||
|
||||
void constrainMousePosition(
|
||||
struct RabbitPosition *pos,
|
||||
struct MouseStatus *mouseStatus
|
||||
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
|
||||
) {
|
||||
float distanceX, distanceY;
|
||||
float angle;
|
||||
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];
|
||||
|
||||
pos->mousePosition[0] = pos->rabbitPosition[0] + distanceX;
|
||||
pos->mousePosition[1] = pos->rabbitPosition[1] + distanceY;
|
||||
targetMousePosition.x = pos->rabbitPosition[0];
|
||||
targetMousePosition.y = pos->rabbitPosition[1];
|
||||
|
||||
pos->mouseAngle = angle;
|
||||
constrainMousePosition(pos, &targetMousePosition);
|
||||
|
||||
pos->mousePosition[0] = targetMousePosition.x;
|
||||
pos->mousePosition[1] = targetMousePosition.y;
|
||||
}
|
||||
|
||||
#define SIGN(x) ((x > 0) - (x < 0))
|
||||
|
||||
void handleEnemyMovement(
|
||||
struct EnemyPosition enemyPosition[],
|
||||
|
27
movement.h
27
movement.h
@ -18,18 +18,13 @@ struct RabbitPosition {
|
||||
int mouseAngle;
|
||||
};
|
||||
|
||||
#define WEAPON_TYPE_SINGLE_SHOT_GUN (0)
|
||||
#define WEAPON_TYPE_SPREAD_SHOT_GUN (1)
|
||||
|
||||
struct RabbitWeaponry {
|
||||
char cooldown;
|
||||
char currentWeapon;
|
||||
int currentWeaponRemainingRounds;
|
||||
char damage;
|
||||
};
|
||||
|
||||
#define POWERUP_TYPE_SHOTGUN (0)
|
||||
#define POWERUP_TYPE_SHIELD_KILLER (1)
|
||||
|
||||
struct PlayerPowerup {
|
||||
int x, y;
|
||||
int cooldown;
|
||||
@ -54,23 +49,23 @@ 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*, struct MouseStatus*);
|
||||
void calculateTargetAngle(struct RabbitPosition*);
|
||||
void captureAndLimitMousePosition(struct RabbitPosition*, struct MouseStatus*);
|
||||
void handleRabbitMovement(struct RabbitPosition*, struct KeyboardKeydownState*);
|
||||
void handleEnemyMovement(struct EnemyPosition[], struct RabbitPosition*);
|
||||
|
30
movement_test.c
Normal file
30
movement_test.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include "cutest-1.5/CuTest.h"
|
||||
#include "movement.h"
|
||||
|
||||
#include "const.h"
|
||||
|
||||
void TestCalculateTargetAngle_AgainstTopRight(CuTest *tc) {
|
||||
struct RabbitPosition rabbitPosition;
|
||||
|
||||
// mouse against very edge
|
||||
rabbitPosition.mouseDotPosition[0] = 199;
|
||||
rabbitPosition.mouseDotPosition[1] = 0;
|
||||
|
||||
// rabbit against wall
|
||||
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, 21, rabbitPosition.mousePosition[1]);
|
||||
CuAssertIntEquals(tc, 41, rabbitPosition.mouseAngle);
|
||||
}
|
||||
|
||||
CuSuite *MovementGetSuite() {
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, TestCalculateTargetAngle_AgainstTopRight);
|
||||
return suite;
|
||||
}
|
47
powerup.c
Normal file
47
powerup.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "game.h"
|
||||
#include "movement.h"
|
||||
#include "powerup.h"
|
||||
#include "const.h"
|
||||
|
||||
// powerup should spawn every other difficulty band
|
||||
// so the first spawn should happen somewhere 10 + (20 * rand())
|
||||
int determinePowerupCooldownTime(int difficulty) {
|
||||
if (difficulty > MAX_DIFFICULTY) exit(1);
|
||||
|
||||
return difficulty * 10 + rand() % 20;
|
||||
}
|
||||
|
||||
// if every shot lands, you should run out slightly before the next powerup
|
||||
// should be available
|
||||
// so for the first rounds should be difficulty(1) + difficulty(2) % rand() or so
|
||||
int determineWeaponRounds(int difficulty) {
|
||||
if (difficulty > MAX_DIFFICULTY) exit(1);
|
||||
|
||||
return difficultyBands[difficulty] / 2 +
|
||||
rand() % (difficultyBands[difficulty] / 8);
|
||||
}
|
||||
|
||||
void processPowerupCooldown(
|
||||
struct PlayerPowerup *playerPowerup,
|
||||
struct GlobalGameState *globalGameState,
|
||||
struct RabbitWeaponry *rabbitWeaponry,
|
||||
int killCount
|
||||
) {
|
||||
if (playerPowerup->isActive) return;
|
||||
//if (rabbitWeaponry->currentWeapon != WEAPON_TYPE_SINGLE_SHOT_GUN) return;
|
||||
|
||||
playerPowerup->cooldown -= killCount;
|
||||
if (playerPowerup->cooldown <= 0) {
|
||||
playerPowerup->x = TILE_SIZE + rand() % ((ARENA_WIDTH_TILES - 2) * TILE_SIZE);
|
||||
playerPowerup->y = TILE_SIZE + rand() % ((ARENA_HEIGHT_TILES - 2) * TILE_SIZE);
|
||||
playerPowerup->isActive = 1;
|
||||
playerPowerup->willBeInactive = 0;
|
||||
playerPowerup->type = rand() % 2 + 1;
|
||||
|
||||
playerPowerup->cooldown = determinePowerupCooldownTime(globalGameState->difficulty);
|
||||
}
|
||||
}
|
||||
|
16
powerup.h
Normal file
16
powerup.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __POWERUP_H__
|
||||
#define __POWERUP_H__
|
||||
|
||||
#include "game.h"
|
||||
#include "movement.h"
|
||||
|
||||
int determinePowerupCooldownTime(int difficulty);
|
||||
int determineWeaponRounds(int difficulty);
|
||||
void processPowerupCooldown(
|
||||
struct PlayerPowerup*,
|
||||
struct GlobalGameState*,
|
||||
struct RabbitWeaponry*,
|
||||
int killCount
|
||||
);
|
||||
|
||||
#endif
|
125
powerup_test.c
Normal file
125
powerup_test.c
Normal file
@ -0,0 +1,125 @@
|
||||
#include "cutest-1.5/CuTest.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "game.h"
|
||||
#include "movement.h"
|
||||
#include "powerup.h"
|
||||
#include "const.h"
|
||||
|
||||
void TestDeterminePowerupCooldownTime(CuTest *tc) {
|
||||
srand(1);
|
||||
|
||||
CuAssertIntEquals(tc, 18, determinePowerupCooldownTime(0));
|
||||
CuAssertIntEquals(tc, 28, determinePowerupCooldownTime(1));
|
||||
CuAssertIntEquals(tc, 33, determinePowerupCooldownTime(2));
|
||||
}
|
||||
|
||||
void TestDetermineWeaponRounds(CuTest *tc) {
|
||||
srand(1);
|
||||
|
||||
CuAssertIntEquals(tc, 10, determineWeaponRounds(0));
|
||||
CuAssertIntEquals(tc, 23, determineWeaponRounds(1));
|
||||
CuAssertIntEquals(tc, 36, determineWeaponRounds(2));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TestProcessPowerupCooldown_PowerupActive(CuTest *tc) {
|
||||
struct PlayerPowerup playerPowerup;
|
||||
struct GlobalGameState globalGameState;
|
||||
struct RabbitWeaponry rabbitWeaponry;
|
||||
|
||||
playerPowerup.isActive = 1;
|
||||
playerPowerup.cooldown = 100;
|
||||
|
||||
processPowerupCooldown(
|
||||
&playerPowerup,
|
||||
&globalGameState,
|
||||
&rabbitWeaponry,
|
||||
10
|
||||
);
|
||||
|
||||
CuAssertIntEquals(tc, 100, playerPowerup.cooldown);
|
||||
CuAssertIntEquals(tc, 1, playerPowerup.isActive);
|
||||
}
|
||||
|
||||
void TestProcessPowerupCooldown_WeaponActive(CuTest *tc) {
|
||||
struct PlayerPowerup playerPowerup;
|
||||
struct GlobalGameState globalGameState;
|
||||
struct RabbitWeaponry rabbitWeaponry;
|
||||
|
||||
playerPowerup.isActive = 0;
|
||||
playerPowerup.cooldown = 100;
|
||||
rabbitWeaponry.currentWeapon = 99;
|
||||
|
||||
processPowerupCooldown(
|
||||
&playerPowerup,
|
||||
&globalGameState,
|
||||
&rabbitWeaponry,
|
||||
10
|
||||
);
|
||||
|
||||
CuAssertIntEquals(tc, 90, playerPowerup.cooldown);
|
||||
CuAssertIntEquals(tc, 0, playerPowerup.isActive);
|
||||
}
|
||||
|
||||
void TestProcessPowerupCooldown_NotTriggered(CuTest *tc) {
|
||||
struct PlayerPowerup playerPowerup;
|
||||
struct GlobalGameState globalGameState;
|
||||
struct RabbitWeaponry rabbitWeaponry;
|
||||
|
||||
playerPowerup.isActive = 0;
|
||||
playerPowerup.cooldown = 100;
|
||||
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
|
||||
|
||||
processPowerupCooldown(
|
||||
&playerPowerup,
|
||||
&globalGameState,
|
||||
&rabbitWeaponry,
|
||||
10
|
||||
);
|
||||
|
||||
CuAssertIntEquals(tc, 90, playerPowerup.cooldown);
|
||||
CuAssertIntEquals(tc, 0, playerPowerup.isActive);
|
||||
}
|
||||
|
||||
void TestProcessPowerupCooldown_Triggered(CuTest *tc) {
|
||||
struct PlayerPowerup playerPowerup;
|
||||
struct GlobalGameState globalGameState;
|
||||
struct RabbitWeaponry rabbitWeaponry;
|
||||
|
||||
srand(1);
|
||||
|
||||
playerPowerup.isActive = 0;
|
||||
playerPowerup.cooldown = 100;
|
||||
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
|
||||
playerPowerup.willBeInactive = 1;
|
||||
globalGameState.difficulty = 0;
|
||||
|
||||
processPowerupCooldown(
|
||||
&playerPowerup,
|
||||
&globalGameState,
|
||||
&rabbitWeaponry,
|
||||
101
|
||||
);
|
||||
|
||||
CuAssertIntEquals(tc, 1, playerPowerup.isActive);
|
||||
CuAssertIntEquals(tc, 0, playerPowerup.willBeInactive);
|
||||
|
||||
// rand
|
||||
CuAssertIntEquals(tc, 15, playerPowerup.cooldown);
|
||||
CuAssertIntEquals(tc, 58, playerPowerup.x);
|
||||
CuAssertIntEquals(tc, 178, playerPowerup.y);
|
||||
}
|
||||
|
||||
CuSuite *PowerupGetSuite() {
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, TestDeterminePowerupCooldownTime);
|
||||
SUITE_ADD_TEST(suite, TestDetermineWeaponRounds);
|
||||
SUITE_ADD_TEST(suite, TestProcessPowerupCooldown_PowerupActive);
|
||||
SUITE_ADD_TEST(suite, TestProcessPowerupCooldown_WeaponActive);
|
||||
SUITE_ADD_TEST(suite, TestProcessPowerupCooldown_NotTriggered);
|
||||
SUITE_ADD_TEST(suite, TestProcessPowerupCooldown_Triggered);
|
||||
return suite;
|
||||
}
|
15
spawn.c
15
spawn.c
@ -17,21 +17,8 @@ struct SpawnPointRange spawnPointRanges[4] = {
|
||||
{ .left = TILE_SIZE - 8, .width = TILE_SIZE, .top = TILE_SIZE * 4, .height = TILE_SIZE * 2 },
|
||||
};
|
||||
|
||||
int HIT_POINT_RANGES[10][4] = {
|
||||
{ 1, 1, 1, 1 },
|
||||
{ 1, 1, 1, 1 },
|
||||
{ 1, 1, 1, 2 },
|
||||
{ 1, 1, 1, 2 },
|
||||
{ 1, 1, 2, 2 },
|
||||
{ 1, 2, 2, 2 },
|
||||
{ 1, 2, 2, 2 },
|
||||
{ 1, 2, 2, 3 },
|
||||
{ 1, 2, 2, 3 },
|
||||
{ 1, 2, 3, 3 }
|
||||
};
|
||||
|
||||
int determineEnemyHitPointCalculation(int difficulty, int roll) {
|
||||
return HIT_POINT_RANGES[difficulty][roll];
|
||||
return hitPointRanges[difficulty][roll];
|
||||
}
|
||||
|
||||
void selectEnemySpawnLocation(
|
||||
|
16
spawn_test.c
16
spawn_test.c
@ -39,17 +39,6 @@ void TestSelectEnemySpawnLocation(CuTest *tc) {
|
||||
CuAssertIntEquals(tc, 91, spawnDetails.spawnY);
|
||||
}
|
||||
|
||||
void TestDetermineEnemyHitPointCalculation(CuTest *tc) {
|
||||
srand(1);
|
||||
|
||||
CuAssertIntEquals(tc, 1, determineEnemyHitPointCalculation(0, 0));
|
||||
CuAssertIntEquals(tc, 1, determineEnemyHitPointCalculation(0, 3));
|
||||
|
||||
CuAssertIntEquals(tc, 1, determineEnemyHitPointCalculation(2, 0));
|
||||
CuAssertIntEquals(tc, 1, determineEnemyHitPointCalculation(2, 2));
|
||||
CuAssertIntEquals(tc, 2, determineEnemyHitPointCalculation(2, 3));
|
||||
}
|
||||
|
||||
void TestSpawnEnemy(CuTest *tc) {
|
||||
srand(1);
|
||||
|
||||
@ -62,8 +51,8 @@ void TestSpawnEnemy(CuTest *tc) {
|
||||
spawnEnemy(&spawnDetails, &globalGameState);
|
||||
|
||||
// randomized values
|
||||
CuAssertIntEquals(tc, 2, enemyPosition.hitPoints);
|
||||
CuAssertIntEquals(tc, 103, enemyPosition.enemyFireDelayStep);
|
||||
CuAssertIntEquals(tc, 1, enemyPosition.hitPoints);
|
||||
CuAssertIntEquals(tc, 108, enemyPosition.enemyFireDelayStep);
|
||||
|
||||
// from spawndetails
|
||||
CuAssertIntEquals(tc, 1, enemyPosition.gateExitedFrom);
|
||||
@ -76,7 +65,6 @@ void TestSpawnEnemy(CuTest *tc) {
|
||||
CuSuite *SpawnGetSuite() {
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, TestSelectEnemySpawnLocation);
|
||||
SUITE_ADD_TEST(suite, TestDetermineEnemyHitPointCalculation);
|
||||
SUITE_ADD_TEST(suite, TestSpawnEnemy);
|
||||
return suite;
|
||||
}
|
||||
|
209
sprites.asm
209
sprites.asm
@ -7,11 +7,66 @@ PUBLIC sprite_mouse_
|
||||
PUBLIC sprite_bullet_
|
||||
PUBLIC sprite_enemy_
|
||||
PUBLIC sprite_shotgun_
|
||||
PUBLIC sprite_shieldKiller_
|
||||
PUBLIC sprite_beam_
|
||||
PUBLIC _palette
|
||||
|
||||
.386
|
||||
.model flat,c
|
||||
|
||||
.DATA
|
||||
_palette:
|
||||
BYTE 63
|
||||
BYTE 0
|
||||
BYTE 63
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 15
|
||||
BYTE 16
|
||||
BYTE 49
|
||||
BYTE 16
|
||||
BYTE 16
|
||||
BYTE 33
|
||||
BYTE 16
|
||||
BYTE 16
|
||||
BYTE 16
|
||||
BYTE 63
|
||||
BYTE 63
|
||||
BYTE 63
|
||||
BYTE 38
|
||||
BYTE 31
|
||||
BYTE 12
|
||||
BYTE 63
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
BYTE 0
|
||||
|
||||
.CODE
|
||||
|
||||
|
||||
@ -1336,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
|
||||
@ -1559,11 +1614,155 @@ sprite_shotgun_:
|
||||
ret
|
||||
|
||||
|
||||
sprite_shieldKiller_:
|
||||
sprite_beam_:
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
|
||||
mov BYTE PTR [eax + -1926], 7
|
||||
mov BYTE PTR [eax + -1925], 7
|
||||
mov BYTE PTR [eax + -1924], 7
|
||||
mov BYTE PTR [eax + -1923], 7
|
||||
mov BYTE PTR [eax + -1922], 7
|
||||
mov BYTE PTR [eax + -1921], 7
|
||||
mov BYTE PTR [eax + -1920], 7
|
||||
mov BYTE PTR [eax + -1919], 7
|
||||
mov BYTE PTR [eax + -1918], 7
|
||||
mov BYTE PTR [eax + -1917], 7
|
||||
mov BYTE PTR [eax + -1916], 7
|
||||
mov BYTE PTR [eax + -1915], 7
|
||||
mov BYTE PTR [eax + -1606], 7
|
||||
mov BYTE PTR [eax + -1605], 7
|
||||
mov BYTE PTR [eax + -1604], 7
|
||||
mov BYTE PTR [eax + -1603], 7
|
||||
mov BYTE PTR [eax + -1602], 7
|
||||
mov BYTE PTR [eax + -1601], 7
|
||||
mov BYTE PTR [eax + -1600], 7
|
||||
mov BYTE PTR [eax + -1599], 7
|
||||
mov BYTE PTR [eax + -1598], 7
|
||||
mov BYTE PTR [eax + -1597], 7
|
||||
mov BYTE PTR [eax + -1596], 7
|
||||
mov BYTE PTR [eax + -1595], 7
|
||||
mov BYTE PTR [eax + -1286], 7
|
||||
mov BYTE PTR [eax + -1285], 7
|
||||
mov BYTE PTR [eax + -1284], 7
|
||||
mov BYTE PTR [eax + -1283], 7
|
||||
mov BYTE PTR [eax + -1282], 7
|
||||
mov BYTE PTR [eax + -1281], 7
|
||||
mov BYTE PTR [eax + -1280], 7
|
||||
mov BYTE PTR [eax + -1279], 7
|
||||
mov BYTE PTR [eax + -1278], 7
|
||||
mov BYTE PTR [eax + -1277], 7
|
||||
mov BYTE PTR [eax + -1276], 7
|
||||
mov BYTE PTR [eax + -1275], 7
|
||||
mov BYTE PTR [eax + -966], 7
|
||||
mov BYTE PTR [eax + -965], 7
|
||||
mov BYTE PTR [eax + -964], 7
|
||||
mov BYTE PTR [eax + -963], 7
|
||||
mov BYTE PTR [eax + -962], 7
|
||||
mov BYTE PTR [eax + -961], 7
|
||||
mov BYTE PTR [eax + -960], 7
|
||||
mov BYTE PTR [eax + -959], 7
|
||||
mov BYTE PTR [eax + -958], 7
|
||||
mov BYTE PTR [eax + -957], 7
|
||||
mov BYTE PTR [eax + -956], 7
|
||||
mov BYTE PTR [eax + -955], 7
|
||||
mov BYTE PTR [eax + -646], 7
|
||||
mov BYTE PTR [eax + -645], 7
|
||||
mov BYTE PTR [eax + -644], 7
|
||||
mov BYTE PTR [eax + -643], 7
|
||||
mov BYTE PTR [eax + -642], 7
|
||||
mov BYTE PTR [eax + -641], 7
|
||||
mov BYTE PTR [eax + -640], 7
|
||||
mov BYTE PTR [eax + -639], 7
|
||||
mov BYTE PTR [eax + -638], 7
|
||||
mov BYTE PTR [eax + -637], 7
|
||||
mov BYTE PTR [eax + -636], 7
|
||||
mov BYTE PTR [eax + -635], 7
|
||||
mov BYTE PTR [eax + -326], 7
|
||||
mov BYTE PTR [eax + -325], 7
|
||||
mov BYTE PTR [eax + -324], 7
|
||||
mov BYTE PTR [eax + -323], 7
|
||||
mov BYTE PTR [eax + -322], 7
|
||||
mov BYTE PTR [eax + -321], 7
|
||||
mov BYTE PTR [eax + -320], 7
|
||||
mov BYTE PTR [eax + -319], 7
|
||||
mov BYTE PTR [eax + -318], 7
|
||||
mov BYTE PTR [eax + -317], 7
|
||||
mov BYTE PTR [eax + -316], 7
|
||||
mov BYTE PTR [eax + -315], 7
|
||||
mov BYTE PTR [eax + -6], 7
|
||||
mov BYTE PTR [eax + -5], 7
|
||||
mov BYTE PTR [eax + -4], 7
|
||||
mov BYTE PTR [eax + -3], 7
|
||||
mov BYTE PTR [eax + -2], 7
|
||||
mov BYTE PTR [eax + -1], 7
|
||||
mov BYTE PTR [eax + 0], 7
|
||||
mov BYTE PTR [eax + 1], 7
|
||||
mov BYTE PTR [eax + 2], 7
|
||||
mov BYTE PTR [eax + 3], 7
|
||||
mov BYTE PTR [eax + 4], 7
|
||||
mov BYTE PTR [eax + 5], 7
|
||||
mov BYTE PTR [eax + 314], 7
|
||||
mov BYTE PTR [eax + 315], 7
|
||||
mov BYTE PTR [eax + 316], 7
|
||||
mov BYTE PTR [eax + 317], 7
|
||||
mov BYTE PTR [eax + 318], 7
|
||||
mov BYTE PTR [eax + 319], 7
|
||||
mov BYTE PTR [eax + 320], 7
|
||||
mov BYTE PTR [eax + 321], 7
|
||||
mov BYTE PTR [eax + 322], 7
|
||||
mov BYTE PTR [eax + 323], 7
|
||||
mov BYTE PTR [eax + 324], 7
|
||||
mov BYTE PTR [eax + 325], 7
|
||||
mov BYTE PTR [eax + 634], 7
|
||||
mov BYTE PTR [eax + 635], 7
|
||||
mov BYTE PTR [eax + 636], 7
|
||||
mov BYTE PTR [eax + 637], 7
|
||||
mov BYTE PTR [eax + 638], 7
|
||||
mov BYTE PTR [eax + 639], 7
|
||||
mov BYTE PTR [eax + 640], 7
|
||||
mov BYTE PTR [eax + 641], 7
|
||||
mov BYTE PTR [eax + 642], 7
|
||||
mov BYTE PTR [eax + 643], 7
|
||||
mov BYTE PTR [eax + 644], 7
|
||||
mov BYTE PTR [eax + 645], 7
|
||||
mov BYTE PTR [eax + 954], 7
|
||||
mov BYTE PTR [eax + 955], 7
|
||||
mov BYTE PTR [eax + 956], 7
|
||||
mov BYTE PTR [eax + 957], 7
|
||||
mov BYTE PTR [eax + 958], 7
|
||||
mov BYTE PTR [eax + 959], 7
|
||||
mov BYTE PTR [eax + 960], 7
|
||||
mov BYTE PTR [eax + 961], 7
|
||||
mov BYTE PTR [eax + 962], 7
|
||||
mov BYTE PTR [eax + 963], 7
|
||||
mov BYTE PTR [eax + 964], 7
|
||||
mov BYTE PTR [eax + 965], 7
|
||||
mov BYTE PTR [eax + 1274], 7
|
||||
mov BYTE PTR [eax + 1275], 7
|
||||
mov BYTE PTR [eax + 1276], 7
|
||||
mov BYTE PTR [eax + 1277], 7
|
||||
mov BYTE PTR [eax + 1278], 7
|
||||
mov BYTE PTR [eax + 1279], 7
|
||||
mov BYTE PTR [eax + 1280], 7
|
||||
mov BYTE PTR [eax + 1281], 7
|
||||
mov BYTE PTR [eax + 1282], 7
|
||||
mov BYTE PTR [eax + 1283], 7
|
||||
mov BYTE PTR [eax + 1284], 7
|
||||
mov BYTE PTR [eax + 1285], 7
|
||||
mov BYTE PTR [eax + 1594], 7
|
||||
mov BYTE PTR [eax + 1595], 7
|
||||
mov BYTE PTR [eax + 1596], 7
|
||||
mov BYTE PTR [eax + 1597], 7
|
||||
mov BYTE PTR [eax + 1598], 7
|
||||
mov BYTE PTR [eax + 1599], 7
|
||||
mov BYTE PTR [eax + 1600], 7
|
||||
mov BYTE PTR [eax + 1601], 7
|
||||
mov BYTE PTR [eax + 1602], 7
|
||||
mov BYTE PTR [eax + 1603], 7
|
||||
mov BYTE PTR [eax + 1604], 7
|
||||
mov BYTE PTR [eax + 1605], 7
|
||||
|
||||
pop ebp
|
||||
ret
|
||||
|
19
sprites.h
19
sprites.h
@ -2,6 +2,11 @@
|
||||
#define __SPRITES_H__
|
||||
|
||||
#include "types.h"
|
||||
#include "system/vga.h"
|
||||
|
||||
#define PALETTE_COLOR_COUNT (17)
|
||||
|
||||
extern struct VGAColor palette[17];
|
||||
|
||||
|
||||
extern void sprite_arenaWallTop(byte *);
|
||||
@ -61,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)
|
||||
|
||||
@ -92,15 +97,15 @@ extern void sprite_shotgun(byte *);
|
||||
#define SPRITE_SHOTGUN_OFFSET_Y (6)
|
||||
|
||||
|
||||
extern void sprite_shieldKiller(byte *);
|
||||
extern void sprite_beam(byte *);
|
||||
|
||||
#define SPRITE_SHIELDKILLER_WIDTH (12)
|
||||
#define SPRITE_BEAM_WIDTH (12)
|
||||
|
||||
#define SPRITE_SHIELDKILLER_HEIGHT (12)
|
||||
#define SPRITE_BEAM_HEIGHT (12)
|
||||
|
||||
#define SPRITE_SHIELDKILLER_OFFSET_X (6)
|
||||
#define SPRITE_BEAM_OFFSET_X (6)
|
||||
|
||||
#define SPRITE_SHIELDKILLER_OFFSET_Y (6)
|
||||
#define SPRITE_BEAM_OFFSET_Y (6)
|
||||
|
||||
|
||||
|
||||
|
@ -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]
|
||||
@ -37,7 +37,7 @@ files:
|
||||
position: [32, 20]
|
||||
dimensions: [12, 12]
|
||||
offset: [6, 6]
|
||||
shieldKiller:
|
||||
beam:
|
||||
position: [44, 20]
|
||||
dimensions: [12, 12]
|
||||
offset: [6, 6]
|
||||
|
BIN
sprtsht.bmp
BIN
sprtsht.bmp
Binary file not shown.
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
63
system/vga.c
63
system/vga.c
@ -2,6 +2,7 @@
|
||||
#include "pc_stuff.h"
|
||||
#include <malloc.h>
|
||||
#include <conio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
22
test.c
22
test.c
@ -1,20 +1,38 @@
|
||||
#include "const.h"
|
||||
#include "cutest-1.5/CuTest.h"
|
||||
#include <stdio.h>
|
||||
|
||||
CuSuite *SpawnGetSuite();
|
||||
CuSuite *PowerupGetSuite();
|
||||
CuSuite *CombatGetSuite();
|
||||
CuSuite *ConstGetSuite();
|
||||
CuSuite *MovementGetSuite();
|
||||
|
||||
void RunAllTests(void) {
|
||||
void beforeAll() {
|
||||
buildDifficultyBands();
|
||||
buildHitPointRages();
|
||||
}
|
||||
|
||||
int RunAllTests(void) {
|
||||
CuString *output = CuStringNew();
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
|
||||
beforeAll();
|
||||
|
||||
CuSuiteAddSuite(suite, SpawnGetSuite());
|
||||
CuSuiteAddSuite(suite, PowerupGetSuite());
|
||||
CuSuiteAddSuite(suite, CombatGetSuite());
|
||||
CuSuiteAddSuite(suite, ConstGetSuite());
|
||||
CuSuiteAddSuite(suite, MovementGetSuite());
|
||||
|
||||
CuSuiteRun(suite);
|
||||
CuSuiteSummary(suite, output);
|
||||
CuSuiteDetails(suite, output);
|
||||
printf("%s\n", output->buffer);
|
||||
|
||||
return suite->failCount > 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
RunAllTests();
|
||||
return RunAllTests();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user