diff --git a/res/enemies/fairy1.png b/res/enemies/fairy1.png new file mode 100644 index 0000000..acb0616 Binary files /dev/null and b/res/enemies/fairy1.png differ diff --git a/res/resources.res b/res/resources.res index 27788af..5dddaca 100644 --- a/res/resources.res +++ b/res/resources.res @@ -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 diff --git a/src/enemies.h b/src/enemies.h new file mode 100644 index 0000000..3926f6d --- /dev/null +++ b/src/enemies.h @@ -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); +} \ No newline at end of file diff --git a/src/foreground.h b/src/foreground.h index 2753e82..f9532bf 100644 --- a/src/foreground.h +++ b/src/foreground.h @@ -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,33 +38,28 @@ 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){ - 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)), - 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); + obstacles[i].startX = obsX; + for(u8 j = 0; j < 4; j++){ + VDP_drawImageEx(BG_B, + 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); } } } @@ -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); } \ No newline at end of file diff --git a/src/global.h b/src/global.h index 65e4cd7..3d95b75 100644 --- a/src/global.h +++ b/src/global.h @@ -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; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c index fb3a078..2184241 100644 --- a/src/main.c +++ b/src/main.c @@ -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(); diff --git a/src/stage.h b/src/stage.h new file mode 100644 index 0000000..7c746e7 --- /dev/null +++ b/src/stage.h @@ -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; +} \ No newline at end of file diff --git a/src/structs.h b/src/structs.h index 4235cbc..deac91b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -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]; \ No newline at end of file