start enemy work

This commit is contained in:
t. boddy 2022-08-17 20:39:28 -04:00
parent 78a032a6d9
commit 60aa78f3f1
8 changed files with 233 additions and 21 deletions

BIN
res/enemies/fairy1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -18,6 +18,8 @@ IMAGE rock1rt "fg/rock1rt.png" FAST
SPRITE nitori "player/nitori.png" 4 4 FAST
SPRITE fairy1 "enemies/fairy1.png" 6 4 FAST
SPRITE smallRedBullet "bullets/smallred.png" 1 1 FAST 5
SPRITE smallBlueBullet "bullets/smallblue.png" 1 1 FAST 5
SPRITE smallGreenBullet "bullets/smallgreen.png" 1 1 FAST 5

96
src/enemies.h Normal file
View File

@ -0,0 +1,96 @@
// enemies
// lifecycle
void spawnEnemy(struct enemySpawner spawner, void(*updater), void(*suicide)){
s16 i = -1;
for(s16 j = 0; j < ENEMY_COUNT; j++) if(!enemies[j].active && i == -1) i = j;
if(i > -1){
enemies[i].active = TRUE;
enemies[i].off.x = FIX16(spawner.offX);
enemies[i].off.y = FIX16(spawner.offY);
enemies[i].pos.x = FIX16(spawner.x);
enemies[i].pos.y = FIX16(spawner.y);
enemies[i].dist = intToFix32(spawner.offX - 2);
enemies[i].speed = spawner.speed;
enemies[i].angle = spawner.angle;
enemies[i].boss = spawner.boss;
enemies[i].shotClock = 0;
enemies[i].dead = FALSE;
enemies[i].health = spawner.health ? spawner.health : 1;
if(spawner.vel.x && spawner.vel.y){
enemies[i].vel.x = spawner.vel.x;
enemies[i].vel.y = spawner.vel.y;
} else {
enemies[i].vel.x = fix16Mul(cosFix16(spawner.angle), spawner.speed);
enemies[i].vel.y = fix16Mul(sinFix16(spawner.angle), spawner.speed);
}
for(s16 j = 0; j < COUNT_INT; j++){
enemies[i].bools[j] = spawner.bools[j];
enemies[i].ints[j] = spawner.ints[j];
enemies[i].fixes[j] = spawner.fixes[j];
}
enemies[i].updater = updater;
enemies[i].suicide = suicide;
enemies[i].clock = 0;
enemies[i].seen = FALSE;
enemies[i].image = SPR_addSprite(spawner.image,
fix16ToInt(fix16Sub(enemies[i].pos.x, enemies[i].off.x)),
fix16ToInt(fix16Sub(enemies[i].pos.y, enemies[i].off.y)),
TILE_ATTR(PAL1, 0, 0, 0));
SPR_setAnim(enemies[i].image, spawner.anim ? spawner.anim : 0);
SPR_setFrame(enemies[i].image, spawner.frame ? spawner.frame : 0);
SPR_setDepth(enemies[i].image, 5);
SPR_setVisibility(enemies[i].image, HIDDEN);
}
}
void killEnemy(s16 i){
enemies[i].dead = TRUE;
}
// helpers
void updateEnemyVel(s16 i){
enemies[i].vel.x = fix16Mul(cosFix16(enemies[i].angle), enemies[i].speed);
enemies[i].vel.y = fix16Mul(sinFix16(enemies[i].angle), enemies[i].speed);
}
// loop
static void updateEnemy(s16 i){
if(enemies[i].seen && (enemies[i].pos.x < fix16Sub(0, enemies[i].off.x) ||
enemies[i].pos.x > fix16Add(FIX16(GAME_W), enemies[i].off.x) ||
enemies[i].pos.y < fix16Sub(0, enemies[i].off.y) ||
enemies[i].pos.y > fix16Add(FIX16(GAME_H), enemies[i].off.y))){
killEnemy(i);
enemies[i].suicide(i);
} else {
if(!enemies[i].seen && enemies[i].pos.x <= fix16Add(FIX16(GAME_W), enemies[i].off.x)){
enemies[i].seen = TRUE;
SPR_setVisibility(enemies[i].image, VISIBLE);
}
enemies[i].pos.x = fix16Add(enemies[i].pos.x, enemies[i].vel.x);
enemies[i].pos.y = fix16Add(enemies[i].pos.y, enemies[i].vel.y);
if(enemies[i].seen){
enemies[i].updater(i);
if(enemies[i].boss) bossHealth = enemies[i].health;
// collideEnemy(i);
enemies[i].clock++;
if(enemies[i].clock >= CLOCK_LIMIT) enemies[i].clock -= CLOCK_LIMIT;
SPR_setPosition(enemies[i].image,
fix16ToInt(fix16Sub(enemies[i].pos.x, enemies[i].off.x)),
fix16ToInt(fix16Sub(enemies[i].pos.y, enemies[i].off.y)));
}
}
if(enemies[i].dead){
enemies[i].active = FALSE;
SPR_releaseSprite(enemies[i].image);
}
}
void updateEnemies(){
for(s16 i = 0; i < ENEMY_COUNT; i++) if(enemies[i].active) updateEnemy(i);
}

View File

@ -1,7 +1,7 @@
// foreground
#define FG_I 576
#define FG_W GAME_W_T + 30
#define FG_W 64
#define OBSTACLE_COUNT 8
#define FG_SPEED FIX16(2)
@ -38,34 +38,29 @@ static void drawFg(){
#define OBS_TOP_Y FIX16(56)
#define OBS_BOTTOM_Y FIX16(160)
s16 fgPos;
s16 fgPos, obsX;
static void spawnObstacle(bool top){
s16 i = -1;
for(s16 j = 0; j < OBSTACLE_COUNT; j++) if(!obstacles[j].active && i == -1) i = j;
if(i > -1){
// obsX = OBS_X;
obsX = OBS_X + (fgPos != 0 ? (-fgPos / 8) : 0);
obstacles[i].active = TRUE;
obstacles[i].top = top;
obstacles[i].pos.x = FIX16(256);
obstacles[i].pos.y = top ? OBS_TOP_Y : OBS_BOTTOM_Y;
obstacles[i].size.x = FIX16(64);
obstacles[i].size.y = FIX16(32);
obstacles[i].startX = OBS_X;
if(top){
obstacles[i].startX = obsX;
for(u8 j = 0; j < 4; j++){
VDP_drawImageEx(BG_B,
j == 0 ? &rock1lt : (j == 1 ? &rock1t : (j == 2 ? &rock1rt : &rock1b)),
TILE_ATTR_FULL(PAL2, 1, 0, 0, FG_I + (j == 0 ? 32 : (j == 1 ? 48 : (j == 2 ? 64 : 80)))),
OBS_X + (j == 1 || j == 3 ? 2 : (j == 2 ? 6 : 0)),
OBS_CEIL_Y - (j == 0 || j == 2 ? 2 : (j == 3 ? 4 : 0)),
top ? (j == 0 ? &rock1lt : (j == 1 ? &rock1t : (j == 2 ? &rock1rt : &rock1b))) : (j == 0 ? &rock1l : (j == 1 ? &rock1 : (j == 2 ? &rock1r : &rock1b))),
TILE_ATTR_FULL(PAL2, 1, 0, 0, FG_I + (top ? (j == 0 ? 32 : (j == 1 ? 48 : (j == 2 ? 64 : 80))) : (j == 0 ? 112 : (j == 1 ? 128 : (j == 2 ? 144 : 80))))),
obsX + (top ? (j == 1 || j == 3 ? 2 : (j == 2 ? 6 : 0)) : ((j == 1 || j == 3 ? 2 : (j == 2 ? 6 : 0)))),
top ? (OBS_CEIL_Y - (j == 0 || j == 2 ? 2 : (j == 3 ? 4 : 0))) : (OBS_FLOOR_Y + (j == 3 ? 4 : 0)),
0, DMA_QUEUE);
}
} else {
VDP_drawImageEx(BG_B, &rock1l, TILE_ATTR_FULL(PAL2, 1, 0, 0, FG_I + 32 + 80), OBS_X, OBS_FLOOR_Y, 0, DMA_QUEUE);
VDP_drawImageEx(BG_B, &rock1, TILE_ATTR_FULL(PAL2, 1, 0, 0, FG_I + 48 + 80), OBS_X + 2, OBS_FLOOR_Y, 0, DMA_QUEUE);
VDP_drawImageEx(BG_B, &rock1r, TILE_ATTR_FULL(PAL2, 1, 0, 0, FG_I + 64 + 80), OBS_X + 2 + 4, OBS_FLOOR_Y, 0, DMA_QUEUE);
VDP_drawImageEx(BG_B, &rock1b, TILE_ATTR_FULL(PAL2, 1, 0, 0, FG_I + 80), OBS_X + 2, OBS_FLOOR_Y + 4, 0, DMA_QUEUE);
}
}
}
@ -93,7 +88,7 @@ static void updateObstacle(s16 i){
// update
#define FG_SIZE 32
#define FG_LIMIT FIX16(-320)
#define FG_LIMIT FIX16(-512)
#define FG_SIZE_F 40
@ -117,12 +112,11 @@ static void scrollFg(){
void loadFg(){
drawFg();
spawnObstacle(FALSE);
spawnObstacle(TRUE);
}
void updateFg(){
VDP_setHorizontalScrollTile(BG_B, 0, fgPosX, GAME_H_T, DMA);
scrollFg();
for(s16 i = 0; i < OBSTACLE_COUNT; i++) if(obstacles[i].active) updateObstacle(i);
if(clock % 60 == 0) spawnObstacle(clock % 120 < 60);
}

View File

@ -19,7 +19,8 @@
bool killBullets,
started;
s16 clock;
s16 clock,
bossHealth, bossMax;
u32 score,
highScore;
@ -30,3 +31,20 @@ void EMPTY(s16 i){emptyI = i;}
void loadGame();
void collideObstacleWithPlayer(s16);
fix16 honeSpeed;
Vect2D_f16 hPos, velPos;
Vect2D_f16 hone(Vect2D_f16 pos, Vect2D_f16 target, fix16 speed, s16 lerp){
hPos.x = target.x;
hPos.y = target.y;
// if(lerp > 0){
// hPos.x = fix16Add(fix16Sub(hPos.x, FIX16(lerp)), FIX16(random() % (lerp * 2)));
// hPos.y = fix16Add(fix16Sub(hPos.y, FIX16(lerp)), FIX16(random() % (lerp * 2)));
// }
honeSpeed = fix32ToFix16(getApproximatedDistance(
fix32Sub(fix16ToFix32(pos.x), fix16ToFix32(hPos.x)),
fix32Sub(fix16ToFix32(pos.y), fix16ToFix32(hPos.y))));
hPos.x = fix16Mul(fix16Div(fix16Sub(hPos.x, pos.x), honeSpeed), speed);
hPos.y = fix16Mul(fix16Div(fix16Sub(hPos.y, pos.y), honeSpeed), speed);
return hPos;
}

View File

@ -11,6 +11,8 @@
#include "foreground.h"
#include "bullets.h"
#include "player.h"
#include "enemies.h"
#include "stage.h"
#include "chrome.h"
// game loop
@ -36,6 +38,8 @@ void loadGame(){
static void updateGame(){
updatePlayer();
updateEnemies();
updateStage();
updateBullets();
updateBg();
updateFg();

59
src/stage.h Normal file
View File

@ -0,0 +1,59 @@
// stage
#define SPAWN_MID 124
// waves
static void waveOne(){
struct enemySpawner spawner = {
.angle = 512,
.speed = FIX16(0.75),
.x = GAME_W + 24,
.y = SPAWN_MID,
.image = &fairy1,
.offX = 24,
.offY = 16
};
spawner.fixes[0] = FIX16(spawner.y);
spawner.fixes[1] = FIX16(8);
spawner.fixes[2] = FIX16(12);
spawner.fixes[3] = FIX16(0.1);
void updater(s16 i){
enemies[i].pos.y = fix16Sub(enemies[i].fixes[0], fix16Mul(sinFix16(enemies[i].fixes[1]), enemies[i].fixes[2]));
enemies[i].fixes[1] = fix16Add(enemies[i].fixes[1], enemies[i].fixes[3]);
if(enemies[i].clock == 30){
struct bulletSpawner spawner = {
.x = enemies[i].pos.x,
.y = enemies[i].pos.y,
.image = &smallRedBullet,
.angle = 512,
.speed = FIX16(1.5)
};
spawner.vel = hone(enemies[i].pos, player.pos, spawner.speed, 0);
spawnBullet(spawner, EMPTY);
}
}
for(u8 j = 0; j < 3; j++){
spawnEnemy(spawner, updater, EMPTY);
spawner.x += 50;
}
}
// loop
s16 stageClock;
static void updateWaves(){
switch(stageClock){
case 5: waveOne(); break;
}
}
void updateStage(){
updateWaves();
stageClock++;
if(stageClock >= CLOCK_LIMIT) stageClock -= CLOCK_LIMIT;
}

View File

@ -1,5 +1,8 @@
// structs
// bullets
#define BULLET_COUNT 64
struct bulletSpawner {
fix16 x, y, speed;
@ -26,9 +29,45 @@ struct bullet {
};
struct bullet bullets[BULLET_COUNT];
// player
struct playerStruct {
Vect2D_f16 pos, vel;
Sprite* image;
s16 clock, invincibleClock, shotClock;
};
struct playerStruct player;
// enemies
#define ENEMY_COUNT 32
struct enemySpawner {
fix16 speed;
s16 angle, offX, offY, x, y, anim, health, frame;
Vect2D_f16 vel;
Sprite* image;
bool boss, seal;
bool bools[COUNT_INT];
s16 ints[COUNT_INT];
fix16 fixes[COUNT_INT];
};
struct enemy {
bool active, boss, seen, seal, dead;
fix16 speed;
fix32 dist;
Vect2D_f16 pos, vel, off;
s16 angle, clock, health, shotClock;
Sprite* image;
Sprite* sealImage;
void (*updater)(s16);
void (*bossUpdater)(s16);
void (*suicide)(s16);
bool bools[COUNT_INT];
s16 ints[COUNT_INT];
fix16 fixes[COUNT_INT];
};
struct enemy enemies[ENEMY_COUNT];