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
|
*.bak
|
||||||
*.lib
|
*.lib
|
||||||
capture/
|
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
|
ASM_TEMPLATE = <<~ASM
|
||||||
<% sprites.each do |sprite| %>
|
<% sprites.each do |sprite| %>
|
||||||
PUBLIC <%= sprite.exported_name %><% end %>
|
PUBLIC <%= sprite.exported_name %><% end %>
|
||||||
|
PUBLIC _palette
|
||||||
|
|
||||||
.386
|
.386
|
||||||
.model flat,c
|
.model flat,c
|
||||||
|
|
||||||
|
.DATA
|
||||||
|
_palette:
|
||||||
|
<%= palette.flat_map { |color| color.to_asm_struct }.join("\n") %>
|
||||||
|
|
||||||
.CODE
|
.CODE
|
||||||
|
|
||||||
<% sprites.each do |sprite| %>
|
<% sprites.each do |sprite| %>
|
||||||
@ -60,11 +65,16 @@ ASM_TEMPLATE = <<~ASM
|
|||||||
end
|
end
|
||||||
ASM
|
ASM
|
||||||
|
|
||||||
C_TEMPLATE = <<~C
|
H_TEMPLATE = <<~H
|
||||||
#ifndef __SPRITES_H__
|
#ifndef __SPRITES_H__
|
||||||
#define __SPRITES_H__
|
#define __SPRITES_H__
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "system/vga.h"
|
||||||
|
|
||||||
|
#define PALETTE_COLOR_COUNT (<%= palette.length %>)
|
||||||
|
|
||||||
|
extern struct VGAColor palette[<%= palette.length %>];
|
||||||
|
|
||||||
<% sprites.each do |sprite| %>
|
<% sprites.each do |sprite| %>
|
||||||
extern void <%= sprite.function_name %>(byte *);
|
extern void <%= sprite.function_name %>(byte *);
|
||||||
@ -74,7 +84,7 @@ C_TEMPLATE = <<~C
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
C
|
H
|
||||||
|
|
||||||
class AssemblerSprite
|
class AssemblerSprite
|
||||||
def initialize(
|
def initialize(
|
||||||
@ -144,9 +154,33 @@ screen = Screen.new(
|
|||||||
|
|
||||||
sprite_data = []
|
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|
|
data["files"].each do |spritesheet|
|
||||||
image = Magick::Image.read(spritesheet["file"]).first
|
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|
|
spritesheet["sprites"].each do |name, details|
|
||||||
sprite_data << SpriteData.new(
|
sprite_data << SpriteData.new(
|
||||||
image:,
|
image:,
|
||||||
@ -179,12 +213,13 @@ sprites = sprite_data.map do |sd|
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
File.open('sprites.asm', 'w') do |fh|
|
File.open('sprites.asm', 'w') do |fh|
|
||||||
fh.puts ERB.new(ASM_TEMPLATE).result_with_hash(sprites:)
|
fh.puts ERB.new(ASM_TEMPLATE).result_with_hash(sprites:)
|
||||||
end
|
end
|
||||||
|
|
||||||
File.open('sprites.h', 'w') do |fh|
|
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
|
end
|
||||||
|
|
||||||
puts "sprites.{asm,h} written"
|
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 <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "game.h"
|
#include "system/vga.h"
|
||||||
|
|
||||||
|
#include "game.h"
|
||||||
|
#include "powerup.h"
|
||||||
#include "movement.h"
|
#include "movement.h"
|
||||||
#include "const.h"
|
#include "const.h"
|
||||||
#include "system/vga.h"
|
|
||||||
|
|
||||||
void setupBullet(
|
void setupBullet(
|
||||||
struct BulletPosition *bullet,
|
struct BulletPosition *bullet,
|
||||||
@ -38,13 +39,33 @@ void setupBullet(
|
|||||||
bullet->velocityStep = 0;
|
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(
|
int attemptToFireRabbitBullet(
|
||||||
struct RabbitPosition *rabbitPosition,
|
struct RabbitPosition *rabbitPosition,
|
||||||
struct RabbitWeaponry *rabbitWeaponry,
|
struct RabbitWeaponry *rabbitWeaponry,
|
||||||
struct BulletPosition rabbitBulletPosition[]
|
struct BulletPosition rabbitBulletPosition[]
|
||||||
) {
|
) {
|
||||||
int okToFire = 0, i, mouseAngle;
|
int okToFire = 0, i, mouseAngle;
|
||||||
int availableBullets[3];
|
int availableBullets[4];
|
||||||
|
float beamOffsetX, beamOffsetY;
|
||||||
|
|
||||||
if (rabbitWeaponry->cooldown > 0) return 0;
|
if (rabbitWeaponry->cooldown > 0) return 0;
|
||||||
mouseAngle = rabbitPosition->mouseAngle;
|
mouseAngle = rabbitPosition->mouseAngle;
|
||||||
@ -52,15 +73,11 @@ int attemptToFireRabbitBullet(
|
|||||||
rabbitWeaponry->cooldown = RABBIT_BULLET_COOLDOWN;
|
rabbitWeaponry->cooldown = RABBIT_BULLET_COOLDOWN;
|
||||||
|
|
||||||
if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_SINGLE_SHOT_GUN) {
|
if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_SINGLE_SHOT_GUN) {
|
||||||
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
if (!maybeFireShotCount(
|
||||||
if (rabbitBulletPosition[i].isActive == 0) {
|
rabbitBulletPosition,
|
||||||
okToFire = 1;
|
availableBullets,
|
||||||
availableBullets[0] = i;
|
1
|
||||||
break;
|
)) return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!okToFire) return 0;
|
|
||||||
|
|
||||||
setupBullet(
|
setupBullet(
|
||||||
&rabbitBulletPosition[availableBullets[0]],
|
&rabbitBulletPosition[availableBullets[0]],
|
||||||
@ -71,14 +88,11 @@ int attemptToFireRabbitBullet(
|
|||||||
);
|
);
|
||||||
} else if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_SPREAD_SHOT_GUN) {
|
} else if (rabbitWeaponry->currentWeapon == WEAPON_TYPE_SPREAD_SHOT_GUN) {
|
||||||
// make sure three bullets are available
|
// make sure three bullets are available
|
||||||
for (i = 0; i < RABBIT_BULLET_LIMIT; ++i) {
|
if (!maybeFireShotCount(
|
||||||
if (rabbitBulletPosition[i].isActive == 0) {
|
rabbitBulletPosition,
|
||||||
availableBullets[okToFire++] = i;
|
availableBullets,
|
||||||
if (okToFire == 3) break;
|
3
|
||||||
}
|
)) return 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (okToFire < 3) return 0;
|
|
||||||
|
|
||||||
// if so, fire away
|
// if so, fire away
|
||||||
for (i = 0; i < 3; ++i) {
|
for (i = 0; i < 3; ++i) {
|
||||||
@ -90,6 +104,27 @@ int attemptToFireRabbitBullet(
|
|||||||
RABBIT_BULLET_VELOCITY
|
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;
|
return 1;
|
||||||
@ -232,8 +267,8 @@ int rabbitBulletGrid[4][RABBIT_BULLET_LIMIT];
|
|||||||
int enemyGrid[4][ENEMY_MAX_COUNT];
|
int enemyGrid[4][ENEMY_MAX_COUNT];
|
||||||
int enemyBulletGrid[4][ENEMY_BULLET_LIMIT];
|
int enemyBulletGrid[4][ENEMY_BULLET_LIMIT];
|
||||||
int rabbitBulletGridIndex[4],
|
int rabbitBulletGridIndex[4],
|
||||||
enemyGridIndex[4],
|
enemyGridIndex[4],
|
||||||
enemyBulletGridIndex[4];
|
enemyBulletGridIndex[4];
|
||||||
int gridPosition[4];
|
int gridPosition[4];
|
||||||
|
|
||||||
#define CALCULATE_GRID_POS(x,y) ((x / COLLISION_GRID_SIZE) + (y / COLLISION_GRID_SIZE) * 2)
|
#define CALCULATE_GRID_POS(x,y) ((x / COLLISION_GRID_SIZE) + (y / COLLISION_GRID_SIZE) * 2)
|
||||||
@ -261,6 +296,7 @@ void buildCollisionGrids(
|
|||||||
struct PlayerPowerup *playerPowerup
|
struct PlayerPowerup *playerPowerup
|
||||||
) {
|
) {
|
||||||
int grid, i;
|
int grid, i;
|
||||||
|
struct CompiledSpriteRender *which;
|
||||||
|
|
||||||
for (grid = 0; grid < 4; ++grid) {
|
for (grid = 0; grid < 4; ++grid) {
|
||||||
rabbitBulletGridIndex[grid] = 0;
|
rabbitBulletGridIndex[grid] = 0;
|
||||||
@ -278,17 +314,17 @@ void buildCollisionGrids(
|
|||||||
|
|
||||||
switch (playerPowerup->type) {
|
switch (playerPowerup->type) {
|
||||||
case POWERUP_TYPE_SHOTGUN:
|
case POWERUP_TYPE_SHOTGUN:
|
||||||
shotgun.x = playerPowerup->x;
|
which = &shotgun;
|
||||||
shotgun.y = playerPowerup->y;
|
|
||||||
determineGridPositionsForSprite(&shotgun);
|
|
||||||
break;
|
break;
|
||||||
case POWERUP_TYPE_SHIELD_KILLER:
|
case POWERUP_TYPE_BEAM_WEAPON:
|
||||||
shieldKiller.x = playerPowerup->x;
|
which = &beam;
|
||||||
shieldKiller.y = playerPowerup->y;
|
|
||||||
determineGridPositionsForSprite(&shieldKiller);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
which->x = playerPowerup->x;
|
||||||
|
which->y = playerPowerup->y;
|
||||||
|
determineGridPositionsForSprite(which);
|
||||||
|
|
||||||
for (grid = 0; grid < 4; ++grid) {
|
for (grid = 0; grid < 4; ++grid) {
|
||||||
powerupGrid[grid] = gridPosition[grid];
|
powerupGrid[grid] = gridPosition[grid];
|
||||||
}
|
}
|
||||||
@ -374,8 +410,6 @@ int handleEnemyBulletToRabbitCollisions(
|
|||||||
return hitCount;
|
return hitCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define KNOCKBACK_DISTANCE (3)
|
|
||||||
|
|
||||||
void knockbackEnemy(
|
void knockbackEnemy(
|
||||||
struct EnemyPosition *enemy,
|
struct EnemyPosition *enemy,
|
||||||
int angle
|
int angle
|
||||||
@ -390,7 +424,8 @@ void knockbackEnemy(
|
|||||||
|
|
||||||
void handleRabbitBulletToEnemyCollisions(
|
void handleRabbitBulletToEnemyCollisions(
|
||||||
struct BulletPosition rabbitBulletPosition[],
|
struct BulletPosition rabbitBulletPosition[],
|
||||||
struct EnemyPosition enemyPosition[]
|
struct EnemyPosition enemyPosition[],
|
||||||
|
struct RabbitWeaponry *rabbitWeaponry
|
||||||
) {
|
) {
|
||||||
int bulletIdx, enemyIdx, grid;
|
int bulletIdx, enemyIdx, grid;
|
||||||
int resolvedBulletIdx, resolvedEnemyIdx;
|
int resolvedBulletIdx, resolvedEnemyIdx;
|
||||||
@ -413,7 +448,7 @@ void handleRabbitBulletToEnemyCollisions(
|
|||||||
populateTargetCollision(&enemy);
|
populateTargetCollision(&enemy);
|
||||||
|
|
||||||
if (isCollision()) {
|
if (isCollision()) {
|
||||||
enemyPosition[resolvedEnemyIdx].hitPoints--;
|
enemyPosition[resolvedEnemyIdx].hitPoints -= rabbitWeaponry->damage;
|
||||||
if (enemyPosition[resolvedEnemyIdx].hitPoints < 1) {
|
if (enemyPosition[resolvedEnemyIdx].hitPoints < 1) {
|
||||||
enemyPosition[resolvedEnemyIdx].willBeInactive = 1;
|
enemyPosition[resolvedEnemyIdx].willBeInactive = 1;
|
||||||
enemyPosition[resolvedEnemyIdx].wasKilled = 1;
|
enemyPosition[resolvedEnemyIdx].wasKilled = 1;
|
||||||
@ -463,6 +498,8 @@ int handleRabbitToPowerupCollision(
|
|||||||
struct RabbitPosition *rabbitPosition,
|
struct RabbitPosition *rabbitPosition,
|
||||||
struct PlayerPowerup *playerPowerup
|
struct PlayerPowerup *playerPowerup
|
||||||
) {
|
) {
|
||||||
|
struct CompiledSpriteRender *which;
|
||||||
|
|
||||||
if (!playerPowerup->isActive) return 0;
|
if (!playerPowerup->isActive) return 0;
|
||||||
|
|
||||||
rabbit.x = rabbitPosition->rabbitPosition[0];
|
rabbit.x = rabbitPosition->rabbitPosition[0];
|
||||||
@ -471,16 +508,84 @@ int handleRabbitToPowerupCollision(
|
|||||||
|
|
||||||
switch (playerPowerup->type) {
|
switch (playerPowerup->type) {
|
||||||
case POWERUP_TYPE_SHOTGUN:
|
case POWERUP_TYPE_SHOTGUN:
|
||||||
shotgun.x = playerPowerup->x;
|
which = &shotgun;
|
||||||
shotgun.y = playerPowerup->y;
|
|
||||||
populateTargetCollision(&shotgun);
|
|
||||||
break;
|
break;
|
||||||
case POWERUP_TYPE_SHIELD_KILLER:
|
case POWERUP_TYPE_BEAM_WEAPON:
|
||||||
shieldKiller.x = playerPowerup->x;
|
which = &beam;
|
||||||
shieldKiller.y = playerPowerup->y;
|
|
||||||
populateTargetCollision(&shieldKiller);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
which->x = playerPowerup->x;
|
||||||
|
which->y = playerPowerup->y;
|
||||||
|
populateTargetCollision(which);
|
||||||
|
|
||||||
return isCollision();
|
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(
|
void handleRabbitBulletToEnemyCollisions(
|
||||||
struct BulletPosition[],
|
struct BulletPosition[],
|
||||||
struct EnemyPosition[]
|
struct EnemyPosition[],
|
||||||
|
struct RabbitWeaponry*
|
||||||
);
|
);
|
||||||
|
|
||||||
int handleRabbitToEnemyCollisions(
|
int handleRabbitToEnemyCollisions(
|
||||||
@ -48,3 +49,18 @@ int handleRabbitToPowerupCollision(
|
|||||||
struct RabbitPosition *rabbitPosition,
|
struct RabbitPosition *rabbitPosition,
|
||||||
struct PlayerPowerup *playerPowerup
|
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 TILE_SIZE (20)
|
||||||
#define ARENA_WIDTH_TILES (10)
|
#define ARENA_WIDTH_TILES (10)
|
||||||
#define ARENA_HEIGHT_TILES (10)
|
#define ARENA_HEIGHT_TILES (10)
|
||||||
#define COLLISION_GRID_SIZE (ARENA_WIDTH_TILES / 2 * TILE_SIZE)
|
#define COLLISION_GRID_SIZE (ARENA_WIDTH_TILES / 2 * TILE_SIZE)
|
||||||
|
|
||||||
|
#define ARENA_LEFT_EDGE (TILE_SIZE)
|
||||||
|
#define ARENA_TOP_EDGE (TILE_SIZE)
|
||||||
|
#define ARENA_RIGHT_EDGE (ARENA_WIDTH_TILES - 1) * TILE_SIZE
|
||||||
|
#define ARENA_BOTTOM_EDGE (ARENA_WIDTH_TILES - 1) * TILE_SIZE
|
||||||
|
|
||||||
#define MOUSE_DISTANCE (32)
|
#define MOUSE_DISTANCE (32)
|
||||||
|
|
||||||
#define RABBIT_MOTION_DRAG (2)
|
#define RABBIT_MOTION_DRAG (2)
|
||||||
#define RABBIT_MOTION_ACCELERATION (5)
|
#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_MOTION_VELOCITY_DECAY (5)
|
||||||
|
|
||||||
#define RABBIT_BULLET_LIMIT (9)
|
#define RABBIT_BULLET_LIMIT (9)
|
||||||
@ -28,25 +38,60 @@
|
|||||||
#define DEG2RAD (3.14159/180)
|
#define DEG2RAD (3.14159/180)
|
||||||
|
|
||||||
#define ENEMY_MAX_COUNT (50)
|
#define ENEMY_MAX_COUNT (50)
|
||||||
#define ENEMY_BULLET_LIMIT (ENEMY_MAX_COUNT / 4)
|
#define ENEMY_BULLET_LIMIT (ENEMY_MAX_COUNT / 5)
|
||||||
|
|
||||||
#define ENEMY_MOVE_SPEED (1)
|
#define ENEMY_MOVE_SPEED (1)
|
||||||
#define ENEMY_MOVE_DELAY (3)
|
#define ENEMY_MOVE_DELAY (3)
|
||||||
#define ENEMY_BULLET_VELOCITY (RABBIT_BULLET_VELOCITY - 2)
|
#define ENEMY_BULLET_VELOCITY (RABBIT_BULLET_VELOCITY - 1)
|
||||||
|
|
||||||
#define ENEMY_FIRE_MIN_DELAY (45)
|
#define ENEMY_FIRE_MIN_DELAY (50)
|
||||||
#define ENEMY_FIRE_VARIABLE_DELAY (60)
|
#define ENEMY_FIRE_VARIABLE_DELAY (60)
|
||||||
|
|
||||||
#define ENEMY_BULLET_DAMAGE (3)
|
#define ENEMY_BULLET_DAMAGE (3)
|
||||||
#define ENEMY_COLLISION_DAMAGE (6)
|
#define ENEMY_COLLISION_DAMAGE (6)
|
||||||
#define ENEMY_KILL_HEALTH_GAIN (1)
|
|
||||||
#define ENEMY_HIT_POINT_DIFFICULTY_INCREASE_EVERY (4)
|
#define ENEMY_HIT_POINT_DIFFICULTY_INCREASE_EVERY (4)
|
||||||
|
|
||||||
#define BASE_ENEMY_SPAWN_COOLDOWN (30)
|
#define BASE_ENEMY_SPAWN_COOLDOWN (30)
|
||||||
#define DIFFICULTY_SPAWN_COOLDOWN_REDUCTION (2)
|
#define DIFFICULTY_SPAWN_COOLDOWN_REDUCTION (1)
|
||||||
#define MINIMUM_ENEMY_SPAWN_COOLDOWN (3)
|
#define MINIMUM_ENEMY_SPAWN_COOLDOWN (3)
|
||||||
#define VARIABLE_ENEMY_SPAWN_COOLDOWN (10)
|
#define VARIABLE_ENEMY_SPAWN_COOLDOWN (10)
|
||||||
|
|
||||||
#define POWERUP_RESPAWN_COOLDOWN_PER_LEVEL (10)
|
#define POWERUP_RESPAWN_COOLDOWN_PER_LEVEL (10)
|
||||||
|
|
||||||
#define SHOTGUN_ROUNDS_PER_LEVEL (25)
|
#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 <assert.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -11,7 +14,7 @@
|
|||||||
* CuStr
|
* CuStr
|
||||||
*-------------------------------------------------------------------------*/
|
*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
char* CuStrAlloc(int size)
|
char* CuStrAlloc(size_t size)
|
||||||
{
|
{
|
||||||
char* newStr = (char*) malloc( sizeof(char) * (size) );
|
char* newStr = (char*) malloc( sizeof(char) * (size) );
|
||||||
return newStr;
|
return newStr;
|
||||||
@ -19,7 +22,7 @@ char* CuStrAlloc(int size)
|
|||||||
|
|
||||||
char* CuStrCopy(const char* old)
|
char* CuStrCopy(const char* old)
|
||||||
{
|
{
|
||||||
int len = strlen(old);
|
size_t len = strlen(old);
|
||||||
char* newStr = CuStrAlloc(len + 1);
|
char* newStr = CuStrAlloc(len + 1);
|
||||||
strcpy(newStr, old);
|
strcpy(newStr, old);
|
||||||
return newStr;
|
return newStr;
|
||||||
@ -54,7 +57,7 @@ void CuStringDelete(CuString *str)
|
|||||||
free(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->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize);
|
||||||
str->size = newSize;
|
str->size = newSize;
|
||||||
@ -62,7 +65,7 @@ void CuStringResize(CuString* str, int newSize)
|
|||||||
|
|
||||||
void CuStringAppend(CuString* str, const char* text)
|
void CuStringAppend(CuString* str, const char* text)
|
||||||
{
|
{
|
||||||
int length;
|
size_t length;
|
||||||
|
|
||||||
if (text == NULL) {
|
if (text == NULL) {
|
||||||
text = "NULL";
|
text = "NULL";
|
||||||
@ -93,9 +96,9 @@ void CuStringAppendFormat(CuString* str, const char* format, ...)
|
|||||||
CuStringAppend(str, buf);
|
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)
|
if (pos > str->length)
|
||||||
pos = str->length;
|
pos = str->length;
|
||||||
if (str->length + length + 1 >= str->size)
|
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->name = CuStrCopy(name);
|
||||||
t->failed = 0;
|
t->failed = 0;
|
||||||
t->ran = 0;
|
t->ran = 0;
|
||||||
t->message = NULL;
|
t->message = NULL;
|
||||||
t->function = function;
|
t->function = function;
|
||||||
t->jumpBuf = NULL;
|
t->jumpBuf = NULL;
|
||||||
}
|
}
|
||||||
@ -129,6 +132,7 @@ CuTest* CuTestNew(const char* name, TestFunction function)
|
|||||||
void CuTestDelete(CuTest *t)
|
void CuTestDelete(CuTest *t)
|
||||||
{
|
{
|
||||||
if (!t) return;
|
if (!t) return;
|
||||||
|
CuStringDelete(t->message);
|
||||||
free(t->name);
|
free(t->name);
|
||||||
free(t);
|
free(t);
|
||||||
}
|
}
|
||||||
@ -153,7 +157,9 @@ static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* str
|
|||||||
CuStringInsert(string, buf, 0);
|
CuStringInsert(string, buf, 0);
|
||||||
|
|
||||||
tc->failed = 1;
|
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);
|
if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +333,7 @@ void CuSuiteDetails(CuSuite* testSuite, CuString* details)
|
|||||||
{
|
{
|
||||||
failCount++;
|
failCount++;
|
||||||
CuStringAppendFormat(details, "%d) %s: %s\n",
|
CuStringAppendFormat(details, "%d) %s: %s\n",
|
||||||
failCount, testCase->name, testCase->message);
|
failCount, testCase->name, testCase->message->buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CuStringAppend(details, "\n!!!FAILURES!!!\n");
|
CuStringAppend(details, "\n!!!FAILURES!!!\n");
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
#define CUTEST_VERSION "CuTest 1.5"
|
#define CUTEST_VERSION "CuTest 1.5c"
|
||||||
|
|
||||||
/* CuString */
|
/* CuString */
|
||||||
|
|
||||||
char* CuStrAlloc(int size);
|
char* CuStrAlloc(size_t size);
|
||||||
char* CuStrCopy(const char* old);
|
char* CuStrCopy(const char* old);
|
||||||
|
|
||||||
#define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE)))
|
#define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE)))
|
||||||
@ -19,8 +20,8 @@ char* CuStrCopy(const char* old);
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int length;
|
size_t length;
|
||||||
int size;
|
size_t size;
|
||||||
char* buffer;
|
char* buffer;
|
||||||
} CuString;
|
} CuString;
|
||||||
|
|
||||||
@ -30,8 +31,8 @@ void CuStringRead(CuString* str, const char* path);
|
|||||||
void CuStringAppend(CuString* str, const char* text);
|
void CuStringAppend(CuString* str, const char* text);
|
||||||
void CuStringAppendChar(CuString* str, char ch);
|
void CuStringAppendChar(CuString* str, char ch);
|
||||||
void CuStringAppendFormat(CuString* str, const char* format, ...);
|
void CuStringAppendFormat(CuString* str, const char* format, ...);
|
||||||
void CuStringInsert(CuString* str, const char* text, int pos);
|
void CuStringInsert(CuString* str, const char* text, size_t pos);
|
||||||
void CuStringResize(CuString* str, int newSize);
|
void CuStringResize(CuString* str, size_t newSize);
|
||||||
void CuStringDelete(CuString* str);
|
void CuStringDelete(CuString* str);
|
||||||
|
|
||||||
/* CuTest */
|
/* CuTest */
|
||||||
@ -46,7 +47,7 @@ struct CuTest
|
|||||||
TestFunction function;
|
TestFunction function;
|
||||||
int failed;
|
int failed;
|
||||||
int ran;
|
int ran;
|
||||||
const char* message;
|
CuString *message;
|
||||||
jmp_buf *jumpBuf;
|
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(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 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 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 CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),((p) != NULL))
|
||||||
|
|
||||||
/* CuSuite */
|
/* CuSuite */
|
||||||
|
|
||||||
|
322
game.c
322
game.c
@ -18,16 +18,7 @@
|
|||||||
#include "combat.h"
|
#include "combat.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "spawn.h"
|
#include "spawn.h"
|
||||||
|
#include "powerup.h"
|
||||||
// TODO: centralize these outside of game.c
|
|
||||||
struct CompiledSpriteRender rabbit,
|
|
||||||
mouse,
|
|
||||||
bullet,
|
|
||||||
enemy,
|
|
||||||
enemyBullet,
|
|
||||||
shotgun,
|
|
||||||
shieldKiller;
|
|
||||||
struct SpriteBounds bounds;
|
|
||||||
|
|
||||||
struct BMPImage spritesheetImage;
|
struct BMPImage spritesheetImage;
|
||||||
struct VGAColor vgaColors[256];
|
struct VGAColor vgaColors[256];
|
||||||
@ -50,6 +41,19 @@ struct BulletPosition enemyBulletPosition[ENEMY_BULLET_LIMIT];
|
|||||||
struct RabbitWeaponry rabbitWeaponry;
|
struct RabbitWeaponry rabbitWeaponry;
|
||||||
struct PlayerPowerup playerPowerup;
|
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() {
|
void setupRabbitBullets() {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -61,12 +65,13 @@ void setupRabbitBullets() {
|
|||||||
rabbitWeaponry.cooldown = 0;
|
rabbitWeaponry.cooldown = 0;
|
||||||
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
|
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
|
||||||
rabbitWeaponry.currentWeaponRemainingRounds = 0;
|
rabbitWeaponry.currentWeaponRemainingRounds = 0;
|
||||||
|
rabbitWeaponry.damage = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupPowerup() {
|
void setupPowerup() {
|
||||||
playerPowerup.x = 100;
|
playerPowerup.x = 100;
|
||||||
playerPowerup.y = 100;
|
playerPowerup.y = 100;
|
||||||
playerPowerup.cooldown = 20 + rand() % 10;
|
playerPowerup.cooldown = determinePowerupCooldownTime(globalGameState.difficulty);
|
||||||
playerPowerup.type = POWERUP_TYPE_SHOTGUN;
|
playerPowerup.type = POWERUP_TYPE_SHOTGUN;
|
||||||
playerPowerup.isActive = 0;
|
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() {
|
void setupEnemySprites() {
|
||||||
buildCompiledSprite(
|
buildCompiledSprite(
|
||||||
&sprite_enemy,
|
&sprite_enemy,
|
||||||
@ -197,12 +154,12 @@ void setupPowerupSprites() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
buildCompiledSprite(
|
buildCompiledSprite(
|
||||||
&sprite_shieldKiller,
|
&sprite_beam,
|
||||||
&shieldKiller,
|
&beam,
|
||||||
SPRITE_SHIELDKILLER_WIDTH,
|
SPRITE_BEAM_WIDTH,
|
||||||
SPRITE_SHIELDKILLER_HEIGHT,
|
SPRITE_BEAM_HEIGHT,
|
||||||
SPRITE_SHIELDKILLER_OFFSET_X,
|
SPRITE_BEAM_OFFSET_X,
|
||||||
SPRITE_SHIELDKILLER_OFFSET_Y
|
SPRITE_BEAM_OFFSET_Y
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +167,10 @@ void renderMouse() {
|
|||||||
mouse.x = rabbitPosition.mousePosition[0];
|
mouse.x = rabbitPosition.mousePosition[0];
|
||||||
mouse.y = rabbitPosition.mousePosition[1];
|
mouse.y = rabbitPosition.mousePosition[1];
|
||||||
drawCompiledSprite(&mouse);
|
drawCompiledSprite(&mouse);
|
||||||
drawPixel(rabbitPosition.mouseDotPosition[0], rabbitPosition.mouseDotPosition[1], 2);
|
|
||||||
|
mouse.x = rabbitPosition.mouseDotPosition[0];
|
||||||
|
mouse.y = rabbitPosition.mouseDotPosition[1];
|
||||||
|
drawCompiledSprite(&mouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderRabbit() {
|
void renderRabbit() {
|
||||||
@ -256,20 +216,22 @@ void renderEnemyBullets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void renderPowerup() {
|
void renderPowerup() {
|
||||||
|
struct CompiledSpriteRender *which;
|
||||||
|
|
||||||
if (!playerPowerup.isActive) return;
|
if (!playerPowerup.isActive) return;
|
||||||
|
|
||||||
switch (playerPowerup.type) {
|
switch (playerPowerup.type) {
|
||||||
case POWERUP_TYPE_SHOTGUN:
|
case POWERUP_TYPE_SHOTGUN:
|
||||||
shotgun.x = playerPowerup.x;
|
which = &shotgun;
|
||||||
shotgun.y = playerPowerup.y;
|
|
||||||
drawCompiledSprite(&shotgun);
|
|
||||||
break;
|
break;
|
||||||
case POWERUP_TYPE_SHIELD_KILLER:
|
case POWERUP_TYPE_BEAM_WEAPON:
|
||||||
shieldKiller.x = playerPowerup.x;
|
which = &beam;
|
||||||
shieldKiller.y = playerPowerup.y;
|
|
||||||
drawCompiledSprite(&shieldKiller);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
which->x = playerPowerup.x;
|
||||||
|
which->y = playerPowerup.y;
|
||||||
|
drawCompiledSprite(which);
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawOnlyArenaForSprite(struct CompiledSpriteRender *sprite) {
|
void drawOnlyArenaForSprite(struct CompiledSpriteRender *sprite) {
|
||||||
@ -282,11 +244,9 @@ void drawOnlyMouseArena() {
|
|||||||
mouse.y = rabbitPosition.oldMousePosition[1];
|
mouse.y = rabbitPosition.oldMousePosition[1];
|
||||||
drawOnlyArenaForSprite(&mouse);
|
drawOnlyArenaForSprite(&mouse);
|
||||||
|
|
||||||
bounds.top = rabbitPosition.oldMouseDotPosition[1];
|
mouse.x = rabbitPosition.oldMouseDotPosition[0];
|
||||||
bounds.bottom = rabbitPosition.oldMouseDotPosition[1];
|
mouse.y = rabbitPosition.oldMouseDotPosition[1];
|
||||||
bounds.left = rabbitPosition.oldMouseDotPosition[0];
|
drawOnlyArenaForSprite(&mouse);
|
||||||
bounds.right = rabbitPosition.oldMouseDotPosition[0];
|
|
||||||
drawOnlyArena(&bounds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawOnlyRabbitArena() {
|
void drawOnlyRabbitArena() {
|
||||||
@ -296,21 +256,23 @@ void drawOnlyRabbitArena() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void drawOnlyPowerupArena() {
|
void drawOnlyPowerupArena() {
|
||||||
|
struct CompiledSpriteRender *which;
|
||||||
|
|
||||||
if (!playerPowerup.isActive) return;
|
if (!playerPowerup.isActive) return;
|
||||||
|
|
||||||
switch (playerPowerup.type) {
|
switch (playerPowerup.type) {
|
||||||
case POWERUP_TYPE_SHOTGUN:
|
case POWERUP_TYPE_SHOTGUN:
|
||||||
shotgun.x = playerPowerup.x;
|
which = &shotgun;
|
||||||
shotgun.y = playerPowerup.y;
|
|
||||||
drawOnlyArenaForSprite(&shotgun);
|
|
||||||
break;
|
break;
|
||||||
case POWERUP_TYPE_SHIELD_KILLER:
|
case POWERUP_TYPE_BEAM_WEAPON:
|
||||||
shieldKiller.x = playerPowerup.x;
|
which = &beam;
|
||||||
shieldKiller.y = playerPowerup.y;
|
|
||||||
drawOnlyArenaForSprite(&shieldKiller);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
which->x = playerPowerup.x;
|
||||||
|
which->y = playerPowerup.y;
|
||||||
|
drawOnlyArenaForSprite(which);
|
||||||
|
|
||||||
if (playerPowerup.willBeInactive) {
|
if (playerPowerup.willBeInactive) {
|
||||||
playerPowerup.isActive = 0;
|
playerPowerup.isActive = 0;
|
||||||
}
|
}
|
||||||
@ -368,11 +330,8 @@ int setupGame() {
|
|||||||
installKeyboardHandler();
|
installKeyboardHandler();
|
||||||
initializeDrawBuffer();
|
initializeDrawBuffer();
|
||||||
|
|
||||||
fh = fopen("sprtsht.bmp", "rb");
|
buildDifficultyBands();
|
||||||
if (readBMPIntoNewMemory(fh, &spritesheetImage)) return 1;
|
buildHitPointRages();
|
||||||
fclose(fh);
|
|
||||||
|
|
||||||
spritesheetImage.transparentColor = 0;
|
|
||||||
|
|
||||||
setupWallSprites();
|
setupWallSprites();
|
||||||
setupRabbitSprites();
|
setupRabbitSprites();
|
||||||
@ -384,8 +343,8 @@ int setupGame() {
|
|||||||
setupPowerupSprites();
|
setupPowerupSprites();
|
||||||
|
|
||||||
setVideoMode(VIDEO_MODE_VGA_256);
|
setVideoMode(VIDEO_MODE_VGA_256);
|
||||||
bmp256ColorPaletteToVGAColorPalette(&spritesheetImage, vgaColors);
|
|
||||||
setVGAColors(vgaColors, 256);
|
setVGAColors(palette, PALETTE_COLOR_COUNT);
|
||||||
|
|
||||||
activateMouse(&mouseStatus);
|
activateMouse(&mouseStatus);
|
||||||
|
|
||||||
@ -411,8 +370,7 @@ void handleMovement() {
|
|||||||
&mouseStatus
|
&mouseStatus
|
||||||
);
|
);
|
||||||
calculateTargetAngle(
|
calculateTargetAngle(
|
||||||
&rabbitPosition,
|
&rabbitPosition
|
||||||
&mouseStatus
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,12 +383,9 @@ void handleCombat() {
|
|||||||
&rabbitWeaponry,
|
&rabbitWeaponry,
|
||||||
rabbitBulletPosition
|
rabbitBulletPosition
|
||||||
)) {
|
)) {
|
||||||
if (rabbitWeaponry.currentWeaponRemainingRounds > 0) {
|
fireCurrentWeaponOnce(
|
||||||
rabbitWeaponry.currentWeaponRemainingRounds--;
|
&rabbitWeaponry
|
||||||
if (rabbitWeaponry.currentWeaponRemainingRounds == 0) {
|
);
|
||||||
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SINGLE_SHOT_GUN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +419,7 @@ void handleCombat() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (didHitRabbit) {
|
if (didHitRabbit) {
|
||||||
health -= ENEMY_COLLISION_DAMAGE * didHitRabbit;
|
globalGameState.health -= ENEMY_COLLISION_DAMAGE * didHitRabbit;
|
||||||
}
|
}
|
||||||
|
|
||||||
didHitRabbit = handleEnemyBulletToRabbitCollisions(
|
didHitRabbit = handleEnemyBulletToRabbitCollisions(
|
||||||
@ -473,18 +428,19 @@ void handleCombat() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (didHitRabbit) {
|
if (didHitRabbit) {
|
||||||
health -= ENEMY_BULLET_DAMAGE * didHitRabbit;
|
globalGameState.health -= ENEMY_BULLET_DAMAGE * didHitRabbit;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRabbitBulletToEnemyCollisions(
|
handleRabbitBulletToEnemyCollisions(
|
||||||
rabbitBulletPosition,
|
rabbitBulletPosition,
|
||||||
enemyPosition
|
enemyPosition,
|
||||||
|
&rabbitWeaponry
|
||||||
);
|
);
|
||||||
|
|
||||||
if (handleRabbitToPowerupCollision(&rabbitPosition, &playerPowerup)) {
|
if (handleRabbitToPowerupCollision(&rabbitPosition, &playerPowerup)) {
|
||||||
playerPowerup.willBeInactive = 1;
|
playerPowerup.willBeInactive = 1;
|
||||||
rabbitWeaponry.currentWeapon = WEAPON_TYPE_SPREAD_SHOT_GUN;
|
rabbitWeaponry.currentWeapon = playerPowerup.type;
|
||||||
rabbitWeaponry.currentWeaponRemainingRounds = (globalGameState.difficulty + 1) * SHOTGUN_ROUNDS_PER_LEVEL;
|
rabbitWeaponry.currentWeaponRemainingRounds = determineWeaponRounds(globalGameState.difficulty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,6 +469,123 @@ double averageSpeedCalc;
|
|||||||
clock_t startTime;
|
clock_t startTime;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
struct PriorStats {
|
||||||
|
int kills;
|
||||||
|
int health;
|
||||||
|
int remainingRounds;
|
||||||
|
int coins;
|
||||||
|
|
||||||
|
int damageUpgradeAvailable;
|
||||||
|
int healthUpgradeAvailable;
|
||||||
|
int healthGainUpgradeAvailable;
|
||||||
|
} priorStats = {
|
||||||
|
.kills = -1,
|
||||||
|
.health = -1,
|
||||||
|
.remainingRounds = -1,
|
||||||
|
.coins = -1,
|
||||||
|
.damageUpgradeAvailable = -1,
|
||||||
|
.healthUpgradeAvailable = -1,
|
||||||
|
.healthGainUpgradeAvailable = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UpgradesAvailable {
|
||||||
|
char damage;
|
||||||
|
char health;
|
||||||
|
char healthGain;
|
||||||
|
} upgradesAvailable = {
|
||||||
|
.damage = 0,
|
||||||
|
.health = 0,
|
||||||
|
.healthGain = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
void calculateUpgradesAvailable() {
|
||||||
|
if (globalGameState.damageUpgradeLevel < 3) {
|
||||||
|
upgradesAvailable.damage = globalGameState.coins >= damageUpgradeCosts[globalGameState.damageUpgradeLevel];
|
||||||
|
} else {
|
||||||
|
upgradesAvailable.damage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globalGameState.healthUpgradeLevel < 3) {
|
||||||
|
upgradesAvailable.health = globalGameState.coins >= healthUpgradeCosts[globalGameState.healthUpgradeLevel];
|
||||||
|
} else {
|
||||||
|
upgradesAvailable.health = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globalGameState.healthGainUpgradeLevel < 3) {
|
||||||
|
upgradesAvailable.healthGain = globalGameState.coins >= healthGainUpgradeCosts[globalGameState.healthGainUpgradeLevel];
|
||||||
|
} else {
|
||||||
|
upgradesAvailable.healthGain = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void redrawGameStats() {
|
||||||
|
char buffer[20];
|
||||||
|
|
||||||
|
if (priorStats.kills != globalGameState.kills) {
|
||||||
|
sprintf(buffer, "Hit: %d", globalGameState.kills);
|
||||||
|
renderStringToDrawBuffer(buffer, 1, 0, 210, 20);
|
||||||
|
|
||||||
|
priorStats.kills = globalGameState.kills;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorStats.health != globalGameState.health) {
|
||||||
|
sprintf(buffer, "Health: %d ", globalGameState.health);
|
||||||
|
renderStringToDrawBuffer(buffer, 1, 0, 210, 30);
|
||||||
|
|
||||||
|
priorStats.health = globalGameState.health;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorStats.remainingRounds != rabbitWeaponry.currentWeaponRemainingRounds) {
|
||||||
|
sprintf(buffer, "Rnds: %d ", rabbitWeaponry.currentWeaponRemainingRounds);
|
||||||
|
renderStringToDrawBuffer(buffer, 1, 0, 210, 40);
|
||||||
|
|
||||||
|
priorStats.remainingRounds = rabbitWeaponry.currentWeaponRemainingRounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorStats.coins != globalGameState.coins) {
|
||||||
|
sprintf(buffer, "Coins: %d ", globalGameState.coins);
|
||||||
|
renderStringToDrawBuffer(buffer, 1, 0, 210, 50);
|
||||||
|
|
||||||
|
priorStats.coins = globalGameState.coins;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorStats.damageUpgradeAvailable != upgradesAvailable.damage) {
|
||||||
|
if (upgradesAvailable.damage) {
|
||||||
|
sprintf(buffer, "[P] Dmg up");
|
||||||
|
} else {
|
||||||
|
sprintf(buffer, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStringToDrawBuffer(buffer, 1, 0, 210, 100);
|
||||||
|
|
||||||
|
priorStats.damageUpgradeAvailable = upgradesAvailable.damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorStats.healthUpgradeAvailable != upgradesAvailable.health) {
|
||||||
|
if (upgradesAvailable.health) {
|
||||||
|
sprintf(buffer, "[O] Health up");
|
||||||
|
} else {
|
||||||
|
sprintf(buffer, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStringToDrawBuffer(buffer, 1, 0, 210, 110);
|
||||||
|
|
||||||
|
priorStats.healthUpgradeAvailable = upgradesAvailable.health;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorStats.healthGainUpgradeAvailable != upgradesAvailable.healthGain) {
|
||||||
|
if (upgradesAvailable.healthGain) {
|
||||||
|
sprintf(buffer, "[I] HGain up");
|
||||||
|
} else {
|
||||||
|
sprintf(buffer, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStringToDrawBuffer(buffer, 1, 0, 210, 120);
|
||||||
|
|
||||||
|
priorStats.healthGainUpgradeAvailable = upgradesAvailable.healthGain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
byte *drawBuffer;
|
byte *drawBuffer;
|
||||||
int keepRunning = 1;
|
int keepRunning = 1;
|
||||||
@ -533,18 +606,37 @@ int main(void) {
|
|||||||
enemyPosition,
|
enemyPosition,
|
||||||
&rabbitPosition
|
&rabbitPosition
|
||||||
);
|
);
|
||||||
|
|
||||||
handleMovement();
|
|
||||||
handleRedraw();
|
|
||||||
handleCombat();
|
handleCombat();
|
||||||
handleEnemyKills();
|
handleRedraw();
|
||||||
|
handleEnemyKills(
|
||||||
|
enemyPosition,
|
||||||
|
&globalGameState,
|
||||||
|
&playerPowerup,
|
||||||
|
&rabbitWeaponry
|
||||||
|
);
|
||||||
|
handleMovement();
|
||||||
|
|
||||||
sprintf(buffer, "Hit: %d", kills);
|
calculateUpgradesAvailable();
|
||||||
renderStringToDrawBuffer(buffer, 1, 0, 210, 20);
|
redrawGameStats();
|
||||||
sprintf(buffer, "Health: %d ", health);
|
|
||||||
renderStringToDrawBuffer(buffer, 1, 0, 210, 30);
|
if (keyboardKeydownState.KEY_P && upgradesAvailable.damage) {
|
||||||
sprintf(buffer, "Rnds: %d ", rabbitWeaponry.currentWeaponRemainingRounds);
|
globalGameState.coins -= damageUpgradeCosts[globalGameState.damageUpgradeLevel];
|
||||||
renderStringToDrawBuffer(buffer, 1, 0, 210, 40);
|
globalGameState.damageUpgradeLevel++;
|
||||||
|
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();
|
waitStartVbl();
|
||||||
copyDrawBufferToDisplay();
|
copyDrawBufferToDisplay();
|
||||||
|
9
game.h
9
game.h
@ -9,6 +9,15 @@ extern struct SpriteBounds bounds;
|
|||||||
struct GlobalGameState {
|
struct GlobalGameState {
|
||||||
int difficulty;
|
int difficulty;
|
||||||
int spawnCooldown;
|
int spawnCooldown;
|
||||||
|
int kills;
|
||||||
|
int health;
|
||||||
|
int maxHealth;
|
||||||
|
int healthGainPerKill;
|
||||||
|
int coins;
|
||||||
|
|
||||||
|
int damageUpgradeLevel;
|
||||||
|
int healthUpgradeLevel;
|
||||||
|
int healthGainUpgradeLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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
|
all: system/system.lib game.exe test .SYMBOLIC
|
||||||
|
|
||||||
@ -19,10 +20,14 @@ sprites.asm: sprtsht.bmp spritesheet.yml
|
|||||||
game.exe: $(obj) system/system.lib
|
game.exe: $(obj) system/system.lib
|
||||||
wcl386 -q -fe=game -bt=dos -l=dos4g $(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
|
test: $(test_obj) system/system.lib .SYMBOLIC
|
||||||
wcl386 -fe=test -bt=dos -l=dos4g test.c spawn.c spawn_test.c cutest-1.5/CuTest.c
|
wcl386 -fe=test -bt=dos -l=dos4g $(test_obj) cutest-1.5/CuTest.c system/system.lib
|
||||||
|
|
||||||
clean: .SYMBOLIC
|
clean: .SYMBOLIC
|
||||||
|
rm -f game.exe
|
||||||
|
rm -f test.exe
|
||||||
rm *.o
|
rm *.o
|
||||||
|
rm *.err
|
||||||
rm system/*.o
|
rm system/*.o
|
||||||
rm system/*.lib
|
rm system/*.lib
|
||||||
|
rm system/*.err
|
||||||
|
124
movement.c
124
movement.c
@ -1,4 +1,5 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
@ -8,6 +9,9 @@
|
|||||||
|
|
||||||
#include "system/mouse_io.h"
|
#include "system/mouse_io.h"
|
||||||
#include "system/pc_stuff.h"
|
#include "system/pc_stuff.h"
|
||||||
|
#include "system/vga.h"
|
||||||
|
|
||||||
|
#define SIGN(x) ((x > 0) - (x < 0))
|
||||||
|
|
||||||
void captureAndLimitMousePosition(
|
void captureAndLimitMousePosition(
|
||||||
struct RabbitPosition *pos,
|
struct RabbitPosition *pos,
|
||||||
@ -25,33 +29,131 @@ void captureAndLimitMousePosition(
|
|||||||
if (pos->mouseDotPosition[1] >= MOUSE_LIMIT_BOTTOM) pos->mouseDotPosition[1] = MOUSE_LIMIT_BOTTOM;
|
if (pos->mouseDotPosition[1] >= MOUSE_LIMIT_BOTTOM) pos->mouseDotPosition[1] = MOUSE_LIMIT_BOTTOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateTargetAngle(
|
// now this is ray casting
|
||||||
|
struct TargetMousePosition {
|
||||||
|
int x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
void constrainMousePosition(
|
||||||
struct RabbitPosition *pos,
|
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 distanceX, distanceY;
|
||||||
float angle;
|
float angle, fixHypotenuse, fixDistance;
|
||||||
|
struct TargetMousePosition targetMousePosition;
|
||||||
|
|
||||||
// Position the cursor
|
// Position the cursor
|
||||||
distanceX = pos->mouseDotPosition[0] - pos->rabbitPosition[0];
|
distanceX = pos->mouseDotPosition[0] - pos->rabbitPosition[0];
|
||||||
distanceY = pos->mouseDotPosition[1] - pos->rabbitPosition[1];
|
distanceY = pos->mouseDotPosition[1] - pos->rabbitPosition[1];
|
||||||
|
|
||||||
angle = atan2(distanceY, distanceX) * RAD2DEG + 90;
|
// edge case -- top left corner
|
||||||
if (angle < 0) angle += 360;
|
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;
|
pos->mouseAngle = angle;
|
||||||
distanceY = -cos(angle * DEG2RAD) * MOUSE_DISTANCE;
|
|
||||||
|
|
||||||
pos->oldMousePosition[0] = pos->mousePosition[0];
|
pos->oldMousePosition[0] = pos->mousePosition[0];
|
||||||
pos->oldMousePosition[1] = pos->mousePosition[1];
|
pos->oldMousePosition[1] = pos->mousePosition[1];
|
||||||
|
|
||||||
pos->mousePosition[0] = pos->rabbitPosition[0] + distanceX;
|
targetMousePosition.x = pos->rabbitPosition[0];
|
||||||
pos->mousePosition[1] = pos->rabbitPosition[1] + distanceY;
|
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(
|
void handleEnemyMovement(
|
||||||
struct EnemyPosition enemyPosition[],
|
struct EnemyPosition enemyPosition[],
|
||||||
|
27
movement.h
27
movement.h
@ -18,18 +18,13 @@ struct RabbitPosition {
|
|||||||
int mouseAngle;
|
int mouseAngle;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define WEAPON_TYPE_SINGLE_SHOT_GUN (0)
|
|
||||||
#define WEAPON_TYPE_SPREAD_SHOT_GUN (1)
|
|
||||||
|
|
||||||
struct RabbitWeaponry {
|
struct RabbitWeaponry {
|
||||||
char cooldown;
|
char cooldown;
|
||||||
char currentWeapon;
|
char currentWeapon;
|
||||||
int currentWeaponRemainingRounds;
|
int currentWeaponRemainingRounds;
|
||||||
|
char damage;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define POWERUP_TYPE_SHOTGUN (0)
|
|
||||||
#define POWERUP_TYPE_SHIELD_KILLER (1)
|
|
||||||
|
|
||||||
struct PlayerPowerup {
|
struct PlayerPowerup {
|
||||||
int x, y;
|
int x, y;
|
||||||
int cooldown;
|
int cooldown;
|
||||||
@ -54,23 +49,23 @@ struct BulletPosition {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct EnemyPosition {
|
struct EnemyPosition {
|
||||||
char isActive;
|
int hitPoints;
|
||||||
|
|
||||||
char willBeInactive;
|
|
||||||
char wasKilled;
|
|
||||||
|
|
||||||
char hitPoints;
|
|
||||||
char hasLeftGate;
|
|
||||||
char gateExitedFrom;
|
|
||||||
|
|
||||||
int enemyPosition[2];
|
int enemyPosition[2];
|
||||||
int oldEnemyPosition[2];
|
int oldEnemyPosition[2];
|
||||||
|
|
||||||
int enemyMoveDelayStep;
|
int enemyMoveDelayStep;
|
||||||
int enemyFireDelayStep;
|
int enemyFireDelayStep;
|
||||||
|
|
||||||
|
char isActive;
|
||||||
|
|
||||||
|
char willBeInactive;
|
||||||
|
char wasKilled;
|
||||||
|
|
||||||
|
char hasLeftGate;
|
||||||
|
char gateExitedFrom;
|
||||||
};
|
};
|
||||||
|
|
||||||
void calculateTargetAngle(struct RabbitPosition*, struct MouseStatus*);
|
void calculateTargetAngle(struct RabbitPosition*);
|
||||||
void captureAndLimitMousePosition(struct RabbitPosition*, struct MouseStatus*);
|
void captureAndLimitMousePosition(struct RabbitPosition*, struct MouseStatus*);
|
||||||
void handleRabbitMovement(struct RabbitPosition*, struct KeyboardKeydownState*);
|
void handleRabbitMovement(struct RabbitPosition*, struct KeyboardKeydownState*);
|
||||||
void handleEnemyMovement(struct EnemyPosition[], struct RabbitPosition*);
|
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 },
|
{ .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) {
|
int determineEnemyHitPointCalculation(int difficulty, int roll) {
|
||||||
return HIT_POINT_RANGES[difficulty][roll];
|
return hitPointRanges[difficulty][roll];
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectEnemySpawnLocation(
|
void selectEnemySpawnLocation(
|
||||||
|
16
spawn_test.c
16
spawn_test.c
@ -39,17 +39,6 @@ void TestSelectEnemySpawnLocation(CuTest *tc) {
|
|||||||
CuAssertIntEquals(tc, 91, spawnDetails.spawnY);
|
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) {
|
void TestSpawnEnemy(CuTest *tc) {
|
||||||
srand(1);
|
srand(1);
|
||||||
|
|
||||||
@ -62,8 +51,8 @@ void TestSpawnEnemy(CuTest *tc) {
|
|||||||
spawnEnemy(&spawnDetails, &globalGameState);
|
spawnEnemy(&spawnDetails, &globalGameState);
|
||||||
|
|
||||||
// randomized values
|
// randomized values
|
||||||
CuAssertIntEquals(tc, 2, enemyPosition.hitPoints);
|
CuAssertIntEquals(tc, 1, enemyPosition.hitPoints);
|
||||||
CuAssertIntEquals(tc, 103, enemyPosition.enemyFireDelayStep);
|
CuAssertIntEquals(tc, 108, enemyPosition.enemyFireDelayStep);
|
||||||
|
|
||||||
// from spawndetails
|
// from spawndetails
|
||||||
CuAssertIntEquals(tc, 1, enemyPosition.gateExitedFrom);
|
CuAssertIntEquals(tc, 1, enemyPosition.gateExitedFrom);
|
||||||
@ -76,7 +65,6 @@ void TestSpawnEnemy(CuTest *tc) {
|
|||||||
CuSuite *SpawnGetSuite() {
|
CuSuite *SpawnGetSuite() {
|
||||||
CuSuite *suite = CuSuiteNew();
|
CuSuite *suite = CuSuiteNew();
|
||||||
SUITE_ADD_TEST(suite, TestSelectEnemySpawnLocation);
|
SUITE_ADD_TEST(suite, TestSelectEnemySpawnLocation);
|
||||||
SUITE_ADD_TEST(suite, TestDetermineEnemyHitPointCalculation);
|
|
||||||
SUITE_ADD_TEST(suite, TestSpawnEnemy);
|
SUITE_ADD_TEST(suite, TestSpawnEnemy);
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
|
209
sprites.asm
209
sprites.asm
@ -7,11 +7,66 @@ PUBLIC sprite_mouse_
|
|||||||
PUBLIC sprite_bullet_
|
PUBLIC sprite_bullet_
|
||||||
PUBLIC sprite_enemy_
|
PUBLIC sprite_enemy_
|
||||||
PUBLIC sprite_shotgun_
|
PUBLIC sprite_shotgun_
|
||||||
PUBLIC sprite_shieldKiller_
|
PUBLIC sprite_beam_
|
||||||
|
PUBLIC _palette
|
||||||
|
|
||||||
.386
|
.386
|
||||||
.model flat,c
|
.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
|
.CODE
|
||||||
|
|
||||||
|
|
||||||
@ -1336,10 +1391,10 @@ sprite_bullet_:
|
|||||||
mov ebp, esp
|
mov ebp, esp
|
||||||
|
|
||||||
|
|
||||||
|
mov BYTE PTR [eax + -321], 5
|
||||||
|
mov BYTE PTR [eax + -320], 5
|
||||||
|
mov BYTE PTR [eax + -1], 5
|
||||||
mov BYTE PTR [eax + 0], 5
|
mov BYTE PTR [eax + 0], 5
|
||||||
mov BYTE PTR [eax + 1], 5
|
|
||||||
mov BYTE PTR [eax + 320], 5
|
|
||||||
mov BYTE PTR [eax + 321], 5
|
|
||||||
|
|
||||||
pop ebp
|
pop ebp
|
||||||
ret
|
ret
|
||||||
@ -1559,11 +1614,155 @@ sprite_shotgun_:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
sprite_shieldKiller_:
|
sprite_beam_:
|
||||||
push ebp
|
push ebp
|
||||||
mov ebp, esp
|
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
|
pop ebp
|
||||||
ret
|
ret
|
||||||
|
19
sprites.h
19
sprites.h
@ -2,6 +2,11 @@
|
|||||||
#define __SPRITES_H__
|
#define __SPRITES_H__
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "system/vga.h"
|
||||||
|
|
||||||
|
#define PALETTE_COLOR_COUNT (17)
|
||||||
|
|
||||||
|
extern struct VGAColor palette[17];
|
||||||
|
|
||||||
|
|
||||||
extern void sprite_arenaWallTop(byte *);
|
extern void sprite_arenaWallTop(byte *);
|
||||||
@ -61,9 +66,9 @@ extern void sprite_mouse(byte *);
|
|||||||
|
|
||||||
extern void sprite_bullet(byte *);
|
extern void sprite_bullet(byte *);
|
||||||
|
|
||||||
#define SPRITE_BULLET_WIDTH (4)
|
#define SPRITE_BULLET_WIDTH (2)
|
||||||
|
|
||||||
#define SPRITE_BULLET_HEIGHT (4)
|
#define SPRITE_BULLET_HEIGHT (2)
|
||||||
|
|
||||||
#define SPRITE_BULLET_OFFSET_X (1)
|
#define SPRITE_BULLET_OFFSET_X (1)
|
||||||
|
|
||||||
@ -92,15 +97,15 @@ extern void sprite_shotgun(byte *);
|
|||||||
#define SPRITE_SHOTGUN_OFFSET_Y (6)
|
#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]
|
dimensions: [8, 8]
|
||||||
offset: [4, 4]
|
offset: [4, 4]
|
||||||
bullet:
|
bullet:
|
||||||
position: [16, 28]
|
position: [17, 29]
|
||||||
dimensions: [4, 4]
|
dimensions: [2, 2]
|
||||||
offset: [1, 1]
|
offset: [1, 1]
|
||||||
enemy:
|
enemy:
|
||||||
position: [0, 20]
|
position: [0, 20]
|
||||||
@ -37,7 +37,7 @@ files:
|
|||||||
position: [32, 20]
|
position: [32, 20]
|
||||||
dimensions: [12, 12]
|
dimensions: [12, 12]
|
||||||
offset: [6, 6]
|
offset: [6, 6]
|
||||||
shieldKiller:
|
beam:
|
||||||
position: [44, 20]
|
position: [44, 20]
|
||||||
dimensions: [12, 12]
|
dimensions: [12, 12]
|
||||||
offset: [6, 6]
|
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_A = keystateBits[3] & 0x40;
|
||||||
keyboardKeydownState.KEY_S = keystateBits[3] & 0x80;
|
keyboardKeydownState.KEY_S = keystateBits[3] & 0x80;
|
||||||
keyboardKeydownState.KEY_D = keystateBits[4] & 0x01;
|
keyboardKeydownState.KEY_D = keystateBits[4] & 0x01;
|
||||||
|
keyboardKeydownState.KEY_I = keystateBits[2] & 0x80;
|
||||||
|
keyboardKeydownState.KEY_O = keystateBits[3] & 0x01;
|
||||||
|
keyboardKeydownState.KEY_P = keystateBits[3] & 0x02;
|
||||||
keyboardKeydownState.KEY_ESC = keystateBits[0] & 0x02;
|
keyboardKeydownState.KEY_ESC = keystateBits[0] & 0x02;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ struct KeyboardKeydownState {
|
|||||||
byte KEY_A;
|
byte KEY_A;
|
||||||
byte KEY_S;
|
byte KEY_S;
|
||||||
byte KEY_D;
|
byte KEY_D;
|
||||||
|
byte KEY_I;
|
||||||
|
byte KEY_O;
|
||||||
|
byte KEY_P;
|
||||||
byte KEY_ESC;
|
byte KEY_ESC;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
63
system/vga.c
63
system/vga.c
@ -2,6 +2,7 @@
|
|||||||
#include "pc_stuff.h"
|
#include "pc_stuff.h"
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -128,26 +129,68 @@ void setVGAColors(struct VGAColor colors[], int totalColors) {
|
|||||||
|
|
||||||
#define RENDER_STRING_FAILSAFE (80)
|
#define RENDER_STRING_FAILSAFE (80)
|
||||||
|
|
||||||
|
struct CharRowDetails {
|
||||||
|
char c;
|
||||||
|
char color;
|
||||||
|
char backgroundColor;
|
||||||
|
char writeBackgroundColor;
|
||||||
|
char* pos;
|
||||||
|
} charRowDetails;
|
||||||
|
|
||||||
|
// Write this in inline assembler for practice!
|
||||||
|
void plotCharRow();
|
||||||
|
#pragma aux plotCharRow = \
|
||||||
|
"mov al, [edi]" \
|
||||||
|
"mov ah, 8" \
|
||||||
|
"mov bl, [edi+1]" \
|
||||||
|
"mov bh, [edi+2]" \
|
||||||
|
"mov cl, [edi+3]" \
|
||||||
|
"mov edi, [edi + 4]" \
|
||||||
|
"nextBit: mov ch, al" \
|
||||||
|
"and ch,1" \
|
||||||
|
"cmp ch,1" \
|
||||||
|
"jne background" \
|
||||||
|
"mov [edi], bl" \
|
||||||
|
"jmp maybeKeepGoing" \
|
||||||
|
"background: cmp cl,0" \
|
||||||
|
"je maybeKeepGoing" \
|
||||||
|
"mov [edi], bh" \
|
||||||
|
"maybeKeepGoing: ror al, 1" \
|
||||||
|
"inc edi" \
|
||||||
|
"dec ah" \
|
||||||
|
"jnz nextBit" \
|
||||||
|
parm [edi] \
|
||||||
|
modify [eax ebx ecx edi];
|
||||||
|
|
||||||
void renderStringToDrawBuffer(char* buffer, int color, int backgroundColor, int x, int y) {
|
void renderStringToDrawBuffer(char* buffer, int color, int backgroundColor, int x, int y) {
|
||||||
char *bufferPos = buffer, currentCharacter, charRow;
|
char *bufferPos = buffer, currentCharacter, charRow;
|
||||||
int i, fontX, fontY, currentX = x;
|
int i, fontX, fontY, currentX = x;
|
||||||
|
|
||||||
|
int charX, fontYs[8];
|
||||||
|
|
||||||
|
charRowDetails.color = color;
|
||||||
|
if (backgroundColor == -1) {
|
||||||
|
charRowDetails.backgroundColor = 0;
|
||||||
|
}
|
||||||
|
charRowDetails.writeBackgroundColor = backgroundColor != -1;
|
||||||
|
|
||||||
|
for (fontY = 0; fontY < 8; ++fontY) {
|
||||||
|
fontYs[fontY] = (y + fontY) * VGA_DISPLAY_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
charX = x;
|
||||||
|
|
||||||
for (i = 0; i < RENDER_STRING_FAILSAFE; ++i) {
|
for (i = 0; i < RENDER_STRING_FAILSAFE; ++i) {
|
||||||
currentCharacter = *(bufferPos++);
|
currentCharacter = *(bufferPos++);
|
||||||
if (!currentCharacter) break;
|
if (!currentCharacter) break;
|
||||||
|
|
||||||
for (fontY = 0; fontY < 8; ++fontY) {
|
for (fontY = 0; fontY < 8; ++fontY) {
|
||||||
charRow = font8x8_basic[currentCharacter][fontY];
|
charRowDetails.c = font8x8_basic[currentCharacter][fontY];
|
||||||
|
charRowDetails.pos = drawBuffer + fontYs[fontY] + charX;
|
||||||
|
|
||||||
for (fontX = 0; fontX < 8; ++fontX) {
|
plotCharRow(&charRowDetails);
|
||||||
if (charRow & (1 << fontX)) {
|
|
||||||
drawBuffer[(y + fontY) * VGA_DISPLAY_WIDTH + (i * 8 + x + fontX)] = color;
|
|
||||||
} else {
|
|
||||||
if (backgroundColor > -1) {
|
|
||||||
drawBuffer[(y + fontY) * VGA_DISPLAY_WIDTH + (i * 8 + x + fontX)] = backgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
charX += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
test.c
22
test.c
@ -1,20 +1,38 @@
|
|||||||
|
#include "const.h"
|
||||||
#include "cutest-1.5/CuTest.h"
|
#include "cutest-1.5/CuTest.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
CuSuite *SpawnGetSuite();
|
CuSuite *SpawnGetSuite();
|
||||||
|
CuSuite *PowerupGetSuite();
|
||||||
|
CuSuite *CombatGetSuite();
|
||||||
|
CuSuite *ConstGetSuite();
|
||||||
|
CuSuite *MovementGetSuite();
|
||||||
|
|
||||||
void RunAllTests(void) {
|
void beforeAll() {
|
||||||
|
buildDifficultyBands();
|
||||||
|
buildHitPointRages();
|
||||||
|
}
|
||||||
|
|
||||||
|
int RunAllTests(void) {
|
||||||
CuString *output = CuStringNew();
|
CuString *output = CuStringNew();
|
||||||
CuSuite *suite = CuSuiteNew();
|
CuSuite *suite = CuSuiteNew();
|
||||||
|
|
||||||
|
beforeAll();
|
||||||
|
|
||||||
CuSuiteAddSuite(suite, SpawnGetSuite());
|
CuSuiteAddSuite(suite, SpawnGetSuite());
|
||||||
|
CuSuiteAddSuite(suite, PowerupGetSuite());
|
||||||
|
CuSuiteAddSuite(suite, CombatGetSuite());
|
||||||
|
CuSuiteAddSuite(suite, ConstGetSuite());
|
||||||
|
CuSuiteAddSuite(suite, MovementGetSuite());
|
||||||
|
|
||||||
CuSuiteRun(suite);
|
CuSuiteRun(suite);
|
||||||
CuSuiteSummary(suite, output);
|
CuSuiteSummary(suite, output);
|
||||||
CuSuiteDetails(suite, output);
|
CuSuiteDetails(suite, output);
|
||||||
printf("%s\n", output->buffer);
|
printf("%s\n", output->buffer);
|
||||||
|
|
||||||
|
return suite->failCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
RunAllTests();
|
return RunAllTests();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user