From de29b11a88dbdd3af0824e59b51528b91ee73c54 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 30 Nov 2017 22:49:38 +0100 Subject: First commit. Version works on Linux (keyboard only, not configurable) --- src/enemies/bat.c | 172 +++++++ src/enemies/bat.h | 16 + src/enemies/batboss.c | 319 +++++++++++++ src/enemies/batboss.h | 18 + src/enemies/bee.c | 214 +++++++++ src/enemies/bee.h | 16 + src/enemies/boar.c | 221 +++++++++ src/enemies/boar.h | 18 + src/enemies/boomknight.c | 285 +++++++++++ src/enemies/boomknight.h | 27 ++ src/enemies/crab.c | 509 ++++++++++++++++++++ src/enemies/crab.h | 30 ++ src/enemies/devil.c | 458 ++++++++++++++++++ src/enemies/devil.h | 28 ++ src/enemies/dodo.c | 587 +++++++++++++++++++++++ src/enemies/dodo.h | 22 + src/enemies/dog.c | 294 ++++++++++++ src/enemies/dog.h | 17 + src/enemies/firewheel.c | 272 +++++++++++ src/enemies/firewheel.h | 18 + src/enemies/fish.c | 139 ++++++ src/enemies/fish.h | 24 + src/enemies/garm.c | 578 +++++++++++++++++++++++ src/enemies/garm.h | 31 ++ src/enemies/gas.c | 116 +++++ src/enemies/gas.h | 18 + src/enemies/ghoul.c | 218 +++++++++ src/enemies/ghoul.h | 22 + src/enemies/golem.c | 199 ++++++++ src/enemies/golem.h | 16 + src/enemies/gyra.c | 332 +++++++++++++ src/enemies/gyra.h | 19 + src/enemies/heads.c | 838 ++++++++++++++++++++++++++++++++ src/enemies/heads.h | 89 ++++ src/enemies/hydra.c | 1127 ++++++++++++++++++++++++++++++++++++++++++++ src/enemies/hydra.h | 65 +++ src/enemies/jellyfish.c | 195 ++++++++ src/enemies/jellyfish.h | 16 + src/enemies/knight.c | 232 +++++++++ src/enemies/knight.h | 23 + src/enemies/lolidra.c | 367 +++++++++++++++ src/enemies/lolidra.h | 40 ++ src/enemies/pendulum.c | 78 +++ src/enemies/pendulum.h | 19 + src/enemies/podoboo.c | 176 +++++++ src/enemies/podoboo.h | 20 + src/enemies/poisonknight.c | 318 +++++++++++++ src/enemies/poisonknight.h | 26 + src/enemies/pumpkin.c | 375 +++++++++++++++ src/enemies/pumpkin.h | 27 ++ src/enemies/seal.c | 204 ++++++++ src/enemies/seal.h | 14 + src/enemies/skeleton.c | 298 ++++++++++++ src/enemies/skeleton.h | 31 ++ src/enemies/skull.c | 174 +++++++ src/enemies/skull.h | 15 + src/enemies/slime.c | 228 +++++++++ src/enemies/slime.h | 17 + src/enemies/slug.c | 164 +++++++ src/enemies/slug.h | 13 + src/enemies/thwomp.c | 265 +++++++++++ src/enemies/thwomp.h | 18 + src/enemies/waterjumper.c | 356 ++++++++++++++ src/enemies/waterjumper.h | 23 + src/enemies/wizard.c | 133 ++++++ src/enemies/wizard.h | 20 + 66 files changed, 11257 insertions(+) create mode 100644 src/enemies/bat.c create mode 100644 src/enemies/bat.h create mode 100644 src/enemies/batboss.c create mode 100644 src/enemies/batboss.h create mode 100644 src/enemies/bee.c create mode 100644 src/enemies/bee.h create mode 100644 src/enemies/boar.c create mode 100644 src/enemies/boar.h create mode 100644 src/enemies/boomknight.c create mode 100644 src/enemies/boomknight.h create mode 100644 src/enemies/crab.c create mode 100644 src/enemies/crab.h create mode 100644 src/enemies/devil.c create mode 100644 src/enemies/devil.h create mode 100644 src/enemies/dodo.c create mode 100644 src/enemies/dodo.h create mode 100644 src/enemies/dog.c create mode 100644 src/enemies/dog.h create mode 100644 src/enemies/firewheel.c create mode 100644 src/enemies/firewheel.h create mode 100644 src/enemies/fish.c create mode 100644 src/enemies/fish.h create mode 100644 src/enemies/garm.c create mode 100644 src/enemies/garm.h create mode 100644 src/enemies/gas.c create mode 100644 src/enemies/gas.h create mode 100644 src/enemies/ghoul.c create mode 100644 src/enemies/ghoul.h create mode 100644 src/enemies/golem.c create mode 100644 src/enemies/golem.h create mode 100644 src/enemies/gyra.c create mode 100644 src/enemies/gyra.h create mode 100644 src/enemies/heads.c create mode 100644 src/enemies/heads.h create mode 100644 src/enemies/hydra.c create mode 100644 src/enemies/hydra.h create mode 100644 src/enemies/jellyfish.c create mode 100644 src/enemies/jellyfish.h create mode 100644 src/enemies/knight.c create mode 100644 src/enemies/knight.h create mode 100644 src/enemies/lolidra.c create mode 100644 src/enemies/lolidra.h create mode 100644 src/enemies/pendulum.c create mode 100644 src/enemies/pendulum.h create mode 100644 src/enemies/podoboo.c create mode 100644 src/enemies/podoboo.h create mode 100644 src/enemies/poisonknight.c create mode 100644 src/enemies/poisonknight.h create mode 100644 src/enemies/pumpkin.c create mode 100644 src/enemies/pumpkin.h create mode 100644 src/enemies/seal.c create mode 100644 src/enemies/seal.h create mode 100644 src/enemies/skeleton.c create mode 100644 src/enemies/skeleton.h create mode 100644 src/enemies/skull.c create mode 100644 src/enemies/skull.h create mode 100644 src/enemies/slime.c create mode 100644 src/enemies/slime.h create mode 100644 src/enemies/slug.c create mode 100644 src/enemies/slug.h create mode 100644 src/enemies/thwomp.c create mode 100644 src/enemies/thwomp.h create mode 100644 src/enemies/waterjumper.c create mode 100644 src/enemies/waterjumper.h create mode 100644 src/enemies/wizard.c create mode 100644 src/enemies/wizard.h (limited to 'src/enemies') diff --git a/src/enemies/bat.c b/src/enemies/bat.c new file mode 100644 index 0000000..90ff12f --- /dev/null +++ b/src/enemies/bat.c @@ -0,0 +1,172 @@ +#include "bat.h" +#include "../game.h" +#include "../PHL.h" +#include "../hero.h" +#include +#include + +void batStep(Bat* b); +void batDraw(Bat* b); + +void createBat(int x, int y, int type) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* result = /*(Enemy*)*/malloc(sizeof *result); + Bat* b = /*(Bat*)*/malloc(sizeof *b); + b->id = i; + + b->x = b->xstart = x; + b->y = b->ystart = y; + b->type = type; + + b->imageIndex = 5; + b->counter = 0; + b->timer = 0; + b->state = 0; + b->dir = 1; + + result->data = b; + result->enemyStep = batStep; + result->enemyDraw = batDraw; + result->type = 1; + + enemies[i] = result; + i = MAX_ENEMIES; + } + } +} + +void batStep(Bat* b) +{ + //Wait + if (b->state == 0) + { + //Animate + { + b->imageIndex = 5; + } + + //wait for hero to get near + { + if (b->timer <= 0) { + Mask area; + area.circle = 0; + area.unused = 0; + area.x = b->xstart - 60; + area.y = b->ystart; + area.w = 160; area.h = 100; + + if (checkCollisionXY(area, herox, heroy + 20)) { + PHL_PlaySound(sounds[sndPi07], CHN_ENEMIES); + b->state = 1; + b->timer = 270; + if (b->type == 1) { + b->counter = 1; + if (herox < b->x + 20) { + b->dir = -1; + }else{ + b->dir = 1; + } + } + } + }else{ + b->timer -= 1; + } + } + } + //Fly + else if (b->state == 1) + { + //Animate + { + b->imageIndex += 0.25; + if (b->imageIndex >= 5) { + b->imageIndex -= 5; + } + } + + //Rotation angle + { + b->timer += 4; + if (b->timer >= 360) { + b->timer -= 360; + } + } + + //Movement + { + b->y = b->ystart + 30 + (30 * sin(b->timer * 3.14159 / 180)); + //Red bat + if (b->type == 1) { + b->x += 2 * b->dir; + } + } + + //Return to perch + { + if (b->timer == 270) { + if (b->type == 1 && b->counter > 0) { + b->dir *= -1; + b->timer = 270; + b->counter -= 1; + }else{ + b->state = 0; + b->timer = 70; + } + } + } + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 32; + mask.h = 28; + mask.x = b->x + ((40 - mask.w) / 2); + mask.y = b->y; + } + + //Hit Player + { + if (checkCollision(mask, heroMask)) { + heroHit(10, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + //Death + createEffect(2, b->x - 12, b->y - 6); + spawnCollectable(b->x + 20, b->y); + enemyDestroy(b->id); + + i = MAX_WEAPONS; + } + } + } + } + +} + +void batDraw(Bat* b) +{ + int cropX = 0, + cropY = 120; + + if (b->type == 1) { + cropX = 400; + cropY = 280; + } + + cropX += (int)b->imageIndex * 40; + + PHL_DrawSurfacePart(b->x, b->y - 4, cropX, cropY, 40, 40, images[imgEnemies]); +} \ No newline at end of file diff --git a/src/enemies/bat.h b/src/enemies/bat.h new file mode 100644 index 0000000..bd42156 --- /dev/null +++ b/src/enemies/bat.h @@ -0,0 +1,16 @@ +#ifndef BAT_H +#define BAT_H + +typedef struct { + int id; + double x, y; + int xstart, ystart; + int type; //0 = gray | 1 = red + int dir; + double imageIndex; + int counter, timer, state; +} Bat; + +void createBat(int x, int y, int type); + +#endif \ No newline at end of file diff --git a/src/enemies/batboss.c b/src/enemies/batboss.c new file mode 100644 index 0000000..8ea408d --- /dev/null +++ b/src/enemies/batboss.c @@ -0,0 +1,319 @@ +#include "batboss.h" +#include "../game.h" +#include "../PHL.h" +#include "../hero.h" +#include "heads.h" +#include +#include + +int boss2flag = 5; + +//void updateBatMask(Batboss* b); +void batbossStep(Batboss* b); +void batbossDraw(Batboss* b); + +void createBatboss(int x, int y) +{ + if (flags[boss2flag] == 0) { //have not beaten boss 2 + PHL_FreeSurface(images[imgBoss]); + images[imgBoss] = PHL_LoadQDA("boss03.bmp"); + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + setBossRoom(); + + Enemy* e = /*(Enemy*)*/malloc(sizeof *e); + Batboss* b = /*(Batboss*)*/malloc(sizeof *b); + b->id = i; + + b->x = x; + b->y = y; + + b->vsp = 0; + b->hsp = 0; + b->grav = 0.1; + + b->imageIndex = 0; + + b->ypos = y; + b->rot = 0; + + b->hp = 35; + + b->invincible = 0; + b->state = 0; + b->timer = 0; + b->mode = 0; //0 for flame, 1 for tornado stomp + + /* + b->mask.unused = b->mask.circle = 0; + b->mask.w = 100; + b->mask.h = 68; + updateBatMask(b); + */ + //Setup phase + b->state = 0; + b->hsp = 2; + b->ypos = b->y - 24; + b->timer = 60; + + e->data = b; + e->enemyStep = batbossStep; + e->enemyDraw = batbossDraw; + e->type = 41; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + } +} + +void batbossStep(Batboss* b) +{ + char dead = 0; + + //Animate + { + //Wing flap + if (b->state == 0 || b->state == 1 || b->state == 2 || b->state == 5 || b->state == 6) { + b->imageIndex += 0.1; + if (b->imageIndex >= 2) { + b->imageIndex -= 2; + } + } + //Twister + if (b->state == 3 || b->state == 4) { + b->imageIndex += 0.2; + if (b->imageIndex >= 5) { + b->imageIndex -= 3; + } + } + } + + //Counters + { + if (b->timer > 0) { + b->timer -= 1; + } + + if (b->invincible > 0) { + b->invincible -= 1; + } + } + + //Large vertical movement + { + if (b->state == 0 || b->state == 1) { + b->rot += 2; + if (b->rot >= 360) { b->rot -= 360; } + + b->y = b->ypos - (40 * sin(b->rot * 3.14159 / 180)); + } + } + + //Small vertical movement + { + if (b->state == 2) { + b->rot += 2; + if (b->rot >= 360) { b->rot -= 360; } + + b->y = b->ypos - (20 * sin(b->rot * 3.14159 / 180)); + } + } + + //Horizontal movement + if (b->state == 0) { + b->x += b->hsp; + + if (b->x >= 520 || b->x <= 120) { //Hit walls + b->hsp *= -1; + } + + if (b->timer <= 0) { + b->state = 1; + } + } + //Slow to halt + else if (b->state == 1) { + b->x += b->hsp; + + if (b->x >= 520 || b->x <= 120) { //Hit walls + b->hsp *= -1; + } + + double rate = 0.03; + if (b->hsp > 0) { + b->hsp -= rate; + if (b->hsp <= 0) { b->hsp = 0; } + } + else if (b->hsp < 0) { + b->hsp += rate; + if (b->hsp >= 0) { b->hsp = 0; } + } + + if (b->hsp == 0 && b->rot <= 2) { + b->state = 2; + b->timer = 60; + } + } + else if (b->state == 2) { + if (b->timer == 1) { + //Shoot flame + int fx = b->x; + int fy = b->y + 24; + int fangle = (atan2(heroy - fy, fx - (herox - 20)) * 180 / 3.14159) + 270; + createFireball(fx, fy, fangle, b->id); + createFireball(fx, fy, fangle - 15, b->id); + createFireball(fx, fy, fangle + 15, b->id); + PHL_PlaySound(sounds[sndShot03], CHN_ENEMIES); + } + + if (b->timer <= 0 && b->rot <= 2) { + if (b->mode == 0) { + b->state = 0; + b->timer = 60; + b->hsp = 2; + b->mode = 1; + } + else{ + b->mode = 0; + b->state = 3; + b->imageIndex = 2; + b->vsp = -4; + PHL_PlaySound(sounds[sndShot06], CHN_ENEMIES); + } + + if (herox < b->x) { + b->hsp *= -1; + } + } + } + //Stomp + else if (b->state == 3) { + b->y += b->vsp; + b->vsp += b->grav; + if (b->vsp >= 6) { b->vsp = 6; } + + //Hit floor + if (b->y >= 480 - 176) { + b->y = 480 - 176; + b->state = 4; + b->timer = 120; + quakeTimer = 30; + PHL_PlaySound(sounds[sndHit04], CHN_ENEMIES); + b->hsp = 1; + if (b->x > herox) { + b->hsp *= -1; + } + } + } + //Chase + else if (b->state == 4) { + b->x += b->hsp; + + if (b->timer <= 0 || b->x >= 520 || b->x <= 120) { + b->state = 5; + b->timer = 80 + (rand() % 61); + } + } + //Rise + else if (b->state == 5) { + b->y -= 1; + + if (b->timer <= 0) { + b->state = 0; + b->ypos = b->y; + b->rot = 0; + b->timer = 60; + + b->hsp = 2; + if (b->x > herox) { + b->hsp *= -1; + } + } + } + //Death + else if (b->state == 6) { + b->y += 0.2; + + if (b->timer % 12 == 0) { + createEffect(2, b->x - 64 + (rand() % 100), b->y + (rand() % 80)); + } + + if (b->timer <= 0) { + dead = 1; + } + } + + if (b->state != 6) { + //Setup Mask + Mask mask; + { + mask.unused = mask.circle = 0; + if (b->state == 3 || b->state == 4) { + mask.w = 64; + mask.h = 96; + mask.y = b->y; + }else{ + mask.w = 100; + mask.h = 68; + mask.y = b->y + 18; + } + mask.x = b->x - (mask.w / 2); + } + + //Hit Player + { + if (checkCollision(mask, getHeroMask())) { + heroHit(30, b->x); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + //Hit + b->invincible = 15; + b->hp -= 1; + + i = MAX_WEAPONS; + } + } + } + } + } + + if (b->hp <= 0) { + b->state = 6; + b->timer = 180; + b->invincible = 200; + } + } + + //Destroy object + { + if (dead == 1) { + enemyDestroy(b->id); + bossDefeatedFlag = 1; + roomSecret = 1; + + flags[boss2flag] = 1; + PHL_StopMusic(); + } + } + +} + +void batbossDraw(Batboss* b) +{ + if (b->invincible % 2 == 0) { + PHL_DrawSurfacePart(b->x - 64, b->y, (int)b->imageIndex * 128, 0, 128, 96, images[imgBoss]); + } +} \ No newline at end of file diff --git a/src/enemies/batboss.h b/src/enemies/batboss.h new file mode 100644 index 0000000..efac963 --- /dev/null +++ b/src/enemies/batboss.h @@ -0,0 +1,18 @@ +#ifndef BATBOSS_H +#define BATBOSS_H + +typedef struct { + int id; + double x, y; + double hsp, vsp, grav; + double imageIndex; + double ypos; + double rot; + int hp; + int state, timer, mode; + int invincible; +} Batboss; + +void createBatboss(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/bee.c b/src/enemies/bee.c new file mode 100644 index 0000000..1a6206f --- /dev/null +++ b/src/enemies/bee.c @@ -0,0 +1,214 @@ +#include "bee.h" +#include "../enemy.h" +#include "../game.h" +#include "../hero.h" +#include +#include + +void beeStep(Bee* b); +void beeDraw(Bee* b); + +void createBee(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = /*(Enemy*)*/malloc(sizeof *e); + Bee* b = /*(Bee*)*/malloc(sizeof *b); + b->id = i; + + b->x = x; + b->y = y; + b->xstart = b->x; + b->ystart = b->y; + + b->hsp = 0; + b->vsp = 0; + + b->timer = 0; + b->imageIndex = 0; + b->dir = 1; + b->state = 0; + + b->hoverdir = 180; + + if (dir == 1) { + b->hoverdir = 0; + b->dir = -1; + } + + e->data = b; + e->enemyStep = beeStep; + e->enemyDraw = beeDraw; + e->type = 24; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void beeStep(Bee* b) +{ + //Animate + { + b->imageIndex += 0.33; + if (b->imageIndex >= 3) { + b->imageIndex -= 3; + } + } + + //Mindless hovering + if (b->state == 0) + { + b->hoverdir += 2.6; + if (b->hoverdir >= 360) { + b->hoverdir -= 360; + } + + b->dir = 1; + if (b->hoverdir <= 180) { + b->dir = -1; + } + + b->x = b->xstart + (20 * cos(b->hoverdir * 3.14159 /180)); + + //If player is within range + Mask area; + area.unused = area.circle = 0; + area.x = b->x - 80; + area.y = b->y; + area.w = 200; + area.h = 100; + + if (checkCollision(area, getHeroMask())) { + b->state = 1; + + b->dir = 1; + if (b->x + 20 > herox) { + b->dir = -1; + } + + b->hsp = -5.5 * b->dir; + + PHL_PlaySound(sounds[sndBee01], CHN_ENEMIES); + } + } + //Fly backwards + else if (b->state == 1) + { + b->hsp += 0.25 * b->dir; + + if ((b->dir == 1 && b->hsp >= 0) || (b->dir == -1 && b->hsp <= 0)) { + b->hsp = 0; + b->state = 2; + b->vsp = 3.75; + } + } + //Fly downwards + else if (b->state == 2) + { + b->vsp -= 0.1; + if (b->vsp <= 0) { + b->state = 3; + b->vsp = 0; + + b->dir = 1; + if (b->x + 20 > herox) { + b->dir = -1; + } + b->hsp = 3 * b->dir; + } + } + //Fly diaganal + else if (b->state == 3) + { + b->vsp -= 0.1; + + if (b->vsp < -3) { + b->vsp = -3; + } + + if (b->y <= b->ystart) { + b->state = 4; + + b->vsp = 0; + b->y = b->ystart; + + if (b->x < b->xstart) { + b->dir = 1; + }else{ + b->dir = -1; + } + b->hsp = b->dir; + } + } + //Fly back to start + else if (b->state == 4) + { + if ((b->dir == 1 && b->x >= b->xstart) || (b->dir == -1 && b->x <= b->xstart)) { + b->state = 0; + b->hsp = 0; + + b->hoverdir = 0; + } + } + + //Movement + { + b->x += b->hsp; + b->y += b->vsp; + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 24; + mask.h = 32; + mask.y = b->y + 6; + mask.x = b->x + 14; + if (b->dir == -1) { + mask.x = b->x + 2; + } + } + + //Hit Player + { + if (checkCollision(mask, getHeroMask())) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon Collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + createEffect(2, b->x - 12, b->y - 6); + spawnCollectable(b->x + 20, b->y); + enemyDestroy(b->id); + + i = MAX_WEAPONS; + } + } + } + } + +} + +void beeDraw(Bee* b) +{ + int cropx = 280; + + if (b->dir == -1) { + cropx += 120; + } + + cropx += (int)b->imageIndex * 40; + + PHL_DrawSurfacePart(b->x, b->y, cropx, 480, 40, 40, images[imgEnemies]); +} \ No newline at end of file diff --git a/src/enemies/bee.h b/src/enemies/bee.h new file mode 100644 index 0000000..1230f71 --- /dev/null +++ b/src/enemies/bee.h @@ -0,0 +1,16 @@ +#ifndef BEE_H +#define BEE_H + +typedef struct { + int id; + double x, y; + int xstart, ystart; + double hsp, vsp; + double imageIndex; + int dir, state, timer; + double hoverdir; +} Bee; + +void createBee(int x, int y, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/boar.c b/src/enemies/boar.c new file mode 100644 index 0000000..d9582f5 --- /dev/null +++ b/src/enemies/boar.c @@ -0,0 +1,221 @@ +#include "boar.h" +#include "../game.h" +#include "../hero.h" +#include + +void boarStep(Boar* b); +void boarDraw(Boar* b); + +void createBoar(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = /*(Enemy*)*/malloc(sizeof *e); + Boar* b = /*(Boar*)*/malloc(sizeof *b); + + b->id = i; + + b->hp = 3; + + b->x = x; + b->y = y; + + b->hsp = 0; + + b->imageIndex = 0; + b->dir = 1; + + b->blink = 0; + + b->state = 0; + b->timer = 0; + + e->data = b; + e->enemyStep = boarStep; + e->enemyDraw = boarDraw; + e->type = 26; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void boarStep(Boar* b) +{ + //Setup mask + Mask mask; + { + mask.unused = mask.circle = 0; + mask.w = 32; + mask.h = 28; + mask.x = b->x + ((40 - mask.w) / 2); + mask.y = b->y + 40 - mask.h; + } + + //Blink animation + { + if (b->blink > 0) { + b->blink -= 1; + } + } + + //Patterns + { + //Dance + if (b->state == 0) + { + //Animate + b->imageIndex += 0.15; + if (b->imageIndex >= 8) { + b->imageIndex -= 8; + } + + //if player gets near + Mask area; + area.unused = area.circle = 0; + area.x = b->x - 80; + area.y = b->y - 40; + area.w = 200; + area.h = 80; + + if (checkCollision(area, getHeroMask()) == 1) { + b->state = 1; + b->timer = -1; + b->imageIndex = 0; + b->dir = 1; + if (herox < b->x + 20) { + b->dir = -1; + } + } + } + //Rev up + else if (b->state == 1) + { + b->timer += 1; + + //Play sound + if (b->timer % 10 == 0) { + PHL_PlaySound(sounds[sndShot01], CHN_ENEMIES); + } + + //Create effect + if (b->timer % 16 == 0) { + if (b->dir == 1) { + createEffectExtra(3, b->x + 20 - 30, b->y + 8, -1, 0, 0); + } + if (b->dir == -1) { + createEffectExtra(3, b->x + 20 - 10, b->y + 8, 1, 0, 0); + } + } + + if (b->timer >= 60) { + b->state = 2; + b->hsp = 3; + } + } + //Running + else if (b->state == 2) + { + b->x += b->hsp * b->dir; + mask.x = b->x + ((40 - mask.w) / 2); + + //Collide with wall + if (checkTileCollision(1, mask) == 1) { + b->x -= b->hsp * b->dir; + b->dir *= -1; + } + + //On edge + { + mask.x = b->x + ((40 - mask.w) / 2); + + mask.x += mask.w * b->dir; + mask.y += 1; + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + if (collide.x == -1) { + b->dir *= -1; + } + + mask.y -= 1; + mask.x = b->x + ((40 - mask.w) / 2); + } + + b->hsp -= 0.05; + if (b->hsp <= 0) { + b->state = 0; + } + } + + //Running animation + if (b->state == 1 || b->state == 2) { + //Animate + b->imageIndex += 0.2; + if (b->imageIndex >= 2) { + b->imageIndex -= 2; + } + } + } + + //Collide with hero + { + if (checkCollision(mask, getHeroMask())) { + heroHit(30, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + b->hp -= 1; + b->blink = 15; + + //Death + if (b->hp <= 0) { + createEffect(2, b->x - 12, b->y - 12); + spawnCollectable(b->x + 20, b->y); + enemyDestroy(b->id); + } + + i = MAX_WEAPONS; + } + } + } + } + } +} + +void boarDraw(Boar* b) +{ + if (b->blink % 2 == 0) + { + int cropx = 0, cropy = 360; + int drawx = b->x, drawy = b->y; + + //Dance + if (b->state == 0) { + int animation[8] = {0, 1, 2, 1, 0, 3, 4, 3}; + cropx = 160 + (animation[(int)b->imageIndex] * 40); + } + //Charge + else{ + cropx = (int)b->imageIndex * 40; + + if (b->dir == -1) { + cropx += 80; + } + } + + PHL_DrawSurfacePart(drawx, drawy, cropx, cropy, 40, 40, images[imgEnemies]); + } +} \ No newline at end of file diff --git a/src/enemies/boar.h b/src/enemies/boar.h new file mode 100644 index 0000000..8f4a61c --- /dev/null +++ b/src/enemies/boar.h @@ -0,0 +1,18 @@ +#ifndef BOAR_H +#define BOAR_H + +typedef struct { + int id; + int hp; + double x, y; + double hsp; + double imageIndex; + int blink; + int dir; + int state; + int timer; +} Boar; + +void createBoar(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/boomknight.c b/src/enemies/boomknight.c new file mode 100644 index 0000000..3fc3e83 --- /dev/null +++ b/src/enemies/boomknight.c @@ -0,0 +1,285 @@ +#include "boomknight.h" +#include "../game.h" +#include "../hero.h" +#include + +void boomknightStep(Boomknight* b); +void boomknightDraw(Boomknight* b); + +void boomStep(Boom* b); +void boomDraw(Boom* b); + +void createBoomknight(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Boomknight* b = malloc(sizeof *b); + b->id = i; + + b->hp = 2; + b->blink = 0; + + b->x = x; + b->y = y; + + b->dir = 1; + if (herox < b->x + 20) { + b->dir = -1; + } + + b->imageIndex = 0; + + b->state = 0; + b->timer = 0; + + e->data = b; + e->enemyStep = boomknightStep; + e->enemyDraw = boomknightDraw; + e->type = 31; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void boomknightStep(Boomknight* b) +{ + //Animate + { + b->imageIndex += 0.1; + if (b->imageIndex >= 2) { + b->imageIndex -= 2; + } + + if (b->blink > 0) { + b->blink -= 1; + } + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 30; + mask.h = 32; + mask.x = b->x + ((40 - mask.w) / 2); + mask.y = b->y + (40 - mask.h); + } + + //Walk + if (b->state == 0) { + //Movement + { + double hsp = 0.5; + b->x += hsp * b->dir; + mask.x = b->x + ((40 - mask.w) / 2); + } + + //Hit wall + { + if (checkTileCollision(1, mask) == 1) { + b->dir *= -1; + } + } + + //On edge + { + mask.x += mask.w * b->dir; + mask.y += 20; + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + if (collide.x == -1) { + b->dir *= -1; + } + } + + //Player is close + { + if (b->timer <= 0) { + Mask area; + { + area.circle = area.unused = 0; + area.w = 120; + area.h = 40; + area.x = b->x + 20; + if (b->dir == -1) { + area.x -= area.w; + } + area.y = b->y; + } + if (checkCollision(area, getHeroMask()) == 1) { + b->state = 1; + b->timer = 0; + } + + }else{ + b->timer -= 1; + } + } + + } + + //Throw + else if (b->state == 1) { + //Animate + { + b->imageIndex = 0; + if (b->timer >= 15) { + b->imageIndex = 2; + } + } + + b->timer += 1; + if (b->timer == 15) { + createBoom(b->x, b->y, b->dir); + PHL_PlaySound(sounds[sndPi05], CHN_ENEMIES); + } + + if (b->timer >= 110) { + b->state = 0; + b->imageIndex = 0; + b->timer = 120; + } + } + + //Update Mask + mask.x = b->x + ((40 - mask.w) / 2); + mask.y = b->y + (40 - mask.h); + + //Hero Collision + { + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon Collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + b->hp -= 1; + b->blink = 15; + + //Death + if (b->hp <= 0) { + createEffect(2, b->x - 12, b->y - 6); + spawnCollectable(b->x + 20, b->y); + enemyDestroy(b->id); + } + + i = MAX_WEAPONS; + } + } + } + } + } + +} + +void boomknightDraw(Boomknight* b) +{ + if (b->blink % 2 == 0) { + int cropX = 400 + ((int)b->imageIndex * 40); + + if (b->dir == -1) { + cropX += 120; + } + + PHL_DrawSurfacePart(b->x, b->y, cropX, 400, 40, 40, images[imgEnemies]); + } +} + + +//Enemy boomerang +void createBoom(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Boom* b = malloc(sizeof *b); + b->id = i; + + b->dir = dir; + b->x = x; + b->y = y; + + b->hsp = 6 * b->dir; + b->imageIndex = 0; + + b->timer = 90; + + e->data = b; + e->enemyStep = boomStep; + e->enemyDraw = boomDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void boomStep(Boom* b) +{ + //Animate + { + b->imageIndex += 0.33; + if (b->imageIndex >= 8) { + b->imageIndex -= 8; + } + } + + //Movement + { + b->x += b->hsp; + + double fric = 0.125; + b->hsp -= fric * b->dir; + } + + //Hero collision + { + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 24; + mask.h = 24; + mask.x = b->x + ((40 - mask.w) / 2); + mask.y = b->y + ((40 - mask.h) / 2); + } + + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(10, mask.x + (mask.w / 2)); + } + } + + b->timer -= 1; + if (b->timer <= 0) { + createEffectExtra(5, b->x + 20, b->y + 20, 0, 0, 0); + enemyDestroy(b->id); + } +} + +void boomDraw(Boom* b) +{ + int cropX = (int)b->imageIndex * 40; + + if (b->dir == -1) { + cropX += 320; + } + + PHL_DrawSurfacePart(b->x, b->y, cropX, 360, 40, 40, images[imgMisc20]); +} \ No newline at end of file diff --git a/src/enemies/boomknight.h b/src/enemies/boomknight.h new file mode 100644 index 0000000..b93d31f --- /dev/null +++ b/src/enemies/boomknight.h @@ -0,0 +1,27 @@ +#ifndef BOOMKNIGHT_H +#define BOOMKNIGHT_H + +typedef struct { + int id; + int hp; + int blink; + double x, y; + int dir; + double imageIndex; + int state, timer; +} Boomknight; + +void createBoomknight(int x, int y); + +typedef struct { + int id; + int dir; + double x, y; + double hsp; + double imageIndex; + int timer; +} Boom; + +void createBoom(int x, int y, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/crab.c b/src/enemies/crab.c new file mode 100644 index 0000000..b12860d --- /dev/null +++ b/src/enemies/crab.c @@ -0,0 +1,509 @@ +#include "crab.h" +#include "../PHL.h" +#include "../enemy.h" +#include "../game.h" +#include "../hero.h" +#include +#include + +int boss3flag = 13; + +void crabStep(Crab* c); +void crabDraw(Crab* c); +void updateCrabMask(Crab* c); +void crabDestroy(Crab* c); + +void electricityStep(Electricity* e); +void electricityDraw(Electricity* e); + +void createCrab(int x, int y) +{ + if (flags[boss3flag] == 0) { //have not beaten boss 3 + PHL_FreeSurface(images[imgBoss]); + images[imgBoss] = PHL_LoadQDA("boss06.bmp"); + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + //Boss start + setBossRoom(); + + Enemy* e = /*(Enemy*)*/malloc(sizeof(Enemy)); + Crab* c = /*(Crab*)*/malloc(sizeof(Crab)); + c->id = i; + + //c->hp = 1; + c->hp = 35; + c->invincible = 0; + + c->x = x; + c->y = y; + + c->vsp = 0; + c->hsp = 0; + + c->imageIndex = 0; + + c->state = 0; + c->timer = 0; + c->counter = 0; + + c->mask.unused = 0; + c->mask.circle = 1; + c->mask.w = 33; + c->mask.h = 33; + updateCrabMask(c); + + //Setup phase + c->timer = 60; + + e->data = c; + e->enemyStep = crabStep; + e->enemyDraw = crabDraw; + e->type = 42; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + } +} + +void crabStep(Crab* c) +{ + char dead = 0; + double grav = 0.15; + + if (c->invincible > 0) { + c->invincible -= 1; + } + + //Wait + if (c->state == 0) + { + c->imageIndex = 0; + + if (c->timer <= 0) { + c->timer = 0; + if (c->counter == 2 || c->counter == 5) { //Goto roll + c->state = 3; + if (c->counter == 5) { + c->counter = 0; + }else{ + c->counter = 3; + } + }else if (c->counter == 3) { + c->state = 2; + }else{ + c->state = 1; //Goto shoot + } + }else{ + c->timer -= 1; + } + } + //Shoot Electric orbs + else if (c->state == 1) + { + //Create orbs + if (c->timer == 0) { + PHL_PlaySound(sounds[sndPi05], CHN_ENEMIES); + + double angle = (atan2(heroy + 20 - (c->y + 60), c->x - (herox - 20)) * 180 / 3.14159) + 270; + createElectricity(c->x, c->y + 60, angle - 45, c->id); + createElectricity(c->x, c->y + 60, angle - 22.5, c->id); + createElectricity(c->x, c->y + 60, angle, c->id); + createElectricity(c->x, c->y + 60, angle + 22.5, c->id); + createElectricity(c->x, c->y + 60, angle + 45, c->id); + } + + if (c->timer >= 20) { + c->state = 2; + c->timer = 0; + }else{ + c->timer += 1; + } + } + //Leap + else if (c->state == 2) + { + c->imageIndex = 1; + + //Hopping down or hopping up + int hopup = 1; + if (c->counter > 2) { + hopup = 0; + } + + //Jump + if (c->timer == 0) { + PHL_PlaySound(sounds[sndJump02], CHN_ENEMIES); + + c->vsp = -6.5; + if (hopup == 0) { + c->vsp = -1.5; + } + c->timer = 1; + } + + //Vertical velocity + c->y += c->vsp; + c->vsp += grav; + + if (c->vsp >= 6) { + c->vsp = 6; + } + + //Check if onground + if ((hopup == 1 && c->vsp > 0) || (hopup == 0 && c->vsp >= 6)) { + Mask area; + area.unused = area.circle = 0; + area.w = 40; + area.h = 10; + area.x = c->x - (area.w / 2); + area.y = c->y + (80 - area.h); + + PHL_Rect collide = getTileCollision(1, area); + if (collide.x != -1) { + c->y = collide.y - 80; + c->state = 0; + c->counter += 1; + c->timer = 25; + if (c->counter == 2 || c->counter == 5) { + c->timer = 3; + } + } + } + } + //Roll hop + else if (c->state == 3) + { + //Animate + if (c->hsp > 0) { + c->imageIndex += 0.25; + } + if (c->hsp < 0) { + c->imageIndex -= 0.25; + } + if (c->imageIndex < 2) { c->imageIndex += 4; } + if (c->imageIndex >= 6) { c->imageIndex -= 4; } + + + if (c->timer == 0) { + PHL_PlaySound(sounds[sndHit04], CHN_ENEMIES); + + c->timer = 1; + c->vsp = -1.5; + c->imageIndex = 2; + if (c->x > 320) { + c->hsp = -8; + }else{ + c->hsp = 8; + } + } + + //Movement + c->y += c->vsp; + c->vsp += grav; + + //Check if onground + if (c->vsp > 0) { + Mask area; + area.unused = area.circle = 0; + area.w = 40; + area.h = 10; + area.x = c->x - (area.w / 2); + area.y = c->y + (80 - area.h); + + PHL_Rect collide = getTileCollision(1, area); + if (collide.x != -1) { + c->y = collide.y - 80; + c->state = 4; + } + } + } + //Roll + if (c->state == 4) + { + //Animate + if (c->hsp > 0) { + c->imageIndex += 0.25; + } + if (c->hsp < 0) { + c->imageIndex -= 0.25; + } + if (c->imageIndex < 2) { c->imageIndex += 4; } + if (c->imageIndex >= 6) { c->imageIndex -= 4; } + + //Movement + c->x += c->hsp; + + //Collide with wall + Mask area; + area.unused = area.circle = 0; + area.w = area.h = c->mask.w * 2; + area.x = c->x - c->mask.w; + area.y = c->y + (40 - c->mask.h); + + if (checkTileCollision(1, area) == 1) { + c->state = 5; + c->timer = 0; + } + } + //Bounce off wall + if (c->state == 5) + { + if (c->timer == 0) { + PHL_PlaySound(sounds[sndHit04], CHN_ENEMIES); + + c->timer = 1; + c->vsp = -2; + c->hsp = 2; + if (c->x > 320) { + c->hsp *= -1; + } + } + + c->imageIndex = 1; + + c->x += c->hsp; + + c->y += c->vsp; + c->vsp += grav; + + //Check if onground + if (c->vsp > 0) { + Mask area; + area.unused = area.circle = 0; + area.w = 40; + area.h = 10; + area.x = c->x - (area.w / 2); + area.y = c->y + (80 - area.h); + + PHL_Rect collide = getTileCollision(1, area); + if (collide.x != -1) { + c->y = collide.y - 80; + c->state = 0; + c->timer = 65; + } + } + } + //Death + else if (c->state == 6) + { + c->imageIndex = 1; + + c->y += 0.2; + + c->timer -= 1; + + if (c->timer % 12 == 0) { + createEffect(2, c->x - 64 + (rand() % 100), c->y + (rand() % 80)); + } + + if (c->timer <= 0) { + crabDestroy(c); + dead = 1; + } + } + + if (dead == 0) { + if (c->state != 6) { + //Update Mask + c->mask.x = c->x; + c->mask.y = c->y + 40; + + //Weapon collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(c->mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + c->invincible = 15; + c->hp -= 1; + i = MAX_WEAPONS; + } + } + } + } + + //Hit Player + if (checkCollision(c->mask, getHeroMask())) { + heroHit(30, c->x); + } + + //Die + if (c->hp <= 0) { + c->state = 6; + c->timer = 180; + c->invincible = 200; + } + } + } +} + +void crabDraw(Crab* c) +{ + if (c->invincible % 2 == 0) { + PHL_DrawSurfacePart(c->x - 40, c->y, (int)c->imageIndex * 80, 0, 80, 80, images[imgBoss]); + } +} + +void updateCrabMask(Crab* c) +{ + c->mask.x = c->x; + c->mask.y = c->y + 40; +} + +void crabDestroy(Crab* c) +{ + enemyDestroy(c->id); + bossDefeatedFlag = 1; + roomSecret = 1; + + flags[boss3flag] = 1; + PHL_StopMusic(); +} + + +void createElectricity(int x, int y, double angle, int minid) +{ + int i; + for (i = minid; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = /*(Enemy*)*/malloc(sizeof *e); + Electricity* el = /*(Electricity*)*/malloc(sizeof *el); + el->id = i; + + el->x = x; + el->y = y; + + //Fix angle + if (angle < 0) { + angle += 360; + } + if (angle >= 360) { + angle -= 360; + } + + el->angle = angle; + el->imageIndex = 0; + + el->mask.unused = 0; + el->mask.circle = 1; + el->mask.w = 16; + el->mask.h = 16; + el->mask.x = x; + el->mask.y = y; + + e->data = el; + e->enemyStep = electricityStep; + e->enemyDraw = electricityDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + /* + int thisid = -1; + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + if (i <= minid) { + thisid = i; + i = MAX_ENEMIES; + }else{ + i = MAX_ENEMIES; + } + } + } + + if (thisid == -1) { + for (i = minid + 1; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + enemies[i] = enemies[minid]; + Crab* c = enemies[i]->data; + c->id = i; + thisid = minid; + i = MAX_ENEMIES; + } + } + } + + if (thisid != -1) { + Enemy* e = (Enemy*)malloc(sizeof(Enemy)); + Electricity* el = (Electricity*)malloc(sizeof(Electricity)); + el->id = thisid; + + el->x = x; + el->y = y; + + //Fix angle + if (angle < 0) { + angle += 360; + } + if (angle >= 360) { + angle -= 360; + } + + el->angle = angle; + el->imageIndex = 0; + + el->mask.unused = 0; + el->mask.circle = 1; + el->mask.w = 16; + el->mask.h = 16; + el->mask.x = x; + el->mask.y = y; + + e->data = el; + e->enemyStep = electricityStep; + e->enemyDraw = electricityDraw; + e->type = -1; + + enemies[thisid] = e; + } + */ +} + +void electricityStep(Electricity* e) +{ + double spd = 3; + e->x += spd * sin(e->angle * 3.14159 / 180); + e->y += spd * cos(e->angle * 3.14159 / 180); + + //Update Mask + e->mask.x = e->x; + e->mask.y = e->y; + + //Collide with Shield + if (checkCollision(shieldMask, e->mask) == 1) { + createEffect(1, e->x - 20, e->y - 20); + PHL_PlaySound(sounds[sndHit07], CHN_EFFECTS); + enemyDestroy(e->id); + }else{ + //Collide with Hero + if (checkCollision(getHeroMask(), e->mask) == 1) { + if (heroHit(25, e->x) == 1) { + heroStun(); + } + } + } + + //Animate + e->imageIndex += 0.25; + if (e->imageIndex >= 3) { + e->imageIndex -= 3; + } + + //Outside of screen + if (e->x < -20 || e->x > 660 || e->y < -20 || e->y > 500) { + enemyDestroy(e->id); + } +} + +void electricityDraw(Electricity* e) +{ + PHL_DrawSurfacePart(e->x - 20, e->y - 20, 40 + ((int)e->imageIndex * 40), 0, 40, 40, images[imgMisc20]); +} \ No newline at end of file diff --git a/src/enemies/crab.h b/src/enemies/crab.h new file mode 100644 index 0000000..15e2b59 --- /dev/null +++ b/src/enemies/crab.h @@ -0,0 +1,30 @@ +#ifndef CRAB_H +#define CRAB_H + +#include "../collision.h" + +typedef struct { + int id; + int hp, invincible; + double x, y; + double hsp, vsp; + double imageIndex; + int state, timer, counter; + + Mask mask; +} Crab; + +void createCrab(int x, int y); + +typedef struct { + int id; + double x, y; + double angle; + double imageIndex; + + Mask mask; +} Electricity; + +void createElectricity(int x, int y, double angle, int minid); + +#endif \ No newline at end of file diff --git a/src/enemies/devil.c b/src/enemies/devil.c new file mode 100644 index 0000000..ae22e98 --- /dev/null +++ b/src/enemies/devil.c @@ -0,0 +1,458 @@ +#include "devil.h" +#include "../game.h" +#include "../PHL.h" +#include "../hero.h" +#include +#include + +int boss6flag = 31; + +void devilStep(Devil* d); +void devilDraw(Devil* d); + +void orbStep(Orb* o); +void orbDraw(Orb* o); + +void createDevil(int x, int y) +{ + if (flags[boss6flag] == 0) { //have not beaten boss 6 + PHL_FreeSurface(images[imgBoss]); + images[imgBoss] = PHL_LoadQDA("boss04.bmp"); + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + setBossRoom(); + + Enemy* e = /*(Enemy*)*/malloc(sizeof *e); + Devil* d = /*(Devil*)*/malloc(sizeof *d); + d->id = i; + + d->x = x; + d->y = y; + + d->ystart = d->y; + d->newystart = d->ystart; + d->hsp = -2.5; + + d->hp = 100; + //d->hp = 1; + + d->state = 0; + d->timer = 0; + + d->blink = 0; + d->boblen = 32; + d->bobspd = 3; + + d->tailangle = 90; + + d->rotcounter = 0; + d->bobcounter = 0; + + d->bobspd = 3; + d->rotspd = 1; + + d->imageIndex = 0; + + e->data = d; + e->enemyStep = devilStep; + e->enemyDraw = devilDraw; + e->type = 45; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + } +} + +void devilStep(Devil* d) +{ + char dead = 0; + + //Animate + { + d->imageIndex += 0.1; + if (d->imageIndex >= 2) { + d->imageIndex -= 2; + } + + if (d->blink > 0) { + d->blink -= 1; + } + } + + //Bob + { + if (d->state != 4) { + d->bobcounter += d->bobspd; + if (d->bobcounter >= 360) { + d->bobcounter -= 360; + } + + d->y = d->ystart + (d->boblen * cos(d->bobcounter * 3.14159 / 180)); + } + } + + //Swing tail + { + d->rotcounter += d->rotspd; + if (d->rotcounter >= 360) { + d->rotcounter -= 360; + } + + d->tailangle = 90 + (55 * cos(d->rotcounter * 3.14159 / 180)); + } + + //Patterns + { + //movement + if (d->state == 0 || d->state == 2) + { + d->rotspd = 1; + d->boblen = 32; + d->bobspd = 3; + + //Re-align ystart + if (d->ystart > d->newystart) { + d->ystart -= 1; + } + if (d->ystart < d->newystart) { + d->ystart += 1; + } + + d->x += d->hsp; + + //Slow Down + double rate = 0.016; + if (d->hsp < 0) { + d->hsp += rate; + if (d->hsp >= 0) { + d->hsp = 0; + } + } + + if (d->hsp > 0) { + d->hsp -= rate; + if (d->hsp <= 0) { + d->hsp = 0; + } + } + + if (d->hsp == 0) { + d->timer = 0; + if (d->state == 0) { + d->state = 1; + } + + if (d->state == 2) { + if ((d->rotcounter >= 90 && d->rotcounter <= 90 + d->rotspd) || (d->rotcounter >= 270 && d->rotcounter <= 270 + d->rotspd)) { + d->state = 3; + } + } + } + } + //mid room pause + else if (d->state == 1) + { + d->timer += 1; + if (d->timer >= 60) { + if (d->state == 1) { + d->hsp = 2.5; + if (herox < d->x) { + d->hsp *= -1; + } + } + d->state = 2; + } + } + //Shoot + else if (d->state == 3) + { + d->rotspd = 3; + d->boblen = 10; + d->bobspd = 10; + + d->timer += 1; + + //Shoot orbs + if (d->timer == 120 || d->timer == 240 || d->timer == 360) { + int aim = (atan2((heroy + 20) - d->y, d->x - herox) * 180 / 3.14159) + 270; + + int spawnY = d->y + 20; + createOrb(d->x, spawnY, aim + 22); + createOrb(d->x, spawnY, aim + 11); + createOrb(d->x, spawnY, aim); + createOrb(d->x, spawnY, aim - 11); + createOrb(d->x, spawnY, aim - 22); + + PHL_PlaySound(sounds[sndShot03], CHN_ENEMIES); + } + + if (d->timer == 360) { + d->state = 0; + d->hsp = 2.5; + + if (d->x > 320) { + d->hsp *= -1; + } + + int chaseY = heroy - d->ystart; + if (chaseY > 52) { chaseY = 52; } + if (chaseY < -52) { chaseY = -52; } + + d->newystart = d->ystart + chaseY; + } + } + + //Death + if (d->state == 4) { + d->rotspd = 3; + d->y += 0.2; + d->timer -= 1; + + if (d->timer % 12 == 0) { + createEffect(2, d->x - 64 + (rand() % 100), d->y - 64 + (rand() % 80)); + } + + if (d->timer <= 0) { + dead = 1; + } + } + } + + //Collisions + if (d->state != 4) { + //Setup masks + Mask masks[6]; + + //Head mask + masks[0].unused = masks[0].circle = 0; + masks[0].w = 100; + masks[0].h = 104; + masks[0].x = d->x - (masks[0].w / 2); + masks[0].y = d->y - (masks[0].h / 2); + + //Link masks + for (int i = 1; i < 5; i++) { + int taildis[4] = {54, 80, 108, 134}; + int taillag[4] = {10, 15, 10, 5}; + + double newtailangle = 90 + (55 * cos((d->rotcounter - taillag[i-1]) * 3.14159 / 180)); + + masks[i].unused = 0; + masks[i].circle = 1; + masks[i].w = 16; + masks[i].h = 16; + masks[i].x = d->x + (taildis[i-1] * cos(newtailangle * 3.14159 / 180)); + masks[i].y = d->y + (taildis[i-1] * sin(newtailangle * 3.14159 / 180)); + } + + //Barb mask + masks[5].unused = masks[5].circle = 0; + masks[5].w = 40; + masks[5].h = 40; + masks[5].x = (d->x + (160 * cos(d->tailangle * 3.14159 / 180))) - (masks[5].w / 2); + masks[5].y = (d->y + (160 * sin(d->tailangle * 3.14159 / 180))) - (masks[5].h / 2); + + //Collisions + int hitHead = 0; + for (int a = 0; a < 6; a++) + { + if (a == 0 || a == 5) { + //Hit Player + if (checkCollision(masks[a], getHeroMask())) { + + int damage = 25; + if (a == 0) { + damage = 50; + } + + if (heroHit(damage, masks[a].x + (masks[a].w / 2)) == 1) { + if (a == 5) { //Barb + heroStun(); + } + } + } + } + + //Weapon collision + if (hitHead == 0) { + for (int i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(masks[a], weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + //Head + if (a == 0) { + d->hp -= 1; + d->blink = 15; + hitHead = 1; + + if (d->hp <= 0) { + d->state = 4; + d->timer = 180; + d->blink = 200; + } + }else{ + PHL_PlaySound(sounds[sndHit03], CHN_WEAPONS); + } + + i = MAX_WEAPONS; + } + } + } + } + } + } + } + + //Destroy + if (dead == 1) { + enemyDestroy(d->id); + bossDefeatedFlag = 1; + roomSecret = 1; + + flags[boss6flag] = 1; + PHL_StopMusic(); + } +} + +void devilDraw(Devil* d) +{ + if (d->blink % 2 == 0) + { + int dx, dy; + + //Draw tail + int taildis[4] = {54, 80, 108, 134}; + int taillag[4] = {10, 15, 10, 5}; + for (int i = 0; i < 4; i++) { + double newtailangle = 90 + (55 * cos((d->rotcounter - taillag[i]) * 3.14159 / 180)); + + dx = d->x + (taildis[i] * cos(newtailangle * 3.14159 / 180)) - 32; + dy = d->y + (taildis[i] * sin(newtailangle * 3.14159 / 180)) - 32; + PHL_DrawSurfacePart(dx, dy, 0, 128, 64, 64, images[imgBoss]); + } + + //Draw Head + dx = d->x - 64; + dy = d->y - 64; + PHL_DrawSurfacePart(dx, dy, (int)d->imageIndex * 128, 0, 128, 128, images[imgBoss]); + + //Draw Tail Tip + dx = d->x + (160 * cos(d->tailangle * 3.14159 / 180)) - 32; + dy = d->y + (160 * sin(d->tailangle * 3.14159 / 180)) - 32; + PHL_DrawSurfacePart(dx, dy, 64, 128, 64, 64, images[imgBoss]); + + } +} + + +//Stone Orbs +void createOrb(int x, int y, double dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = /*(Enemy*)*/malloc(sizeof *e); + Orb* o = /*(Orb*)*/malloc(sizeof *o); + o->id = i; + + o->x = x; + o->y = y; + + o->dir = dir; + + o->imageIndex = 0; + + e->data = o; + e->enemyStep = orbStep; + e->enemyDraw = orbDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void orbStep(Orb* o) +{ + char dead = 0; + + //Animate + { + o->imageIndex += 0.33; + if (o->imageIndex >= 4) { + o->imageIndex -= 4; + } + } + + //Movement + { + int spd = 4; + o->x += spd * sin(o->dir * 3.14159 / 180); + o->y += spd * cos(o->dir * 3.14159 / 180); + } + + //Collision + { + Mask mask; + mask.unused = 0; + mask.circle = 1; + mask.w = 6; + mask.x = o->x; + mask.y = o->y; + + //Collide with shield + /*if (checkCollision(mask, shieldMask)) { + createEffect(1, o->x - 20, o->y - 20); + PHL_PlaySound(sounds[sndHit07], CHN_EFFECTS); + dead = 1; + }else{*/ + //Hit player + if (checkCollision(getHeroMask(), mask)) { + heroStone(); + heroHit(25, mask.x); + } + //} + + //Collide with weapon + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + createEffect(2, o->x - 32, o->y - 32); + dead = 1; + + i = MAX_WEAPONS; + } + } + } + } + + //Destroy if outside of room + { + if (o->x < -20 || o->x > 660 || o->y < -20 || o->y > 500) { + dead = 1; + } + } + + //Finally erase object + { + if (dead == 1) { + enemyDestroy(o->id); + } + } +} + +void orbDraw(Orb* o) +{ + int animation[4] = {0, 1, 0, 2}; + PHL_DrawSurfacePart(o->x - 20, o->y - 20, 440 + (animation[(int)o->imageIndex] * 40), 480, 40, 40, images[imgMisc20]); +} \ No newline at end of file diff --git a/src/enemies/devil.h b/src/enemies/devil.h new file mode 100644 index 0000000..3b6d67d --- /dev/null +++ b/src/enemies/devil.h @@ -0,0 +1,28 @@ +#ifndef DEVIL_H +#define DEVIL_H + +typedef struct { + int id; + double x, y; + double ystart, newystart; + double hsp; + int hp; + int state, timer; + int blink; + int boblen, bobspd; + double tailangle, bobcounter, rotcounter, rotspd; + double imageIndex; +} Devil; + +void createDevil(int x, int y); + +typedef struct { + int id; + double x, y; + double dir; + double imageIndex; +} Orb; + +void createOrb(int x, int y, double dir); + +#endif \ No newline at end of file diff --git a/src/enemies/dodo.c b/src/enemies/dodo.c new file mode 100644 index 0000000..0fc26a4 --- /dev/null +++ b/src/enemies/dodo.c @@ -0,0 +1,587 @@ +#include "dodo.h" +#include "../game.h" +#include "../hero.h" +#include "../PHL.h" +#include "../enemy.h" +#include + +void dodoStep(Dodo* d); +void dodoDraw(Dodo* d); + +Mask updateDodoMask(Dodo* d, Mask mask); +int dodoWallCollision(Dodo* d, Mask mask); + +int boss1flag = 1; + +void createDodo(int x, int y, int flag) +{ + char miniboss = 0; + + if (flag != 0) { + miniboss = 1; + }else{ + flag = boss1flag; + } + + if (flags[flag] == 0) { + PHL_FreeSurface(images[imgBoss]); + images[imgBoss] = PHL_LoadQDA("boss01.bmp"); + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + + if (miniboss == 0) { + setBossRoom(); + } + + Enemy* e = malloc(sizeof *e); + Dodo* d = malloc(sizeof *d); + d->id = i; + + d->x = x; + d->y = y; + + d->vsp = -6; + d->hsp = 0; + d->grav = 0.2; + + d->onground = 0; + d->dir = -1; + if (herox > d->x) { + d->dir = 1; + } + + d->imageIndex = 0; + + d->timer = 0; + d->state = 2; + + d->hp = 45; + d->blink = 0; + + d->tojump = 1; + d->jumptoggle = 0; + + d->flag = flag; + + e->data = d; + e->enemyStep = dodoStep; + e->enemyDraw = dodoDraw; + e->type = 40; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + } +} + +void dodoStep(Dodo* d) +{ + char dead = 0; + + //Constants + double fric = 0.06; + + //Animation vars + double imgspd = 0; + int frames = 0; + + //timers + { + if (d->timer > 0) { + d->timer -= 1; + } + + if (d->blink > 0) { + d->blink -= 1; + } + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 56; + mask.h = 56; + mask = updateDodoMask(d, mask); + } + + //Idle + if (d->state == 0) + { + d->hsp = 0; + d->vsp = 0; + + //Animate + imgspd = 0.1; + frames = 4; + + //End state + if (d->timer <= 0) { + //Go to chase + if (d->tojump == 0) { + d->state = 1; + d->timer = 260; + d->tojump = 1; + } + //Go to windup + else { + d->state = 3; + d->timer = 30; + } + } + + //Fall + { + if (d->onground == 0) { + d->state = 6; + d->imageIndex = 1; + } + } + } + + //Chase + else if (d->state == 1) + { + //Animate + imgspd = 0.2; + frames = 4; + + //Chase + if ( (d->dir == -1 && herox < d->x) || (d->dir == 1 && herox > d->x) ) { + d->hsp += (fric / 2) * d->dir; + + //limit speed + if (d->hsp > 3) { + d->hsp = 3; + } + if (d->hsp < -3) { + d->hsp = -3; + } + } + + //Turn around + else{ + d->hsp -= fric * d->dir; + + //Done slowing down + if ( (d->dir == 1 && d->hsp <= 0) || (d->dir == -1 && d->hsp >= 0) ) { + d->hsp = 0; + d->state = 4; + d->imageIndex = 0; + } + } + + //Stop running + { + if (d->timer <= 0) { + if (d->hsp >= 1 || d->hsp <= -1) { + d->state = 5; + } + } + } + + //Fall + { + if (d->onground == 0) { + d->state = 6; + d->imageIndex = 1; + } + } + + } + + //Turn around + else if (d->state == 4) + { + //Animate + imgspd = 0; + d->imageIndex += 0.2; + + //Done turning around + if (d->imageIndex >= 3) { + d->dir *= -1; + d->state = 1; + d->imageIndex = 0; + } + + //Fall + { + if (d->onground == 0) { + d->state = 6; + d->imageIndex = 1; + } + } + } + + //Jump + if (d->state == 2) + { + //Set image + imgspd = 0; + { + //Fall + d->imageIndex = 0; + + //Jump + if (d->vsp < 0) { + d->imageIndex = 1; + } + } + + //Face hsp + { + if (d->hsp > 0) { + d->dir = 1; + } + if (d->hsp < 0) { + d->dir = -1; + } + } + + //Land + { + if (d->onground == 1) { + d->state = 5; + d->tojump = 0; + + PHL_PlaySound(sounds[sndHit04], CHN_ENEMIES); + quakeTimer = 30; + createEffectExtra(3, d->x - 30, d->y + 50, -1, 0, 0); + createEffectExtra(3, d->x - 10, d->y + 50, 1, 0, 0); + } + } + + } + + //Windup + if (d->state == 3) + { + //Set image + imgspd = 0; + d->imageIndex = 0; + + //Jump + if (d->timer <= 0) { + d->state = 2; + PHL_PlaySound(sounds[sndJump01], CHN_ENEMIES); + if (d->jumptoggle == 0) { + d->jumptoggle = 1; + d->vsp = -3; + d->hsp = 2 * d->dir; + }else{ + d->jumptoggle = 0; + d->hsp = 1.5 * d->dir; + d->vsp = -6; + } + } + } + + //Slide to a stop + else if (d->state == 5) + { + //Friction + { + if (d->hsp > 0) { + d->hsp -= fric; + if (d->hsp <= 0) { + d->hsp = 0; + } + } + else if (d->hsp < 0) { + d->hsp += fric; + if (d->hsp >= 0) { + d->hsp = 0; + } + } + } + + //Go to idle + { + if (d->hsp == 0) { + d->state = 0; + d->timer = 140; + } + } + + } + + //Fall + if (d->state == 6) + { + //Set image + imgspd = 0; + d->imageIndex = 0; + + //Face hsp + { + if (d->hsp > 0) { + d->dir = 1; + } + if (d->hsp < 0) { + d->dir = -1; + } + } + + //Land + { + if (d->onground == 1) { + d->state = 5; + d->tojump = 1; + } + } + + } + + //Death + if (d->state == 7) + { + imgspd = 0.2; + frames = 4; + + //Movement + d->y += 0.2; + + if (d->blink % 12 == 0) { + createEffect(2, d->x - 72 + (rand() % 80), d->y - 12 + (rand() % 76)); + } + + if (d->blink <= 0) { + dead = 1; + } + } + + else{ + //Horizontal movement + { + if (d->hsp != 0) { + d->x += d->hsp; + + //Wall collision + if (d->state != 6) { + if (dodoWallCollision(d, mask) == 1) { + d->hsp *= -1; + if (d->state == 1) { + d->state = 5; + } + } + } + + } + } + + //Vertical movement + { + if (d->vsp != 0) { + d->y += d->vsp; + + mask = updateDodoMask(d, mask); + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + if (d->vsp < 0) { + d->y = collide.y + 40 - (96 - 14 - mask.h); + } + else if (d->vsp > 0) { + d->y = collide.y - 96 + 14; + } + } + } + } + + //Check if onground + mask = updateDodoMask(d, mask); + mask.y += 1; + if (!checkTileCollision(1, mask)) { + d->onground = 0; + }else{ + d->onground = 1; + } + mask.y -= 1; + + //Gravity + { + if (d->onground == 0) { + d->vsp += d->grav; + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + //Hit + d->blink = 15; + d->hp -= 1; + + //Death + if (d->hp <= 0) { + d->state = 7; + d->blink = 180; + } + + i = MAX_WEAPONS; + } + } + } + } + } + + //Hit Player + { + if (checkCollision(mask, getHeroMask())) { + heroHit(30, d->x); + } + } + } + + //Animate + { + if (imgspd != 0) { + d->imageIndex += imgspd; + + if (d->imageIndex >= frames) { + d->imageIndex -= frames; + } + } + } + + //Destroy object + { + if (dead == 1) { + //Is the level 1 boss + if (d->flag == boss1flag) { + bossDefeatedFlag = 1; + PHL_StopMusic(); + } + + roomSecret = 1; + flags[d->flag] = 1; + enemyDestroy(d->id); + } + } + +} + +void dodoDraw(Dodo* d) +{ + //PHL_DrawMask(d->mask); + if (d->blink % 2 == 0) { + int cropX = 0, + cropY = 0; + + int dirW = 0; + + //Idle + if (d->state == 0) { + dirW = 0; + int frame = 0; + + if (d->dir == 1) { + int animation[4] = {0, 6, 7, 6}; + frame = animation[(int)d->imageIndex]; + }else{ + int animation[4] = {3, 8, 9, 8}; + frame = animation[(int)d->imageIndex]; + } + + cropX = frame * 96; + + while (cropX >= 576) { + cropX -= 576; + cropY += 96; + } + } + + //Chase + else if (d->state == 1 || d->state == 7) { + dirW = 288; + int animation[4] = {0, 1, 0, 2}; + + cropX = animation[(int)d->imageIndex] * 96; + } + + //Jump + else if (d->state == 2) { + dirW = 192; + + cropY = 192; + cropX = (int)d->imageIndex * 96; + } + + //Turn around + else if (d->state == 4) { + dirW = 0; + cropY = 288; + + if (d->dir == 1) { + int animation[3] = {0, 1, 2}; + cropX = animation[(int)d->imageIndex] * 96; + }else{ + int animation[3] = {2, 1, 0}; + cropX = animation[(int)d->imageIndex] * 96; + } + } + + //Duck + else if (d->state == 3 || d->state == 5 || d->state == 6) { + dirW = 192; + + cropX = 0; + cropY = 192; + } + + //Direction offset + { + if (dirW != 0 && d->dir == -1) { + cropX += dirW; + } + } + + PHL_DrawSurfacePart(d->x - 48, d->y, cropX, cropY, 96, 96, images[imgBoss]); + } +} + +Mask updateDodoMask(Dodo* d, Mask mask) +{ + mask.x = d->x - (mask.w / 2); + mask.y = d->y + (96 - 14 - mask.h); + + return mask; +} + +int dodoWallCollision(Dodo* d, Mask mask) +{ + int result = 0; + + mask = updateDodoMask(d, mask); + + //Stay inside of room + if (d->x < 24) { + result = 1; + d->x = 24; + } + else if (d->x > 616) { + result = 1; + d->x = 616; + } + else{ + //Tile collision + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + result = 1; + if (d->hsp > 0) { + d->x = collide.x - (mask.w / 2); + }else{ + d->x = collide.x + 40 + (mask.w / 2); + } + } + } + + return result; +} \ No newline at end of file diff --git a/src/enemies/dodo.h b/src/enemies/dodo.h new file mode 100644 index 0000000..8797116 --- /dev/null +++ b/src/enemies/dodo.h @@ -0,0 +1,22 @@ +#ifndef DODO_H +#define DODO_H + +#include "../collision.h" + +typedef struct { + int id; + double x, y; + double vsp, hsp, grav; + int dir, onground; + double imageIndex; + int state, timer, hp; + int blink; + int tojump, jumptoggle; + int flag; + + //Mask mask; +} Dodo; + +void createDodo(int x, int y, int flag); + +#endif \ No newline at end of file diff --git a/src/enemies/dog.c b/src/enemies/dog.c new file mode 100644 index 0000000..6d0783a --- /dev/null +++ b/src/enemies/dog.c @@ -0,0 +1,294 @@ +#include "dog.h" +#include "../game.h" +#include "../hero.h" +#include + +void dogStep(Dog* d); +void dogDraw(Dog* d); + +int hitWall(Dog* d, Mask mask); + +void createDog(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Dog* d = malloc(sizeof *d); + d->id = i; + d->hp = 3; + d->blink = 0; + + d->x = x; + d->y = y; + + d->hsp = 0; + d->vsp = 0; + + d->imageIndex = 0; + + d->dir = 1; + if (herox < d->x) { + d->dir = -1; + } + + d->state = 0; + d->timer = 0; + d->counter = 0; + + e->data = d; + e->enemyStep = dogStep; + e->enemyDraw = dogDraw; + e->type = 30; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void dogStep(Dog* d) +{ + double grav = 0.175; + + char onground = 0; + char wallhit = 0; + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 32; + mask.h = 32; + mask.x = d->x + ((40 - mask.w) / 2); + mask.y = d->y + (40 - mask.h); + } + + //Blink animation + { + if (d->blink > 0) { + d->blink -= 1; + } + } + + //Horizontal movement + { + d->x += d->hsp; + mask.x = d->x + ((40 - mask.w) / 2); + + //Wall collision + if (hitWall(d, mask) == 1) { + wallhit = 1; + mask.x = d->x + ((40 - mask.w) / 2); + } + } + + //Vertical Movement + { + d->vsp += grav; + d->y += d->vsp; + mask.y = d->y + (40 - mask.h); + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + if (collide.x != -1) { + //Floor + if (d->vsp >= 0) { + onground = 1; + d->vsp = 0; + d->y = collide.y - 40; + } + //Ceiling + if (d->vsp < 0) { + d->y = collide.y + 40 - (40 - mask.h); + } + mask.y = d->y + (40 - mask.h); + } + } + + //Wait + if (d->state == 0) + { + double fric = 0.1; + + //Animate + { + d->imageIndex += 0.1; + if (d->imageIndex >= 2) { + d->imageIndex -= 2; + } + } + + //Collide with wall + { + if (wallhit == 1 && onground == 1) { + d->hsp *= -1; + } + } + + //Slide to hault + if (d->hsp > 0) { + d->dir = 1; + + d->hsp -= fric; + if (d->hsp <= 0) { + d->hsp = 0; + } + } + if (d->hsp < 0) { + d->dir = -1; + + d->hsp += fric; + if (d->hsp >= 0) { + d->hsp = 0; + } + } + + //Player is close + { + if (d->hsp == 0) { + Mask area; + area.unused = area.circle = 0; + area.w = 220; + area.h = 60; + area.x = d->x - 90; + area.y = d->y - 20; + + if (checkCollision(area, getHeroMask()) == 1) { + d->state = 1; + d->counter = 0; + d->vsp = 1; + } + } + } + + } + + //Hopping + else if (d->state == 1) + { + int spd = 2; + + d->hsp = spd * d->dir; + + //Land on floor + { + if (onground == 1) { + + //Landed + d->counter += 1; + d->vsp = -1.5; + if (d->counter == 3) { + d->vsp = -4; + } + if (d->counter == 4) { + d->state = 0; + d->counter = 0; + d->vsp = 0; + d->hsp = spd * d->dir; + }else{ + PHL_PlaySound(sounds[sndPi05], CHN_ENEMIES); + d->dir = 1; + if (herox < d->x + 20) { + d->dir = -1; + } + } + } + } + + //Animate + { + d->imageIndex = 1; + if (d->vsp < 0) { + d->imageIndex = 2; + } + } + + } + + //Update mask to be safe + mask.x = d->x + ((40 - mask.w) / 2); + mask.y = d->y + (40 - mask.h); + + //Hit Player + { + if (checkCollision(mask, getHeroMask())) { + if (heroHit(10, mask.x + (mask.w / 2)) == 1) { + heroStun(); + } + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + //Hit + d->blink = 15; + d->hp -= 1; + + //Death + if (d->hp <= 0) { + createEffect(2, d->x - 12, d->y - 6); + spawnCollectable(d->x + 20, d->y); + enemyDestroy(d->id); + } + + i = MAX_WEAPONS; + } + } + } + } + } + +} + +void dogDraw(Dog* d) +{ + if (d->blink % 2 == 0) { + int cropX = 240 + ((int)d->imageIndex * 40); + + if (d->dir == -1) { + cropX += 120; + } + + PHL_DrawSurfacePart(d->x, d->y, cropX, 40, 40, 40, images[imgEnemies]); + } +} + +int hitWall(Dog* d, Mask mask) +{ + PHL_Rect collide = getTileCollision(1, mask); + + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + if (collide.x != -1) { + int dir = 1; + if (d->hsp < 0) { + dir = -1; + } + d->x = collide.x + 20 - ((20 + (mask.w / 2)) * dir) - 20; + + return 1; + }else{ + if (d->x < -20) { + d->x = -20; + return 1; + } + + if (d->x > 620) { + d->x = 620; + return 1; + } + } + + return 0; +} \ No newline at end of file diff --git a/src/enemies/dog.h b/src/enemies/dog.h new file mode 100644 index 0000000..ee93ce7 --- /dev/null +++ b/src/enemies/dog.h @@ -0,0 +1,17 @@ +#ifndef DOG_H +#define DOG_H + +typedef struct { + int id; + int hp; + int blink; + double x, y; + double vsp, hsp; + double imageIndex; + int dir; + int state, timer, counter; +} Dog; + +void createDog(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/firewheel.c b/src/enemies/firewheel.c new file mode 100644 index 0000000..7368761 --- /dev/null +++ b/src/enemies/firewheel.c @@ -0,0 +1,272 @@ +#include "firewheel.h" +#include "../game.h" +#include "../PHL.h" +#include "../hero.h" +#include + +void firewheelRotate(Firewheel* f, int clockwise); + +void firewheelStep(Firewheel* f); +void firewheelDraw(Firewheel* f); + +void createFirewheel(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Firewheel* f = malloc(sizeof *f); + + f->id = i; + + f->x = x; + f->y = y; + + f->imageIndex = 0; + + f->hp = 2; + f->blink = 0; + + f->hsp = 1; + f->vsp = 0; + + f->wallx = 0; + f->wally = 1; + + f->timer = 0; + if (x % 40 != 0) { + f->timer = 20; + } + + //Start on ceiling + { + Mask mask; + mask.circle = mask.unused = 0; + mask.w = 40; + mask.h = 40; + mask.x = f->x; + mask.y = f->y + 10; + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + if (collide.x == -1) { + f->wally = -1; + f->hsp *= -1; + } + } + + f->dir = 1; + if (dir == 1) { + f->hsp *= -1; + f->dir = -1; + } + + e->data = f; + e->enemyStep = firewheelStep; + e->enemyDraw = firewheelDraw; + e->type = 27; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void firewheelStep(Firewheel* f) +{ + //Animate + { + f->imageIndex += 0.33; + if (f->imageIndex >= 4) { + f->imageIndex -= 4; + } + + if (f->blink > 0) { + f->blink -= 1; + } + } + + //Movement + int spd = 2; + f->x += spd * f->hsp; + f->y += spd * f->vsp; + + //Setup mask + Mask mask; + mask.circle = mask.unused = 0; + mask.w = 40; + mask.h = 40; + mask.x = f->x; + mask.y = f->y; + + //Check if ready to change angle + if ( (f->hsp != 0 && (int)f->x % 20 == 0) || (f->vsp != 0 && (int)f->y % 20 == 0) ) + { + int doCheck = 1; + while (doCheck == 1) { + doCheck = 0; + + //Check on edge + mask.x += (f->wallx * 10); + mask.y += (f->wally * 10); + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + //Outside of room + if (f->y <= -40) { + collide.x = 1; + } + + //On edge + if (collide.x == -1) { + int tempHsp = f->hsp; + int tempVsp = f->vsp; + f->hsp = f->wallx; + f->vsp = f->wally; + + f->wallx = -tempHsp; + f->wally = -tempVsp; + doCheck = 1; + } + //Hit wall + else { + mask.x = f->x; + mask.y = f->y; + mask.x += f->hsp * 10; + mask.y += f->vsp * 10; + + collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + //Outside of room + if (collide.x == -1) { + if (f->y <= -40 && f->vsp != 1) { + collide.x = 1; + } + } + + //Did collide with wall + if (collide.x != -1) { + int tempWallx = f->wallx; + int tempWally = f->wally; + f->wallx = f->hsp; + f->wally = f->vsp; + + f->hsp = -tempWallx; + f->vsp = -tempWally; + + doCheck = 1; + } + + } + } + + /* + mask.x += f->hsp * 10; + mask.y += f->vsp * 10; + + //Collide with wall + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + //Outside of room + if (collide.x == -1) { + if (mask.y <= 0 && f->vsp < 0) { + collide.x = f->x; + collide.y = -40; + collide.w = 40; + collide.h = 40; + } + } + + //Did collide with wall + if (collide.x != -1) { + int tempWallx = f->wallx; + int tempWally = f->wally; + f->wallx = f->hsp; + f->wally = f->vsp; + + f->hsp = -tempWallx; + f->vsp = -tempWally; + } + //Edge rotate + else{ + mask.x = f->x; + mask.y = f->y; + mask.x += (f->wallx * 10); + mask.y += (f->wally * 10); + + collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + if (collide.x == -1) { + int tempHsp = f->hsp; + int tempVsp = f->vsp; + f->hsp = f->wallx; + f->vsp = f->wally; + + f->wallx = -tempHsp; + f->wally = -tempVsp; + } + } +*/ + } + + //Update Mask + mask.w = 30; + mask.h = 30; + mask.x = f->x + 5; + mask.y = f->y + 5; + + //Collide with hero + if (checkCollision(mask, getHeroMask())) { + heroHit(20, mask.x + (mask.w / 2)); + } + + //Weapon collision + for (int i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + f->hp -= 1; + f->blink = 15; + + //Death + if (f->hp <= 0) { + createEffect(2, f->x - 12, f->y - 12); + spawnCollectable(f->x + 20, f->y); + enemyDestroy(f->id); + } + + i = MAX_WEAPONS; + } + } + } + } + +} + +void firewheelDraw(Firewheel* f) +{ + if (f->blink % 2 == 0) { + int cy = 80; + if (f->dir == -1) { + cy += 40; + } + + PHL_DrawSurfacePart(f->x, f->y, 480 + ((int)f->imageIndex * 40), cy, 40, 40, images[imgEnemies]); + } +} \ No newline at end of file diff --git a/src/enemies/firewheel.h b/src/enemies/firewheel.h new file mode 100644 index 0000000..7a5c6e4 --- /dev/null +++ b/src/enemies/firewheel.h @@ -0,0 +1,18 @@ +#ifndef FIREWHEEL_H +#define FIREWHEEL_H + +typedef struct { + int id; + double x, y; + double imageIndex; + int hp; + int blink; + int dir; + int hsp, vsp; + int wallx, wally; + int timer; +} Firewheel; + +void createFirewheel(int x, int y, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/fish.c b/src/enemies/fish.c new file mode 100644 index 0000000..54a9971 --- /dev/null +++ b/src/enemies/fish.c @@ -0,0 +1,139 @@ +#include "fish.h" +#include "../game.h" +#include "../enemy.h" +#include "../PHL.h" +#include "../collision.h" +#include "../hero.h" +#include + +void createFish(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Fish* f = malloc(sizeof *f); + f->id = i; + + f->x = f->xstart = x; + f->y = y; + + f->imageIndex = 0; + + f->spd = 1; + + f->turning = 0; + f->dir = 1; + if (dir == 1) { + f->dir = -1; + f->spd = -1; + } + + f->mask.circle = f->mask.unused = 0; + f->mask.x = x + 3; + f->mask.y = y + 6; + f->mask.w = 34; + f->mask.h = 32; + + e->data = f; + e->enemyStep = fishStep; + e->enemyDraw = fishDraw; + e->type = 13; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void fishStep(Fish* f) +{ + double fric = 0.02; + + f->x += f->spd; + f->mask.x = f->x + 3; + + if (f->turning == 0) { + f->imageIndex += 0.1; + if (f->imageIndex >= 2) { + f->imageIndex -= 2; + } + }else{ + f->imageIndex += 0.25; + if (f->imageIndex >= 3) { + f->turning = 0; + } + } + + if (f->dir == 1) { + if (f->x > f->xstart + 25) { + f->spd -= fric; + + if (f->spd < 0) { + f->dir = -1; + f->turning = 1; + f->imageIndex = 0; + } + }else{ + f->spd += fric; + if (f->spd > 1) { + f->spd = 1; + } + } + }else if (f->dir == -1) { + if (f->x < f->xstart - 25) { + f->spd += fric; + + if (f->spd > 0) { + f->dir = 1; + f->turning = 1; + f->imageIndex = 0; + } + }else{ + f->spd -= fric; + if (f->spd < -1) { + f->spd = -1; + } + } + } + + if (checkCollision(f->mask, getHeroMask())) { + heroHit(15, f->x + 20); + } + + //Weapon collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(f->mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + createEffect(2, f->x - 12, f->y - 12); + spawnCollectable(f->x + 20, f->y); + enemyDestroy(f->id); + + i = MAX_WEAPONS; + } + } + } +} + +void fishDraw(Fish* f) +{ + int thisImage = 0; + if (f->turning == 1) { + if (f->dir == -1) { + int animation[3] = {4, 6, 5}; + thisImage = animation[(int)f->imageIndex]; + }else{ + int animation[3] = {5, 6, 4}; + thisImage = animation[(int)f->imageIndex]; + } + }else{ + thisImage = f->imageIndex; + if (f->spd < 0) { + thisImage += 2; + } + } + + PHL_DrawSurfacePart(f->x, f->y, 360 + (thisImage * 40), 360, 40, 40, images[imgEnemies]); +} \ No newline at end of file diff --git a/src/enemies/fish.h b/src/enemies/fish.h new file mode 100644 index 0000000..9648bcd --- /dev/null +++ b/src/enemies/fish.h @@ -0,0 +1,24 @@ +#ifndef FISH_H +#define FISH_H + +#include "../enemy.h" +#include "../collision.h" + +typedef struct { + int id; + + double x, y; + int xstart; + double imageIndex; + double spd; + int dir, turning; + + Mask mask; +} Fish; + +void createFish(int x, int y, int dir); + +void fishStep(Fish* f); +void fishDraw(Fish* f); + +#endif \ No newline at end of file diff --git a/src/enemies/garm.c b/src/enemies/garm.c new file mode 100644 index 0000000..b9c59ec --- /dev/null +++ b/src/enemies/garm.c @@ -0,0 +1,578 @@ +#include "garm.h" +#include "../game.h" +#include "../PHL.h" +#include "../hero.h" +#include + +int boss7flag = 47; + +void garmStep(Garm* g); +void garmDraw(Garm* g); + +void garmrockStep(Garmrock* g); +void garmrockDraw(Garmrock* g); + +void createGarm(int x, int y) +{ + if (flags[boss7flag] == 0) { //have not beaten boss 7 + PHL_FreeSurface(images[imgBoss]); + images[imgBoss] = PHL_LoadQDA("boss07.bmp"); + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + setBossRoom(); + + Enemy* e = malloc(sizeof *e); + Garm* g = malloc(sizeof *g); + + g->id = i; + + g->hp = 105; + //g->hp = 1; + + g->x = x; + g->y = y; + + g->hsp = 0; + g->vsp = 0; + + g->dir = -1; + + g->imageIndex = 0; + + g->state = 0; + g->timer = 0; + + g->blink = 0; + + g->substate = 0; + g->wallcounter = 0; + g->targx = 0; + + e->data = g; + e->enemyStep = garmStep; + e->enemyDraw = garmDraw; + e->type = 46; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + } +} + +void garmStep(Garm* g) +{ + char dead = 0; + + //Blink animation + { + if (g->blink > 0) { + g->blink -= 1; + } + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 88; + mask.h = 104; + mask.x = g->x - (mask.w / 2); + mask.y = g->y + (120 - mask.h); + } + + //Stand still + if (g->state == 0) + { + //Animate + { + g->imageIndex += 0.0625; + if (g->imageIndex >= 2) { + g->imageIndex -= 2; + } + } + + //End state + { + g->timer += 1; + if (g->timer >= 60) { + g->state = 1; + //g->vsp = -4.5; + g->counter = 0; + g->timer = 0; + //PHL_PlaySound(sounds[sndPi09], CHN_ENEMIES); + } + } + } + + //Bounce + else if (g->state == 1) + { + //Animate + { + g->imageIndex += 0.33; + if (g->imageIndex >= 3) { + g->imageIndex -= 3; + } + } + + if (g->timer > 0) { + g->vsp = 0; + g->imageIndex = 0; + g->timer -= 1; + if (g->timer <= 0) { + //End state + if (g->counter >= 3) { + g->state = 2; + g->counter = 0; + g->imageIndex = 0; + g->vsp = -6; + g->hsp = 8; + if (g->x > herox) { + g->hsp *= -1; + } + + if (g->substate == 0) { + g->wallcounter = 1; + g->substate = 1; + }else{ + g->wallcounter = 2; + g->substate = 0; + } + }else{ + g->vsp = -5; + } + } + } + + else if (g->timer == 0) { + double grav = 0.25; + + //Movement + if (g->timer == 0) { + g->y += g->vsp; + g->vsp += grav; + mask.y = g->y + (120 - mask.h); + } + + //Land on ground + if (g->vsp >= 0 && g->timer == 0) { + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + g->y = collide.y - 120; + mask.y = g->y + (120 - mask.h); + g->vsp = 0; + g->timer = 3; + g->counter += 1; + PHL_PlaySound(sounds[sndPi09], CHN_ENEMIES); + } + } + } + } + + //Leap towards wall + else if (g->state == 2) + { + double grav = 0.25; + + //Set image + { + if (g->hsp > 0) { + g->imageIndex = 0; + } + + if (g->hsp < 0) { + g->imageIndex = 1; + } + } + + //Movement + { + g->y += g->vsp; + g->vsp += grav; + mask.y = g->y + (120 - mask.h); + + g->x += g->hsp; + mask.x = g->x - (mask.w / 2); + } + + if (g->wallcounter > 0) + { + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + g->wallcounter -= 1; + if (g->hsp < 0) { + g->x = collide.x + 40 + (mask.w / 2); + } + if (g->hsp > 0) { + g->x = collide.x - (mask.w / 2); + } + g->state = 3; + g->timer = 0; + } + } + + //Ground pound + else { + char action = 0; + + if ( (g->hsp > 0 && g->x > g->targx) || (g->hsp < 0 && g->x < g->targx) ) { + action = 1; + } + //Wall collision backup + else{ + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + if (g->hsp < 0) { + g->x = collide.x + 40 + (mask.w / 2); + } + if (g->hsp > 0) { + g->x = collide.x - (mask.w / 2); + } + action = 1; + } + } + + if (action == 1) { + g->state = 4; + g->vsp = -4; + PHL_PlaySound(sounds[sndWolf01], CHN_ENEMIES); + } + } + } + + //Grab wall + else if (g->state == 3) + { + g->timer += 1; + if (g->timer > 5) { + g->state = 2; + g->vsp = -6; + g->hsp *= -1; + PHL_PlaySound(sounds[sndPi09], CHN_ENEMIES); + + g->targx = herox; + + if (g->wallcounter <= 0) { + //Get distance from player + int dis = g->x - g->targx; + { + if (dis < 0) { + dis *= -1; + } + } + + if (dis < 200 || g->substate == 1) { + g->hsp /= 2; + } + } + } + } + + //Ground pound + else if (g->state == 4) + { + double grav = 0.2; + + //Animate + { + g->imageIndex += 0.33; + if (g->imageIndex >= 3) { + g->imageIndex -= 3; + } + } + + g->y += g->vsp; + g->vsp += grav; + mask.y = g->y + (120 - mask.h); + + //Collide with floor + { + PHL_Rect collide = getTileCollision(1, mask); + + if (collide.x != -1) { + g->y = collide.y - 120; + PHL_PlaySound(sounds[sndHit04], CHN_ENEMIES); + quakeTimer = 30; + g->state = 0; + g->timer = -20; + //Create rocks + createGarmrock(g->x + 64, g->y + 100, 2, -4); + createGarmrock(g->x + 34, g->y + 100, 1, -5); + createGarmrock(g->x - 34, g->y + 100, -1, -5); + createGarmrock(g->x - 64, g->y + 100, -2, -4); + + createEffectExtra(3, g->x - 50, g->y + 90, -1, 0, 0); + createEffectExtra(3, g->x + 10, g->y + 90, 1, 0, 0); + } + } + } + + //Dead + if (g->state == 5) { + //Animate + { + g->imageIndex += 0.33; + if (g->imageIndex >= 3) { + g->imageIndex -= 3; + } + } + + g->y += 0.2; + + if (g->blink % 12 == 0) { + createEffect(2, g->x - 64 + (rand() % 100), g->y + 60 - 64 + (rand() % 80)); + } + + if (g->blink <= 0) { + dead = 1; + } + } + + else{ + if (dead == 0) { + //Update Mask + { + mask.x = g->x - (mask.w / 2); + mask.y = g->y + (120 - mask.h); + } + + //Hero collision + { + if (checkCollision(getHeroMask(), mask) == 1) { + heroHit(40, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + g->hp -= 1; + g->blink = 15; + //Dead + if (g->hp <= 0) { + g->state = 5; + g->blink = 200; + } + + i = MAX_WEAPONS; + } + } + } + } + } + + } + } + + if (dead == 1) { + //Destroy + { + enemyDestroy(g->id); + bossDefeatedFlag = 1; + roomSecret = 1; + + flags[boss7flag] = 1; + PHL_StopMusic(); + } + } + +} + +void garmDraw(Garm* g) +{ + if (g->blink % 2 == 0) { + int cropX = 0, + cropY = 0; + + //Jump Spinning + if ((g->state == 1 && g->timer == 0) || g->state == 4 || g->state == 5) { + cropY = 128; + cropX = 256; + } + + //Jump + if (g->state == 2) { + cropY = 128; + } + + //Wall grab + if (g->state == 3) { + cropX = 384; + } + + cropX += (int)g->imageIndex * 128; + + PHL_DrawSurfacePart(g->x - 64, g->y - 8, cropX, cropY, 128, 128, images[imgBoss]); + } +} + +//Rocks +void createGarmrock(int x, int y, double hsp, double vsp) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Garmrock* g = malloc(sizeof *g); + + g->id = i; + g->hp = 3; + + g->x = x; + g->y = y; + + g->hsp = hsp; + g->vsp = vsp; + + g->imageIndex = 0; + + g->counter = 0; + g->inwall = 0; + g->blink = 0; + + e->data = g; + e->enemyStep = garmrockStep; + e->enemyDraw = garmrockDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void garmrockStep(Garmrock* g) +{ + char dead = 0; + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 44; + mask.h = 44; + mask.x = g->x - (mask.w / 2); + mask.y = g->y - (mask.h / 2); + } + + //Animate + { + g->imageIndex += 0.2; + if (g->imageIndex >= 4) { + g->imageIndex -= 4; + } + + if (g->blink > 0) { + g->blink -= 1; + } + } + + //Horizontal movement + { + g->x += g->hsp; + mask.x = g->x - (mask.w / 2); + + g->inwall = 0; + if (checkTileCollision(1, mask) == 1) { + g->inwall = 1; + } + } + + //Vertical movement + { + double grav = 0.1; + + g->y += g->vsp; + g->vsp += grav; + mask.y = g->y - (mask.h / 2); + + if (g->inwall == 0 && g->counter == 0) { + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + g->counter = 1; + g->y = collide.y - (mask.h / 2); + g->vsp = -2; + PHL_PlaySound(sounds[sndHit06], CHN_ENEMIES); + } + } + } + + //Update mask + { + mask.x = g->x - (mask.w / 2); + mask.y = g->y - (mask.h / 2); + } + + //Hero collision + { + if (checkCollision(getHeroMask(), mask) == 1) { + heroHit(30, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + g->hp -= 1; + g->blink = 15; + + if (g->hp <= 0) { + dead = 1; + createRockSmash(g->x, g->y + 20); + } + + i = MAX_WEAPONS; + } + } + } + } + } + + //Destroy when out of room + { + if (mask.y > 480) { + dead = 1; + } + } + + //Destroy object + { + if (dead == 1) { + enemyDestroy(g->id); + } + } +} + +void garmrockDraw(Garmrock* g) +{ + if (g->blink % 2 == 0) { + int cropX = 256, + cropY = 192; + + if (g->hsp < 0) { + cropX = 512; + } + + cropX += (int)g->imageIndex * 64; + + while (cropX >= 640) { + cropX -= 640; + cropY += 64; + } + + PHL_DrawSurfacePart(g->x - 32, g->y - 32, cropX, cropY, 64, 64, images[imgMisc32]); + } +} \ No newline at end of file diff --git a/src/enemies/garm.h b/src/enemies/garm.h new file mode 100644 index 0000000..e44a998 --- /dev/null +++ b/src/enemies/garm.h @@ -0,0 +1,31 @@ +#ifndef GARM_H +#define GARM_H + +typedef struct { + int id; + int hp; + double x, y; + double hsp, vsp; + int dir; + double imageIndex; + int state, timer, blink, counter; + int wallcounter, substate; + int targx; +} Garm; + +void createGarm(int x, int y); + +typedef struct { + int id; + int hp; + double x, y; + double vsp, hsp; + double imageIndex; + int counter; + int blink; + int inwall; +} Garmrock; + +void createGarmrock(int x, int y, double hsp, double vsp); + +#endif \ No newline at end of file diff --git a/src/enemies/gas.c b/src/enemies/gas.c new file mode 100644 index 0000000..aee6dbc --- /dev/null +++ b/src/enemies/gas.c @@ -0,0 +1,116 @@ +#include "gas.h" +#include "../PHL.h" +#include "../game.h" +#include "../hero.h" +#include + +void gasStep(Gas* g); +void gasDraw(Gas* g); + +void createGas(int x, int y, int temp) +{ + if (temp == 0 || hasKey[7] == 0) { + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Gas* g = malloc(sizeof *g); + g->id = i; + + g->x = x; + g->y = y; + + g->state = 0; + g->timer = 0; + g->imageIndex = 0; + + /* + g->mask.unused = g->mask.circle = 0; + g->mask.w = g->mask.h = 24; + g->mask.x = x + 20 - (g->mask.w / 2); + g->mask.y = y + 40 - g->mask.h; + */ + + e->data = g; + e->enemyStep = gasStep; + e->enemyDraw = gasDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + } +} + +void gasStep(Gas* g) +{ + if (g->state != 0) { + g->imageIndex += 0.2; + } + + if (g->state == 0) { //Wait + Mask tempMask; + tempMask.circle = tempMask.unused = 0; + tempMask.x = g->x - 100; + tempMask.y = g->y - 20; + tempMask.w = 240; + tempMask.h = 60; + + if (checkCollisionXY(tempMask, herox, heroy + 20)) { + g->state = 1; + g->imageIndex = 3; + g->timer = 32; + PHL_PlaySound(sounds[sndGas01], CHN_ENEMIES); + } + } + else if (g->state == 1 || g->state == 3) { //Small puff + if (g->imageIndex >= 5) { + g->imageIndex -= 2; + } + + g->timer -= 1; + if (g->timer <= 0) { + if (g->state == 3) { + g->state = 0; + }else{ + g->state = 2; + g->imageIndex = 0; + g->timer = 175; + } + } + } + else if (g->state == 2) { //Big puff + if (g->imageIndex >= 3) { + g->imageIndex -= 3; + } + + g->timer -= 1; + if (g->timer <= 0) { + g->state = 3; + g->timer = 120; + g->imageIndex = 3; + } + + if (hasItem[7] != 1) { //Does not have gas mask + Mask mask; + mask.unused = mask.circle = 0; + mask.w = mask.h = 24; + mask.x = g->x + 20 - (mask.w / 2); + mask.y = g->y + 40 - mask.h; + + if (checkCollision(getHeroMask(), mask)) { + if (heroHit(15, g->x + 20)) { + heroPoison(); + } + } + } + } +} + +void gasDraw(Gas* g) +{ + if (g->state != 0) { + PHL_DrawSurfacePart(g->x, g->y, (int)g->imageIndex * 40, 400, 40, 40, images[imgEnemies]); + } +} \ No newline at end of file diff --git a/src/enemies/gas.h b/src/enemies/gas.h new file mode 100644 index 0000000..7bd1ebf --- /dev/null +++ b/src/enemies/gas.h @@ -0,0 +1,18 @@ +#ifndef GAS_H +#define GAS_H + +//#include "../enemy.h" +//#include "../collision.h" + +typedef struct { + int id; + int x, y; + int state, timer; + double imageIndex; + + //Mask mask; +} Gas; + +void createGas(int x, int y, int temp); + +#endif \ No newline at end of file diff --git a/src/enemies/ghoul.c b/src/enemies/ghoul.c new file mode 100644 index 0000000..fca1fbd --- /dev/null +++ b/src/enemies/ghoul.c @@ -0,0 +1,218 @@ +#include "ghoul.h" +#include "../game.h" +#include "../enemy.h" +#include "../PHL.h" +#include "../hero.h" +#include + +void ghoulStep(Ghoul* g); +void ghoulDraw(Ghoul* g); + +void createGhoul(int x, int y, int type) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Ghoul* g = malloc(sizeof *g); + g->id = i; + g->hp = 2; + + g->x = x; + g->y = y; + + g->vsp = 0; + g->grav = 0.1; + + g->dir = 0; + g->type = type; + g->onground = 0; + + g->timer = 0; + g->state = 0; + g->invincible = 0; + + g->imageIndex = 0; + + g->mask.circle = 0; + g->mask.unused = 1; + g->mask.w = 24; + g->mask.h = 32; + g->mask.x = g->x + ((40 - g->mask.w) / 2); + g->mask.y = g->y + (40 - g->mask.h); + + e->data = g; + e->enemyStep = ghoulStep; + e->enemyDraw = ghoulDraw; + e->type = 18; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + +} + +void ghoulStep(Ghoul* g) +{ + if (g->invincible > 0) { + g->invincible -= 1; + } + + if (g->state == 0) { //Wait + Mask area; + area.unused = area.circle = 0; + area.w = 280; + area.h = 80; + area.x = g->x - 120; + area.y = g->y - 20; + + if (checkCollisionXY(area, herox, heroy + 20) == 1) { + g->state = 1; + g->mask.unused = 0; + g->imageIndex = 0; + + g->dir = 1; + if (herox < g->x + 20) { + g->dir = -1; + } + } + } + else if (g->state == 1) { //Pop-up + g->imageIndex += 0.16; + + if (g->imageIndex >= 4) { + g->state = 2; + g->vsp = -1; + g->imageIndex = 0; + PHL_PlaySound(sounds[sndPi05],CHN_ENEMIES); + } + } + else if (g->state == 2) { //Walking + g->mask.unused = 0; + if (g->onground == 0) { + //Vertical movement + g->y += g->vsp; + g->vsp += g->grav; + + g->mask.y = g->y + (40 - g->mask.h); + + PHL_Rect collide = getTileCollision(1, g->mask); + if (collide.x == -1) { + collide = getTileCollision(3, g->mask); + } + if (collide.x != -1) { + g->onground = 1; + g->vsp = 0; + g->y = collide.y - 40; + g->mask.y = g->y + (40 - g->mask.h); + } + } + + g->imageIndex += 0.1; + if (g->imageIndex >= 2) { + g->imageIndex -= 2; + } + + double hsp = 1; + + if ((int)g->imageIndex == 0) { + hsp = 0.5; + } + + //Purple + if (g->type == 1) { + hsp *= 2; + } + + g->x += hsp * g->dir; + g->mask.x = g->x + ((40 - g->mask.w) / 2); + + if (g->onground == 1) { + if ((g->x < -20 || g->x > 660) || checkTileCollision(1, g->mask) == 1) { + g->dir *= -1; + + PHL_Rect collide = getTileCollision(1, g->mask); + if (collide.x != -1) { + g->x = collide.x + (40 * g->dir); + } + } + else { + //check on ledge + g->mask.w = 5; + if (g->dir == 1) { + g->mask.x = g->x + 30; + } + if (g->dir == -1) { + g->mask.x = g->x + 5; + } + g->mask.y += 20; + + if (checkTileCollision(1, g->mask) == 0 && checkTileCollision(3, g->mask) == 0) { + g->dir *= -1; + } + g->mask.w = 24; + g->mask.x = g->x + ((40 - g->mask.w) / 2); + g->mask.y = g->y + (40 - g->mask.h); + } + } + } + + g->mask.x = g->x + ((40 - g->mask.w) / 2); + g->mask.y = g->y + (40 - g->mask.h); + + //Hit Player + { + if (checkCollision(g->mask, getHeroMask())) { + if (heroHit(10, g->x + 20) == 1 && g->type == 1) { + heroPoison(); + } + } + } + + //Weapon Collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(g->mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + g->hp -= 1; + g->invincible = 15; + //Death + if (g->hp <= 0) { + createEffect(2, g->x - 12, g->y - 6); + spawnCollectable(g->x + 20, g->y); + enemyDestroy(g->id); + } + i = MAX_WEAPONS; + } + } + } + } + } + +} + +void ghoulDraw(Ghoul* g) +{ + if (g->state != 0 && g->invincible % 2 == 0) { + int cx = (int)g->imageIndex * 40, + cy = 160; + + if (g->state == 1) { + cx += 160; + }else{ + if (g->dir == -1) { + cx += 80; + } + } + + //Purple palette + cy += 160 * g->type; + + PHL_DrawSurfacePart(g->x, g->y, cx, cy, 40, 40, images[imgEnemies]); + } +} \ No newline at end of file diff --git a/src/enemies/ghoul.h b/src/enemies/ghoul.h new file mode 100644 index 0000000..fd6db3a --- /dev/null +++ b/src/enemies/ghoul.h @@ -0,0 +1,22 @@ +#ifndef GHOUL_H +#define GHOUL_H + +#include "../collision.h" + +typedef struct { + int id; + int hp; + double x, y; + double vsp, grav; + int type; + int onground; + int dir; + int state, timer, invincible; + double imageIndex; + + Mask mask; +} Ghoul; + +void createGhoul(int x, int y, int type); + +#endif \ No newline at end of file diff --git a/src/enemies/golem.c b/src/enemies/golem.c new file mode 100644 index 0000000..a372368 --- /dev/null +++ b/src/enemies/golem.c @@ -0,0 +1,199 @@ +#include "golem.h" +#include "../PHL.h" +#include "../hero.h" +#include "../game.h" +#include + +void golemStep(Golem* g); +void golemDraw(Golem* g); + +void createGolem(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Golem* g = malloc(sizeof *g); + g->id = i; + + g->x = x; + g->y = y; + + g->hp = 4; + + g->dir = 1; + if (dir == 1) { + g->dir = -1; + } + + g->imageIndex = 0; + g->state = 0; + g->blink = 0; + + e->data = g; + e->enemyStep = golemStep; + e->enemyDraw = golemDraw; + e->type = 28; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void golemStep(Golem* g) +{ + double imageSpeed = 0.2; + + //Timers + { + if (g->blink > 0) { + g->blink -= 1; + } + } + + //Setup Mask + Mask mask; + { + mask.unused = mask.circle = 0; + mask.w = 36; + mask.h = 36; + mask.x = g->x + ((40 - mask.w) / 2); + mask.y = g->y + (40 - mask.h); + } + + //Rolling + if (g->state == 0) + { + //Animate + { + g->imageIndex += imageSpeed * g->dir; + + if (g->imageIndex >= 8) { + g->imageIndex -= 8; + } + + if (g->imageIndex < 0) { + g->imageIndex += 8; + } + } + + //Movement + double hsp = 1; + { + g->x += hsp * g->dir; + mask.x = g->x + ((40 - mask.w) / 2); + } + + char nextState = 0; + + //Check on ledge + { + mask.x += 30 * g->dir; + mask.y += 10; + + if (checkTileCollision(1, mask) == 0 && checkTileCollision(3, mask) == 0) { + nextState = 1; + } + + mask.x = g->x + ((40 - mask.w) / 2); + mask.y = g->y + (40 - mask.h); + } + + //Collide with wall + { + mask.x += hsp * g->dir; + + if (checkTileCollision(1, mask) == 1) { + nextState = 1; + } + + mask.x = g->x + ((40 - mask.w) / 2); + } + + if (nextState == 1) { + PHL_PlaySound(sounds[sndPi10], CHN_ENEMIES); + g->state = 1; + g->imageIndex = 0; + } + } + + //Forming + else if (g->state == 1) + { + //Animate + { + g->imageIndex += imageSpeed; + + if (g->imageIndex >= 12) { + g->imageIndex = 0; + g->state = 0; + g->dir *= -1; + } + } + + } + + //Hero Collision + { + if (checkCollision(mask, getHeroMask())) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + //Tink + if (g->state == 0) { + PHL_PlaySound(sounds[sndHit03], CHN_WEAPONS); + }else{ + g->hp -= 1; + g->blink = 15; + } + + i = MAX_WEAPONS; + } + } + } + } + } + + //Death + { + if (g->hp <= 0) { + createRockSmash(mask.x + (mask.w / 2), mask.y + (mask.h / 2)); + spawnCollectable(g->x + 20, g->y); + enemyDestroy(g->id); + } + } +} + +void golemDraw(Golem* g) +{ + if (g->blink % 2 == 0) { + int cropX = 320, + cropY = 160; + + int drawY = g->y; + + if (g->state == 0) { + cropX += (int)g->imageIndex * 40; + drawY += 2; + }else{ + cropY = 280; + cropX = 240; + + int animation[12] = {0, 1, 2, 3, 3, 3, 3, 3, 3, 2, 1, 0}; + cropX += animation[(int)g->imageIndex] * 40; + } + + PHL_DrawSurfacePart(g->x, drawY, cropX, cropY, 40, 40, images[imgEnemies]); + } +} \ No newline at end of file diff --git a/src/enemies/golem.h b/src/enemies/golem.h new file mode 100644 index 0000000..2653605 --- /dev/null +++ b/src/enemies/golem.h @@ -0,0 +1,16 @@ +#ifndef GOLEM_H +#define GOLEM_H + +typedef struct { + int id; + double x, y; + double imageIndex; + int hp; + int dir; + int state; + int blink; +} Golem; + +void createGolem(int x, int y, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/gyra.c b/src/enemies/gyra.c new file mode 100644 index 0000000..a09ea9d --- /dev/null +++ b/src/enemies/gyra.c @@ -0,0 +1,332 @@ +#include "gyra.h" +#include "../game.h" +#include "../enemy.h" +#include "../hero.h" +#include +#include + +void gyraStep(Gyra* g); +void gyraDraw(Gyra* g); +void gyraDestroy(Gyra* g); + +int boss4flag = 21; + +void createGyra(int x, int y) +{ + if (flags[boss4flag] == 0) { //have not yet beaten boss 4 + PHL_FreeSurface(images[imgBoss]); + images[imgBoss] = PHL_LoadQDA("boss02.bmp"); + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + //Boss start + setBossRoom(); + + Enemy* e = malloc(sizeof *e); + Gyra* g = malloc(sizeof *g); + + g->id = i; + g->hp = 50; + //g->hp = 1; + + g->x = x; + g->y = y; + + g->targx = g->x; + g->targy = g->y; + + g->state = 0; + g->timer = 260; + g->counter = 0; + + g->invincible = 0; + g->dir = 0; + g->imageIndex = 0; + + //Setup + g->targx = g->x - 32; + g->targy = g->y + 64; + g->dir = 160; + + g->x = g->targx + (80 * sin(g->dir * 3.14159 / 180)); + g->y = g->targy + (80 * cos(g->dir * 3.14159 / 180)); + + int a; + for (a = 0; a < 144; a++) { + g->xrecord[a] = g->x; + g->yrecord[a] = g->y; + } + + e->data = g; + e->enemyStep = gyraStep; + e->enemyDraw = gyraDraw; + e->type = 43; + + enemies[i] = e; + i = MAX_ENEMIES; + + } + } + } +} + +void gyraStep(Gyra* g) +{ + //Animate + g->imageIndex += 0.1; + if (g->imageIndex >= 2) { + g->imageIndex -= 2; + } + + int pattern[6] = {0, 1, 2, 1, 0, 2}; + + //Move in a circle + if (g->state == 0) + { + int len = 80; + + //Setup + if (g->timer == 0) { + g->targx = g->x + (len * sin((g->dir + 90) * 3.14159 / 180)); + g->targy = g->y + (len * cos((g->dir + 90) * 3.14159 / 180)); + g->dir -= 90; + g->timer = 250; + } + + g->dir += 1.5; + if (g->dir >= 360) { + g->dir -= 360; + } + + g->x = g->targx + (len * sin(g->dir * 3.14159 / 180)); + g->y = g->targy + (len * cos(g->dir * 3.14159 / 180)); + + g->timer -= 1; + if (g->timer <= 0) { + g->counter += 1; + g->state = pattern[g->counter]; + /* + if (g->state != 1 && (g->x < 40 || g->x > 600 || g->y < 40 || g->y > 440)) { + g->state = 1; + }*/ + + g->timer = 0; + } + } + //Attack + else if (g->state == 1) + { + //Setup + if (g->timer == 0) { + g->targx = herox; + g->targy = heroy + 20; + g->dir += 90; + g->timer = 320; + } + + double spd = 2; + double diralt = 1.2; + + double targdir = (atan2(g->targy - g->y, g->x - g->targx) * 180 / 3.14159) + 270; + + targdir = g->dir - targdir; + while (targdir >= 360) { targdir -= 360; } + while (targdir < 0) { targdir += 360; } + + if (targdir > 180) { + g->dir += diralt; + } + if (targdir < 180) { + g->dir -= diralt; + } + + //Movement + g->x += spd * sin(g->dir * 3.14159 / 180); + g->y += spd * cos(g->dir * 3.14159 / 180); + + //Get (close) to targ coords + g->timer -= 1; + if (g->timer <= 0 || sqrt( pow(g->x - g->targx, 2) + pow(g->y - g->targy, 2) ) <= spd * 2) { + g->counter += 1; + if (g->counter >= 5) { + g->counter = 0; + } + g->state = pattern[g->counter]; + g->timer = 0; + } + } + //Oval movement + else if (g->state == 2) + { + int wlen = 120, + hlen = 80; + + //Setup + if (g->timer == 0) { + g->targx = g->x + (wlen * sin((g->dir - 90) * 3.14159 / 180)); + g->targy = g->y + (hlen * cos((g->dir - 90) * 3.14159 / 180)); + g->dir += 90; + g->timer = 200; + } + + g->dir -= 1.5; + if (g->dir < 0) { + g->dir += 360; + } + + g->x = g->targx + (wlen * sin(g->dir * 3.14159 / 180)); + g->y = g->targy + (hlen * cos(g->dir * 3.14159 / 180)); + + g->timer -= 1; + if (g->timer <= 0) { + g->counter += 1; + if (g->counter >= 5) { + g->counter = 0; + } + g->state = pattern[g->counter]; + /* + if (g->state != 1 && (g->x < 40 || g->x > 600 || g->y < 40 || g->y > 440)) { + g->state = 1; + g->timer = 0; + }*/ + } + } + + //Death + if (g->state == 3) + { + g->timer -= 1; + if (g->timer <= 0) { + g->timer = 12; + + int cx = g->xrecord[128 - (g->counter * 16)], + cy = g->yrecord[128 - (g->counter * 16)]; + + createEffect(2, cx - 32, cy - 32); + + g->counter += 1; + if (g->counter == 9) { + gyraDestroy(g); + } + } + }else{ + //Update tail record + int i; + for (i = 142; i >= 0; i--) { + g->xrecord[i + 1] = g->xrecord[i]; + g->yrecord[i + 1] = g->yrecord[i]; + } + g->xrecord[0] = g->x; + g->yrecord[0] = g->y; + + //for (i = 8; i >= 0; i--) { + for (i = 0; i <= 8; i++) { + int cx = g->x, cy = g->y; + + if (i != 0) { + cx = g->xrecord[i * 16]; + cy = g->yrecord[i * 16]; + } + + Mask mask; + mask.unused = 0; + mask.circle = 1; + mask.x = cx; + mask.y = cy; + mask.w = mask.h = 28; + + int a; + for (a = 0; a < MAX_WEAPONS; a++) { + if (weapons[a] != NULL) { + if (weapons[a]->cooldown == 0) { + if (checkCollision(mask, weapons[a]->weaponMask)) { + g->invincible = -15; + weaponHit(weapons[a]); + + if (i == 8) { + g->hp -= 1; + g->invincible = 15; + }else{ + PHL_PlaySound(sounds[sndHit03], CHN_WEAPONS); + } + + a = MAX_WEAPONS; + } + } + } + } + + //Hit player + if (checkCollision(getHeroMask(), mask)) { + if (heroHit(30, mask.x) && i == 0) { + heroPoison(); + } + } + } + + //Death + if (g->hp <= 0) { + g->state = 3; + g->timer = 0; + g->counter = 0; + g->invincible = 200; + } + } + + if (g->invincible > 0) { + g->invincible -= 1; + } + if (g->invincible < 0) { + g->invincible += 1; + } + +} + +void gyraDraw(Gyra* g) +{ + if (g->invincible <= 0 || g->invincible % 2 == 0) { + //Draw Tail Tip + if (g->state != 3 || g->counter <= 0) { + PHL_DrawSurfacePart(g->xrecord[126] - 40, g->yrecord[126] - 40, 320 + ((int)g->imageIndex * 80), 0, 80, 80, images[imgBoss]); + } + + //Draw Tail + int i; + for (i = 7; i > 0; i--) { + if (g->state != 3 || g->counter <= (7 - i) + 1) { + PHL_DrawSurfacePart(g->xrecord[i * 16] - 40, g->yrecord[i * 16] - 40, 160 + ((int)g->imageIndex * 80), 0, 80, 80, images[imgBoss]); + } + } + + //Draw Head + PHL_DrawSurfacePart(g->x - 40, g->y - 40, (int)g->imageIndex * 80, 0, 80, 80, images[imgBoss]); + } + + //PHL_DrawRect(g->targx, g->targy, 10, 10, PHL_NewRGB(255, 255, 255)); + //heroAmmo = g->state; + + /* + int i; + for (i = 8; i >= 0; i--) { + int cx = g->x, cy = g->y; + + if (i != 0) { + cx = g->xrecord[i * 16]; + cy = g->yrecord[i * 16]; + } + + PHL_DrawRect(cx, cy, 10, 10, PHL_NewRGB(255, 255, 255)); + } + */ +} + +void gyraDestroy(Gyra* g) +{ + enemyDestroy(g->id); + bossDefeatedFlag = 1; + roomSecret = 1; + + flags[boss4flag] = 1; + PHL_StopMusic(); +} \ No newline at end of file diff --git a/src/enemies/gyra.h b/src/enemies/gyra.h new file mode 100644 index 0000000..badd7e9 --- /dev/null +++ b/src/enemies/gyra.h @@ -0,0 +1,19 @@ +#ifndef GYRA_H +#define GYRA_H + +typedef struct { + int id; + int hp; + double x, y; + double xrecord[144]; + double yrecord[144]; + int state, timer, counter; + int targx, targy; + int invincible; + double dir; + double imageIndex; +} Gyra; + +void createGyra(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/heads.c b/src/enemies/heads.c new file mode 100644 index 0000000..802d2dc --- /dev/null +++ b/src/enemies/heads.c @@ -0,0 +1,838 @@ +#include "heads.h" +#include "../enemy.h" +#include "../game.h" +#include "../PHL.h" +#include "../hero.h" +#include +#include + +void headStep(Head* h); +void headDraw(Head* h); + +void bulletStep(Bullet* b); +void bulletDraw(Bullet* b); + +void fireballStep(Fireball* f); +void fireballDraw(Fireball* f); + +void laserStep(Laser* l); +void laserDraw(Laser* l); + +void flameStep(Flame* f); +void flameDraw(Flame* f); + +void rockStep(Rock* r); +void rockDraw(Rock* r); + +void airStep(Air* a); +void airDraw(Air* a); + +void createHead(int type, int x, int y, int dir, int offset, int cooloff) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Head* h = malloc(sizeof *h); + + h->id = i; + h->type = type; + + h->x = x; + h->y = y; + + h->state = 0; + + h->hp = 5; + h->invincible = 0; + h->counter = 0; + + h->dir = 1; + if (dir == 1) { + h->dir = -1; + } + + h->timer = 30 * offset; + h->cooloff = 60; + if (cooloff != 0) { + h->cooloff = 30 * cooloff; + } + + e->type = -1; + if (h->type == 0) { + e->type = 4; + h->cooloff = 120; + } + else if (h->type == 1) { + e->type = 6; + } + else if (h->type == 2) { + e->type = 5; + } + else if (h->type == 3) { + e->type = 7; + h->cooloff = 120; + } + else if (h->type == 4) { + e->type = 10; + h->dir = 0; + } + else if (h->type == 5) { + e->type = 25; + h->dir = 0; + } + e->data = h; + e->enemyStep = headStep; + e->enemyDraw = headDraw; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void headStep(Head* h) +{ + int RHYNO = 0, + MEDUSA = 1, + DRAGON = 2, + DEMON = 3, + FIRE = 4, + JAR = 5; + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.x = h->x; + mask.y = h->y + 1; + mask.w = 40; + mask.h = 39; + } + + //Timers + { + if (h->invincible > 0) { + h->invincible -= 1; + } + + if (h->timer > 0) { + h->timer -= 1; + } + } + + //Wait + if (h->state == 0) + { + char endstate = 0; + + if (h->timer <= 0) { + //Proximity + if (h->type == RHYNO || h->type == DEMON) { + Mask area; + area.circle = area.unused = 0; + area.h = 80; + area.w = 400; + area.y = h->y - 20; + area.x = h->x; + if (h->dir == -1) { + area.x -= area.w - 40; + } + + if (checkCollision(area, getHeroMask()) == 1) { + endstate = 1; + } + }else{ + endstate = 1; + } + } + + //Move onto next state + if (endstate == 1) { + h->state = 1; + h->timer = 30; + } + } + + //Blink + else if (h->state == 1) + { + //Shoot projectile + if (h->timer <= 0) { + //Play Sound + { + int soundtoplay[6] = {sndShot03, sndShot04, sndFire01, sndHit06, sndShot03, sndShot06}; + PHL_PlaySound(sounds[soundtoplay[h->type]], CHN_ENEMIES); + } + + //Set vars + { + h->state = 0; + h->timer = h->cooloff; + } + + //Create projectile + { + //Rhyno head + if (h->type == RHYNO) { + createBullet(mask.x + (mask.w / 2), h->y + 24, h->dir, h->id); + } + //Medusa head + if (h->type == MEDUSA) { + createLaser(h->x, h->y, h->dir); + } + //Dragon head + if (h->type == DRAGON) { + createFlame(h->x + 20 + (20 * h->dir), h->y - 10, h->dir); + } + //Demon head + if (h->type == DEMON) { + createRock(h->x + (20 * h->dir), h->y, h->dir); + } + //Fireball Statue + if (h->type == FIRE) { + createFireball(h->x + 20, h->y + 20, (atan2(heroy - h->y, h->x - (herox - 20)) * 180 / 3.14159) + 270, h->id); + } + //Air Jar + if (h->type == JAR) { + h->state = 3; + h->timer = 12; + h->counter = 0; + } + } + + } + } + + //Air Jar + else if (h->state == 3) + { + if (h->timer <= 0) { + h->counter += 1; + h->timer = 12; + createAir(h->x, h->y - 20); + } + + if (h->counter >= 6) { + h->counter = 0; + h->state = 0; + h->timer = h->cooloff; + } + } + + //Hit player + if (h->type != JAR) { + if (checkCollision(getHeroMask(), mask)) { + heroHit(10, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + h->hp -= 1; + h->invincible = 15; + weaponHit(weapons[i]); + //Death + if (h->hp <= 0) { + createRockSmash(h->x + 20, h->y + 20); + spawnCollectable(h->x + 20, h->y); + enemyDestroy(h->id); + } + + i = MAX_WEAPONS; + } + } + } + } +} + +void headDraw(Head* h) +{ + if (h->invincible % 2 == 0) + { + int sheetX[6] = {0, 320, 160, 240, 560, 400}; + int sheetY[6] = {80, 80, 80, 120, 0, 120}; + + int cropX = sheetX[h->type]; + + int addx[6] = {6, 2, 0, 0, 0, 0}; + int frames = 2; + + //Change dir + if (h->dir == 0) { + frames = 1; + }else{ + frames = 2; + if (h->dir == -1) { + cropX += 40; + } + } + + //White flash + if (h->state == 1 && h->timer % 6 < 3) { + cropX += 40 * frames; + } + + PHL_DrawSurfacePart(h->x - (addx[h->type] * h->dir), h->y, cropX, sheetY[h->type], 40, 40, images[imgEnemies]); + } +} + +//Bullets +void createBullet(int x, int y, int dir, int minid) +{ + int i; + for (i = minid; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Bullet* b = malloc(sizeof *b); + b->id = i; + + b->x = x; + b->y = y; + + b->hsp = dir * 4; + + b->imageIndex = 0; + + e->data = b; + e->enemyStep = bulletStep; + e->enemyDraw = bulletDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void bulletStep(Bullet* b) +{ + char dead = 0; + + //Movement + { + b->x += b->hsp; + } + + //Create Mask + Mask mask; + { + mask.unused = 0; + mask.circle = 1; + mask.w = mask.h = 10; + mask.x = b->x; + mask.y = b->y; + } + + //Animation + { + if (b->hsp > 0) { + b->imageIndex += 0.33; + }else{ + b->imageIndex -= 0.33; + } + + if (b->imageIndex < 0) { + b->imageIndex += 4; + } + if (b->imageIndex >= 4) { + b->imageIndex -= 4; + } + } + + //Collide with wall + { + if (checkTileCollision(1, mask) == 1) { + createEffect(1, b->x - 20, b->y - 20); + dead = 1; + } + } + + //Collide with hero + { + //Shield collision + if (checkCollision(mask, shieldMask) == 1) { + dead = 1; + createEffect(1, b->x - 20, b->y - 20); + PHL_PlaySound(sounds[sndHit07], CHN_EFFECTS); + } + //Collide with hero + else{ + if (checkCollision(getHeroMask(), mask)) { + heroHit(10, mask.x); + } + } + } + + //Destroy if outside of view + { + if (b->x > 660 || b->x < -20 || b->y < -20 || b->y > 520) { + dead = 1; + } + } + + //Destroy + { + if (dead == 1) { + enemyDestroy(b->id); + } + } +} + +void bulletDraw(Bullet* b) +{ + PHL_DrawSurfacePart(b->x - 20, b->y - 20, 160 + (40 * (int)b->imageIndex), 480, 40, 40, images[imgMisc20]); +} + +//Fireball +void createFireball(int x, int y, int angle, int minid) +{ + //General idea: try to place fireball over spawner + int i; + for (i = minid; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Fireball* f = malloc(sizeof *f); + f->id = i; + + f->x = x; + f->y = y; + + f->spd = 3; + + f->imageIndex = 0; + f->angle = angle; + + f->mask.circle = 1; + f->mask.unused = 0; + f->mask.x = x; + f->mask.y = y; + f->mask.w = f->mask.h = 14; + + e->data = f; + e->enemyStep = fireballStep; + e->enemyDraw = fireballDraw; + e->type = -1; + + enemies[i] = e; + + i = MAX_ENEMIES; + } + } +} + +void fireballStep(Fireball* f) +{ + f->x += (f->spd) * sin(f->angle * 3.14159 / 180); + f->y += (f->spd) * cos(f->angle * 3.14159 / 180); + + f->mask.x = f->x; + f->mask.y = f->y; + + f->imageIndex += 0.5; + if (f->imageIndex >= 8) { + f->imageIndex -= 8; + } + + //Collide with shield + if (checkCollision(f->mask, shieldMask)) { + createEffect(1, f->x - 20, f->y - 20); + PHL_PlaySound(sounds[sndHit07], CHN_EFFECTS); + enemyDestroy(f->id); + }else{ + //Hit player + if (checkCollision(getHeroMask(), f->mask)) { + heroHit(10, f->mask.x); + } + //Destroy if outside of view + if (f->x > 660 || f->x < -20 || f->y < -20 || f->y > 520) { + enemyDestroy(f->id); + } + } +} + +void fireballDraw(Fireball* f) +{ + PHL_DrawSurfacePart(f->x - 20, f->y - 20, 320 + (40 * (int)f->imageIndex), 440, 40, 40, images[imgMisc20]); +} + +//Laser +void createLaser(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Laser* l = malloc(sizeof *l); + l->id = i; + + l->x = x; + l->y = y; + + l->dir = dir; + l->imageIndex = 0; + + l->mask.circle = l->mask.unused = 0; + l->mask.x = x; + l->mask.y = y + 17; + l->mask.w = 40; + l->mask.h = 6; + + e->data = l; + e->enemyStep = laserStep; + e->enemyDraw = laserDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void laserStep(Laser* l) +{ + char dead = 0; + + l->x += l->dir * 10; + l->mask.x = l->x; + + l->imageIndex += 0.34; + if (l->imageIndex >= 2) { + l->imageIndex -= 2; + } + + if (checkCollision(shieldMask, l->mask)) { //Hit shield + PHL_PlaySound(sounds[sndHit07], CHN_EFFECTS); + createEffect(1, l->x + (20 * l->dir), l->y); + enemyDestroy(l->id); + dead = 1; + }else if (checkCollision(getHeroMask(), l->mask)) { + heroStone(); + heroHit(15, l->x + 20); + } + + if (dead == 0) { + if (checkTileCollision(1, l->mask)) { + createEffect(1, l->x + (20 * l->dir), l->y); + enemyDestroy(l->id); + dead = 1; + } + + if (dead == 0) { + if (l->mask.x > 640 || l->mask.x + l->mask.w <= 0) { + enemyDestroy(l->id); + } + } + } +} + +void laserDraw(Laser* l) +{ + int dx = 0, + dy = 480; + if (l->dir == -1) { + dx += 80; + } + + PHL_DrawSurfacePart(l->x, l->y, dx + (((int)l->imageIndex) * 40), dy, 40, 40, images[imgMisc20]); +} + +//Dragon Flame +void createFlame(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Flame* f = malloc(sizeof *f); + f->id = i; + + f->x = x; + f->y = y; + + f->dir = dir; + f->timer = 60; + + f->imageIndex = 0; + + e->data = f; + e->enemyStep = flameStep; + e->enemyDraw = flameDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void flameStep(Flame* f) +{ + f->imageIndex += 0.25; + + if (f->timer > 0) { + if (f->imageIndex >= 3) { + f->imageIndex -= 3; + } + } + + f->timer -= 1; + + if (f->timer == 0) { + f->imageIndex = 3; + } + + //Hero Collision + { + Mask mask; + mask.circle = mask.unused = 0; + mask.x = f->x; + mask.y = f->y + 16; + mask.w = 120; + mask.h = 18; + if (f->dir == -1) { + mask.x -= 120; + } + + if (checkCollision(mask, getHeroMask()) == 1) { + int centerX = mask.x + 60 - (60 * f->dir); + + //Hero is on ladder + if (getHeroState() == 3) { + centerX = herox; + } + + heroHit(30, centerX); + } + } + + if (f->timer < 0 && f->imageIndex >= 6) { + enemyDestroy(f->id); + } +} + +void flameDraw(Flame* f) +{ + int drawX = f->x, + drawY = f->y; + + int cropX = 0, + cropY = 0; + + if (f->dir == -1) { + cropX += 720; + drawX -= 120; + } + + cropX += 120 * (int)f->imageIndex; + + while (cropX >= 600) { + cropX -= 600; + cropY += 40; + } + + PHL_DrawSurfacePart(drawX, drawY, cropX, cropY, 120, 40, images[imgMisc6020]); +} + +//Demon Rock +void createRock(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Rock* r = malloc(sizeof *r); + r->id = i; + + r->x = x; + r->y = y; + + r->vsp = -3; + r->dir = dir; + + r->imageIndex = 0; + + e->data = r; + e->enemyStep = rockStep; + e->enemyDraw = rockDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void rockStep(Rock* r) +{ + char dead = 0; + + //Animate + { + r->imageIndex += 0.25 * r->dir; + if (r->imageIndex >= 8) { + r->imageIndex -= 8; + } + if (r->imageIndex < 0) { + r->imageIndex += 8; + } + } + + //Setup Mask + Mask mask; + { + mask.unused = mask.circle = 0; + mask.x = r->x + 2; + mask.y = r->y + 2; + mask.w = 36; + mask.h = 36; + } + + int hsp = 3; + double grav = 0.12; + + //Movement + { + r->y += r->vsp; + r->vsp += grav; + + //Collide with floor + { + mask.y = r->y + 2; + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + if (collide.x != -1) { + PHL_PlaySound(sounds[sndHit06], CHN_ENEMIES); + r->y = collide.y - mask.h - 2; + r->vsp = -3; + mask.y = r->y + 2; + } + } + + r->x += hsp * r->dir; + + //Collide with wall + { + mask.x = r->x + 2; + + PHL_Rect collide = getTileCollision(1, mask); + + if (collide.x != -1) { + dead = 1; + } + } + } + + //Collision + { + //Hero collision + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(20, mask.x + (mask.w / 2)); + } + + //Weapon collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + PHL_PlaySound(sounds[sndHit03], CHN_WEAPONS); + + i = MAX_WEAPONS; + } + } + } + } + } + + //Destroy + if (dead == 1) { + createRockSmash(r->x + 20, r->y); + enemyDestroy(r->id); + } +} + +void rockDraw(Rock* r) +{ + PHL_DrawSurfacePart(r->x, r->y, 320 + ((int)r->imageIndex * 40), 160, 40, 40, images[imgEnemies]); +} + +//Air Stream +void createAir(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Air* a = malloc(sizeof *a); + a->id = i; + + a->x = x; + a->y = y; + + a->imageIndex = 0; + + e->data = a; + e->enemyStep = airStep; + e->enemyDraw = airDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +//Air Puff +void airStep(Air* a) +{ + Mask mask; + mask.circle = mask.unused = 0; + mask.w = 36; + mask.h = 30; + mask.x = a->x + ((40 - mask.w) / 2); + + //Animate + a->imageIndex += 0.5; + if (a->imageIndex >= 2) { + a->imageIndex -= 2; + } + + //Movement + a->y -= 6; + mask.y = a->y + (40 - mask.h); + + //Collide with player + if (getHeroState() != 2) { + if (checkCollision(mask, getHeroMask())) { + if (hasItem[27] == 0) { + heroHit(10, mask.x + (mask.w / 2)); + }else{ + //Floating stuff + if (getHeroVsp() > -5) { + setHeroVsp(-5); + setHeroOnground(0); + } + } + } + } + + //destroy if outside of room + if (mask.y + mask.h < 0) { + enemyDestroy(a->id); + } +} + +void airDraw(Air* a) +{ + PHL_DrawSurfacePart(a->x, a->y, (int)a->imageIndex * 40, 560, 40, 40, images[imgMisc20]); +} \ No newline at end of file diff --git a/src/enemies/heads.h b/src/enemies/heads.h new file mode 100644 index 0000000..75fcab0 --- /dev/null +++ b/src/enemies/heads.h @@ -0,0 +1,89 @@ +#ifndef HEADS_H +#define HEADS_H + +#include "../collision.h" + +//Goblin/medusa/dragon head statues +typedef struct { + int id, type; //0 = Rhyno head | 1 = Goblin | 2 = Dragon | 3 = Demon | 4 = Fireball | 5 = Air Jar + int state, timer; + double x, y; + int dir; + int hp, invincible; + int cooloff; + int counter; + + //Mask mask; +} Head; + +void createHead(int type, int x, int y, int dir, int offset, int cooloff); + +//Bullet from Rhyno statues +typedef struct { + int id; + double x, y; + int hsp; + double imageIndex; + + //Mask mask; +} Bullet; + +void createBullet(int x, int y, int dir, int minid); //Minid is the spawner's id + +//Fireball +typedef struct { + int id; + double x, y; + int angle; + int spd; + double imageIndex; + + Mask mask; +} Fireball; + +void createFireball(int x, int y, int angle, int minid); + +//Medusa lazer +typedef struct { + int id; + double x, y; + int dir; + double imageIndex; + + Mask mask; +} Laser; + +void createLaser(int x, int y, int dir); + +//Dragon flame +typedef struct { + int id; + int x, y; + int dir; + int timer; + double imageIndex; +} Flame; + +void createFlame(int x, int y, int dir); + +//Demon Boulder +typedef struct { + int id; + double x, y; + double vsp; + int dir; + double imageIndex; +} Rock; + +void createRock(int x, int y, int dir); + +//Air +typedef struct { + int id; + double x, y; + double imageIndex; +} Air; + +void createAir(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/hydra.c b/src/enemies/hydra.c new file mode 100644 index 0000000..d770c9d --- /dev/null +++ b/src/enemies/hydra.c @@ -0,0 +1,1127 @@ +#include "hydra.h" +#include "../game.h" +#include "../hero.h" +#include +#include + +const double PI = 3.14159; + +double headRot = 0; + +void hydraStep(Hydra* h); +void hydraDraw(Hydra* h); + +void hydraDestroy(Hydra* h); + +void hydraheadStep(Hydrahead* h); +void hydraheadDraw(Hydrahead* h); + +void hydragoopStep(Hydragoop* h); +void hydragoopDraw(Hydragoop* h); + +void hydrarockStep(Hydrarock* h); +void hydrarockDraw(Hydrarock* h); + +void hydrashockStep(Hydrashock* h); +void hydrashockDraw(Hydrashock* h); + +double getHydraX(Hydrahead* h); +double getHydraY(Hydrahead* h); + +Mask getHydraMask(Hydra* h); +int checkWeaponCollision(Mask m); + +double lengthdir_x(double ang, double len); +double lengthdir_y(double ang, double len); + +void setHeadState(int headid, int state); + + +//#hydra +void createHydra(int x) +{ + PHL_FreeSurface(images[imgBoss]); + images[imgBoss] = PHL_LoadQDA("lboss01.bmp"); + + int i; + for (i = 4; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + setBossRoom(); + + Enemy* e = malloc(sizeof *e); + Hydra* h = malloc(sizeof *h); + h->id = i; + + h->hp = 10; + //h->hp = 1; + h->blink = 0; + + h->x = x; + h->y = -64; + + h->hsp = 0; + h->vsp = 0; + + h->imageIndex = 0; + + h->state = 0; + h->timer = 0; + + h->patternCounter = 0; + + h->onground = 0; + h->noheads = 0; + + e->data = h; + e->enemyStep = hydraStep; + e->enemyDraw = hydraDraw; + e->type = 47; + + enemies[i] = e; + + h->headid[0] = createHydrahead(-1, 0, i); + h->headid[1] = createHydrahead(1, 0, i); + h->headid[2] = createHydrahead(1, 1, i); + h->headid[3] = createHydrahead(-1, 1, i); + + i = MAX_ENEMIES; + } + } +} + +void hydraStep(Hydra* h) +{ + double grav = 0.2; + double fric = 0.1; + + //Death + if (h->state == 6) { + h->y += 0.2; + + h->timer -= 1; + h->blink -= 1; + + if (h->timer % 12 == 0) { + createEffect(2, h->x - 64 + (rand() % 128) - 32, h->y - 64 + (rand() % 128)); + } + + if (h->timer <= 0) { + hydraDestroy(h); + } + } + else{ + //Setup Mask + Mask mask = getHydraMask(h); + + //States with hydra heads + if (h->noheads == 0) { + //Fall in intro + if (h->state == 0) { + h->hsp = 0; + h->timer += 1; + if (h->timer >= 50) { + h->timer = 50; + h->imageIndex = 2; + + if (h->onground == 1) { + h->state = 1; + h->timer = 0; + } + }else{ + grav = 0; + } + } + + //Wait/Pattern activate + else if (h->state == 1) + { + h->timer += 1; + + //Stop speed animation + if (h->timer >= 120) { + int patternSize = 9; + int pattern[9] = {4, 0, 4, 1, 4, 2, 4, 3, 2}; + + //Head seizure + if (pattern[h->patternCounter] == 4) { + h->state = 4; + h->timer = 0; + } + //Small hop + if (pattern[h->patternCounter] == 0) { + h->state = 2; + h->timer = 0; + } + + //Goop + if (pattern[h->patternCounter] == 1) { + h->timer = -120; + setHeadState(h->headid[0], 2); + setHeadState(h->headid[1], 2); + } + + //Big Hop + if (pattern[h->patternCounter] == 2) { + h->state = 3; + h->timer = 0; + } + + //Electricity + if (pattern[h->patternCounter] == 3) { + h->timer = -40; + setHeadState(h->headid[2], 3); + setHeadState(h->headid[3], 3); + } + + h->patternCounter += 1; + if (h->patternCounter >= patternSize) { + h->patternCounter = 0; + } + } + } + + //Head seizure state + else if (h->state == 4) { + //Speed up head animation + if (h->timer == 0) { + setHeadState(h->headid[0], 1); + setHeadState(h->headid[1], 1); + setHeadState(h->headid[2], 1); + setHeadState(h->headid[3], 1); + } + + h->timer += 1; + + //Stop speed animation + if (h->timer == 120) { + setHeadState(h->headid[0], 0); + setHeadState(h->headid[1], 0); + setHeadState(h->headid[2], 0); + setHeadState(h->headid[3], 0); + + //Pattern + h->state = 1; + h->timer = 120; + } + } + + //Switch to noheads mode + if (h->onground == 1 && + enemies[h->headid[0]] == NULL && + enemies[h->headid[1]] == NULL && + enemies[h->headid[2]] == NULL && + enemies[h->headid[3]] == NULL) + { + h->noheads = 1; + h->state = 1; + h->timer = -15; + h->patternCounter = 0; + } + } + + //States without hydra heads + else{ + //Wait/pattern activate + if (h->state == 1) { + h->timer += 1; + + if (h->timer >= 0) { + int patternSize = 3; + int pattern[3] = {0, 0, 2}; + + //Small hop + if (pattern[h->patternCounter] == 0) { + h->state = 2; + h->timer = 0; + } + + //Big Hop + if (pattern[h->patternCounter] == 2) { + h->state = 3; + h->timer = 0; + } + + h->patternCounter += 1; + if (h->patternCounter >= patternSize) { + h->patternCounter = 0; + } + } + } + } + + //States used by both modes + { + //Small hop + if (h->state == 2) { + //Setup + if (h->timer == 0) { + h->vsp = -2; + h->onground = 0; + h->hsp = 2.5; + if (herox < h->x) { + h->hsp *= -1; + } + } + + h->timer += 1; + + if (h->onground == 1) { + if (h->noheads == 0 || h->hsp == 0) { + h->state = 1; + h->timer = 0; + } + } + } + + //Large Hop + else if (h->state == 3) { + h->hsp = 0; + + //Setup + if (h->timer == 0) { + h->timer = 1; + if (h->noheads == 0) { + h->vsp = -8; + }else{ + h->vsp = -5; + } + h->onground = 0; + } + + if (h->onground == 1) { + h->timer += 1; + + if (h->timer % 20 == 0) { + createHydrarock(); + } + + if (h->timer >= 220) { + h->state = 1; + h->timer = -15; + } + } + } + } + + //Animate + { + if (h->onground == 1) { + h->imageIndex += 0.1; + if (h->imageIndex >= 2) { + h->imageIndex -= 2; + } + }else{ + if (h->vsp < 0) { + h->imageIndex = 3; + } + else { + h->imageIndex = 2; + } + } + + //Blink + if (h->blink > 0) { + h->blink -= 1; + } + } + + //Movement + { + //Horizontal + if (h->hsp != 0) { + h->x += h->hsp; + mask = getHydraMask(h); + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + int dir = 1; + if (h->hsp < 0) { + dir = -1; + } + h->x = collide.x + 20 - ((20 + (mask.w / 2)) * dir); + + h->hsp *= -1; + } + } + + //Friction + { + if (h->onground == 1) { + if (h->hsp > 0) { + h->hsp -= fric; + if (h->hsp < 0) { + h->hsp = 0; + } + } + if (h->hsp < 0) { + h->hsp += fric; + if (h->hsp > 0) { + h->hsp = 0; + } + } + } + } + + //Vertical + { + int maxVsp = 9; + + if (h->onground == 0) { + h->y += h->vsp; + h->vsp += grav; + mask = getHydraMask(h); + + //Limit vsp + { + if (h->vsp > maxVsp) { + h->vsp = maxVsp; + } + } + + //Collide with floor + { + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + h->y = collide.y - 64; + h->vsp = 0; + h->onground = 1; + PHL_PlaySound(sounds[sndHit04], CHN_ENEMIES); + quakeTimer = 30; + createEffectExtra(3, h->x - 30, h->y + 32, -1, 0, 0); + createEffectExtra(3, h->x - 10, h->y + 32, 1, 0, 0); + } + } + } + } + + } + + //Update mask + mask = getHydraMask(h); + + //Hero Collision + { + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(25, h->x); + } + } + + //Weapon Collision + { + int wid = checkWeaponCollision(mask); + if (wid != -1) { + //Pushed back + if (h->noheads == 0) { + h->hsp = weapons[wid]->dir; + PHL_PlaySound(sounds[sndPi05], CHN_ENEMIES); + }else{ + h->hp -= 1; + h->blink = 15; + } + weaponHit(weapons[wid]); + //Die + if (h->hp <= 0) { + h->state = 6; + h->timer = 180; + h->blink = 200; + } + + } + } + } + +} + +void hydraDraw(Hydra* h) +{ + if (h->blink % 2 == 0) { + int cropX = (int)h->imageIndex * 128; + int cropY = 128; + + if (h->noheads == 1) { + cropY += 128; + } + + PHL_DrawSurfacePart(h->x - 64, h->y - 64, cropX, cropY, 128, 128, images[imgBoss]); + } +} + +void hydraDestroy(Hydra* h) +{ + enemyDestroy(h->id); + bossDefeatedFlag = 1; + roomSecret = 1; + + PHL_StopMusic(); +} + +Mask getHydraMask(Hydra* h) +{ + Mask mask; + + mask.unused = mask.circle = 0; + mask.w = 84; + mask.h = 84; + mask.x = h->x - (mask.w / 2); + mask.y = h->y - 64 + (128 - mask.h); + + return mask; +} + +//#heads +int createHydrahead(int dir, int position, int bodyid) +{ + int result = -1; + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Hydrahead* h = malloc(sizeof *h); + + h->id = i; + result = i; + + h->hp = 25; + //h->hp = 1; + h->blink = 0; + + h->dir = dir; + h->position = position; + + h->imageIndex = 0; + + h->neckRot = 0; + if (position != 0) { + h->neckRot -= 45; + } + + h->state = 0; + h->timer = 0; + h->counter = 0; + + h->bodyid = bodyid; + + int a; + for (a = 0; a < 7; a++) { + h->bodyposX[a] = 0; + h->bodyposY[a] = 0; + } + + e->data = h; + e->enemyStep = hydraheadStep; + e->enemyDraw = hydraheadDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + + return result; +} + +void hydraheadStep(Hydrahead* h) +{ + char dead = 0; + + //Animate + { + h->imageIndex += 0.1; + if (h->imageIndex >= 2) { + h->imageIndex -= 2; + } + + if (h->blink > 0) { + h->blink -= 1; + } + + h->neckRot += 2; + if (h->neckRot >= 360) { + h->neckRot -= 360; + } + } + + //States + { + //Death + if (h->state == 4) { + h->timer += 1; + if (h->timer % 6 == 0) { + createEffect(2, h->bodyposX[6 - h->counter] - 32, h->bodyposY[6 - h->counter] - 32); + h->counter += 1; + } + + if (h->counter >= 7) { + dead = 1; + } + } + else{ + if (h->state == 0) { + //Do nothing special + } + + //Fast movements + else if (h->state == 1) { + h->neckRot += 2; + } + + //Shoot goop + else if (h->state == 2) { + h->neckRot += 2; + h->timer += 1; + + //Create Goop + if (h->timer % 15 == 0) { + int ghsp = -4 + (rand() % 9), + gvsp = -6; + + createHydragoop(h->bodyposX[6], h->bodyposY[6], ghsp, gvsp); + } + + if (h->timer >= 120) { + h->state = 0; + } + } + + //Shoot electricity + else if (h->state == 3) { + if (h->timer == 0) { + h->timer = 1; + } + + if (h->timer % 20 == 0) { + if (h->counter == 0) { + createHydrashock(h->bodyposX[6] + (70 * h->dir), h->bodyposY[6] + 20); + } + h->neckRot -= 2; + h->counter += 1; + if (h->counter >= 20) { + h->counter = 0; + h->timer += 1; + } + }else{ + h->neckRot += 2; + h->timer += 1; + } + + if (h->timer > 80) { + h->state = 0; + /* + Hydra* body = enemies[h->bodyid]->data; + body->state = 1; + body->timer = 239; + */ + } + + } + + Mask mask; + mask.circle = mask.unused = 0; + + //Collide with player + { + int i; + for (i = 0; i < 7; i+=2) { + //Setup mask + { + mask.w = 48; + mask.h = 48; + + //Head + if (i == 6) { + mask.w = 60; + mask.h = 36; + } + + mask.x = h->bodyposX[i] - (mask.w / 2); + mask.y = h->bodyposY[i] - (mask.h / 2); + } + + //Collide + if (checkCollision(getHeroMask(), mask) == 1) { + heroHit(25, getHydraX(h)); + } + } + } + + //Weapon collision + { + //Mask should still be on the head + int wid = checkWeaponCollision(mask); + if (wid != -1) { + h->blink = 15; + h->hp -= 1; + weaponHit(weapons[wid]); + + if (h->hp <= 0) { + h->state = 4; + h->timer = 0; + h->counter = 0; + } + } + } + + + } + + } + + //Destroy object + if (dead == 1) { + enemyDestroy(h->id); + } +} + +void hydraheadDraw(Hydrahead* h) +{ + /* + char c[10]; + sprintf(c, "%02d", h->timer); + PHL_DrawTextBold(c, h->bodyposX[6], 0, 0); + */ + + h->bodyposX[0] = getHydraX(h) + 20; + h->bodyposY[0] = getHydraY(h); + + double drawX = getHydraX(h) + 20; + double drawY = getHydraY(h); + + int dis = 24; + int angle = -25; + + if (h->position == 1) { + angle = -60; + + drawX -= 5; + drawY -= 20; + } + + int i; + for (i = 0; i < 7; i++) { + double wavlen = sin((h->neckRot + (45 * i)) * PI / 180); + + double incang = 45; + + if (h->position != 0) { + incang = 45; + } + + if (i == 6) { + //incang += 15; + incang = 50; + + if (h->position == 1) { + incang = 80; + } + } + + drawX += lengthdir_x(angle + (incang * wavlen), dis); + drawY += lengthdir_y(angle + (incang * wavlen), dis); + + h->bodyposX[i] = drawX; + h->bodyposY[i] = drawY; + + if (h->dir == -1) { + double difference = h->bodyposX[i] - getHydraX(h); + h->bodyposX[i] = getHydraX(h) - difference; + } + + if (h->blink % 2 == 0) { + if (h->state != 4 || (6 - h->counter >= i)) { + if (i != 6) { + int cropX = 0; + + if (h->dir == -1) { + cropX += 64; + } + + PHL_DrawSurfacePart(h->bodyposX[i] - 32, h->bodyposY[i] - 32, cropX, 64, 64, 64, images[imgBoss]); + }else{ + int cropX = 0; + + if (h->dir == -1) { + cropX += 320; + } + + cropX += (int)h->imageIndex * 80; + + PHL_DrawSurfacePart(h->bodyposX[i] - 40, h->bodyposY[i] - 32, cropX, 0, 80, 64, images[imgBoss]); + } + } + } + } + +} + +int checkWeaponCollision(Mask m) +{ + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(weapons[i]->weaponMask, m) == 1) { + return i; + } + } + } + } + + return -1; +} + +double lengthdir_x(double ang, double len) +{ + return cos(ang * PI / 180) * len; +} + +double lengthdir_y(double ang, double len) +{ + return sin(ang * PI / 180) * len; +} + +double getHydraX(Hydrahead* h) +{ + if (enemies[h->bodyid] != NULL) { + Hydra* hbody = enemies[h->bodyid]->data; + return hbody->x; + } + + return -1; +} + +double getHydraY(Hydrahead* h) +{ + if (enemies[h->bodyid] != NULL) { + Hydra* hbody = enemies[h->bodyid]->data; + return hbody->y; + } + + return -1; +} + +void setHeadState(int headid, int state) +{ + if (enemies[headid] != NULL) { + Hydrahead* h = enemies[headid]->data; + if (h->state != 4) { + h->state = state; + h->timer = 0; + h->counter = 0; + } + } +} + +//#goop +void createHydragoop(int x, int y, int hsp, int vsp) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Hydragoop* h = malloc(sizeof *h); + h->id = i; + + h->x = x; + h->y = y; + + h->hsp = hsp; + h->vsp = vsp; + + h->inwall = 0; + h->bounce = 0; + + h->imageIndex = 0; + + e->data = h; + e->enemyStep = hydragoopStep; + e->enemyDraw = hydragoopDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + + PHL_PlaySound(sounds[sndPi06], CHN_ENEMIES); + } + } +} + +void hydragoopStep(Hydragoop* h) +{ + char dead = 0; + + //Animate + { + h->imageIndex += 0.16; + if (h->imageIndex >= 3) { + h->imageIndex -= 3; + } + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 36; + mask.h = 36; + mask.x = h->x - mask.w / 2; + mask.y = h->y - mask.h / 2; + } + + //Movement + { + double grav = 0.2; + + h->x += h->hsp; + mask.x = h->x - mask.w / 2; + + if (checkTileCollision(1, mask) == 1) { + h->inwall = 1; + } + + h->y += h->vsp; + h->vsp += grav; + mask.y = h->y - mask.h / 2; + + if (h->inwall == 0 && h->bounce == 0) { + if (checkTileCollision(1, mask) == 1) { + h->bounce = 1; + h->vsp = -2; + } + } + } + + //Outside of room + { + if ( (h->y > 500 && h->vsp >= 0) || + (h->x < -20 && h->hsp <= 0) || + (h->x > 660 && h->hsp >= 0) ) + { + dead = 1; + } + } + + //Collide with hero + { + //Collide with shield + if (checkCollision(mask, shieldMask) == 1) { + createEffect(1, h->x - 20, h->y - 20); + PHL_PlaySound(sounds[sndHit07], CHN_EFFECTS); + dead = 1; + } + else if (checkCollision(mask, getHeroMask()) == 1) { + if (heroHit(25, h->x) == 1) { + heroPoison(); + } + } + } + + //Destroy object + { + if (dead == 1) { + enemyDestroy(h->id); + } + } + +} + +void hydragoopDraw(Hydragoop* h) +{ + int cropX = 320; + + cropX += (int)h->imageIndex * 40; + + PHL_DrawSurfacePart(h->x - 20, h->y - 20, cropX, 480, 40, 40, images[imgMisc20]); +} + +//#rock +void createHydrarock() +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Hydrarock* h = malloc(sizeof *h); + h->id = i; + + h->x = 70 + (rand() % 26) * 20; + h->y = -24; + + h->vsp = 0; + + h->bounce = 0; + + h->imageIndex = 0; + + e->data = h; + e->enemyStep = hydrarockStep; + e->enemyDraw = hydrarockDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void hydrarockStep(Hydrarock* h) +{ + //Animate + { + h->imageIndex += 0.25; + if (h->imageIndex >= 8) { + h->imageIndex -= 8; + } + } + + //Movement + { + double grav = 0.15; + + h->y += h->vsp; + h->vsp += grav; + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 44; + mask.h = 44; + mask.x = h->x - mask.w / 2; + mask.y = h->y - mask.h / 2; + } + + if (h->bounce == 0) { + if (checkTileCollision(1, mask) == 1) { + h->bounce = 1; + h->vsp = -2; + PHL_PlaySound(sounds[sndHit06], CHN_ENEMIES); + } + } + + //Hero collision + { + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(30, h->x); + } + } + + //Weapon Collision + { + int wid = checkWeaponCollision(mask); + if (wid != -1) { + weaponHit(weapons[wid]); + PHL_PlaySound(sounds[sndHit03], CHN_WEAPONS); + } + } + + if (h->y >= 520) { + enemyDestroy(h->id); + } +} + +void hydrarockDraw(Hydrarock* h) +{ + int cropX = 128; + + cropX += (int)h->imageIndex * 64; + + PHL_DrawSurfacePart(h->x - 32, h->y - 32, cropX, 128, 64, 64, images[imgMisc32]); +} + +//#electricity +void createHydrashock(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Hydrashock* h = malloc(sizeof *h); + h->id = i; + + h->timer = 0; + + h->x = x; + h->y = y; + + h->angle = 0; + + h->imageIndex = 0; + + e->data = h; + e->enemyStep = hydrashockStep; + e->enemyDraw = hydrashockDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + + PHL_PlaySound(sounds[sndShot03], CHN_ENEMIES); + } + } +} + +void hydrashockStep(Hydrashock* h) +{ + //Animate + { + h->imageIndex += 0.5; + if (h->imageIndex >= 4) { + h->imageIndex -= 4; + } + } + + h->timer += 1; + + if (h->timer >= 20) { + if (h->timer == 20) { + //Set angle + h->angle = (atan2(h->x - (herox), heroy + 20 - h->y) * 180 / PI) + 90; + } + + h->timer = 22; + + //Movement + { + int spd = 5; + h->x += lengthdir_x(h->angle, spd); + h->y += lengthdir_y(h->angle, spd); + } + } + + //Setup mask + Mask mask; + { + mask.unused = mask.circle = 0; + mask.w = 28; + mask.h = 28; + mask.x = h->x - mask.w / 2; + mask.y = h->y - mask.h / 2; + } + + //Hero Collision + { + if (checkCollision(mask, getHeroMask()) == 1) { + if (heroHit(25, h->x) == 1) { + heroStun(); + } + } + } + + //Destroy if outside of room + { + if (mask.x > 660 || mask.x + mask.w < -20 || mask.y > 500 || mask.y + mask.h < -20) { + enemyDestroy(h->id); + } + } +} + +void hydrashockDraw(Hydrashock* h) +{ + if (h->timer % 2 == 0) { + int cropX = (int)h->imageIndex * 64; + + PHL_DrawSurfacePart(h->x - 32, h->y - 32, cropX, 192, 64, 64, images[imgMisc32]); + } +} \ No newline at end of file diff --git a/src/enemies/hydra.h b/src/enemies/hydra.h new file mode 100644 index 0000000..40cc28f --- /dev/null +++ b/src/enemies/hydra.h @@ -0,0 +1,65 @@ +#ifndef HYDRA_H +#define HYDRA_H + +typedef struct { + int id; + int hp, blink; + double x, y; + double hsp, vsp; + double imageIndex; + int state, timer; + int patternCounter; + char onground; + char noheads; + int headid[4]; +} Hydra; + +void createHydra(int x); + +typedef struct { + int id; + int hp, blink; + int dir; + int position; //0 = lower 1 = higher + double imageIndex; + double neckRot; + int state, timer, counter; + int bodyid; + double bodyposX[7]; + double bodyposY[7]; +} Hydrahead; + +int createHydrahead(int dir, int position, int bodyid); + +typedef struct { + int id; + double x, y; + double hsp, vsp; + char inwall; + char bounce; + double imageIndex; +} Hydragoop; + +void createHydragoop(int x, int y, int hsp, int vsp); + +typedef struct { + int id; + double x, y; + double vsp; + char bounce; + double imageIndex; +} Hydrarock; + +void createHydrarock(); + +typedef struct { + int id; + int timer; + double x, y; + double angle; + double imageIndex; +} Hydrashock; + +void createHydrashock(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/jellyfish.c b/src/enemies/jellyfish.c new file mode 100644 index 0000000..def3198 --- /dev/null +++ b/src/enemies/jellyfish.c @@ -0,0 +1,195 @@ +#include "jellyfish.h" +#include "../enemy.h" +#include "../game.h" +#include "../hero.h" +#include +#include + +void jellyfishStep(Jellyfish* j); +void jellyfishDraw(Jellyfish* j); + +void createJellyfish(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Jellyfish* j = malloc(sizeof *j); + j->id = i; + + j->x = x; + j->y = j->ystart = y; + j->ystart += 20; + + j->spd = 0; + j->angle = 0; + + j->state = 0; + j->imageIndex = 0; + + e->data = j; + e->enemyStep = jellyfishStep; + e->enemyDraw = jellyfishDraw; + e->type = 20; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void jellyfishStep(Jellyfish* j) +{ + Mask mask; + mask.unused = mask.circle = 0; + mask.w = mask.h = 30; + mask.x = j->x + 20 - (mask.w / 2); + mask.y = j->y + 20 - (mask.h / 2); + + //Idle float + if (j->state == 0) + { + //Animate + j->imageIndex += 0.06; + if (j->imageIndex >= 4) { + j->imageIndex -= 4; + } + + //Movement + j->angle += 2.5; + if (j->angle >= 360) { j->angle -= 360; } + j->y = j->ystart + (20 * sin(j->angle * 3.14159 / 180)); + + //Update mask + mask.y = j->y + 20 - (mask.h / 2); + + //if player is close enough + Mask area; + area.unused = area.circle = 0; + area.w = area.h = 160; + area.x = j->x - 60; + area.y = j->y - 60; + + if (checkCollision(area, getHeroMask()) == 1) { + j->state = 1; + j->spd = 0; + } + } + //Attack + if (j->state == 1) + { + //Setup + if (j->spd == 0) { + PHL_PlaySound(sounds[sndPi02], CHN_ENEMIES); + j->spd = 3; + + //Move Right + if (herox > j->x + 20) { + //Move Up + if (heroy < j->y) { + j->angle = 135; + } + //Move Down + else { + j->angle = 45; + } + } + //Move Left + else{ + //Move Up + if (heroy < j->y) { + j->angle = 225; + } + //Move Down + else { + j->angle = 315; + } + } + } + + //Movement + j->x += (j->spd) * sin(j->angle * 3.14159 / 180); + j->y += (j->spd) * cos(j->angle * 3.14159 / 180); + + //Slow down + j->spd -= 0.075; + if (j->spd <= 0) { + j->spd = 0; + j->state = 2; + } + } + //Stablize + if (j->state == 2) + { + //Setup + if (j->spd == 0) { + j->spd = 1; + j->ystart = j->y - 20; + j->angle = 80; + } + + //Movement + j->angle += 2.5; + if (j->angle >= 360) { j->angle -= 360; } + j->y = j->ystart + (20 * sin(j->angle * 3.14159 / 180)); + + + if (j->angle >= 180) { + j->state = 0; + j->ystart = j->y - 20; + j->angle = 100; + } + } + + //Update Mask + mask.x = j->x + 20 - (mask.w / 2); + mask.y = j->y + 20 - (mask.h / 2); + + //Collide with hero + if (checkCollision(mask, getHeroMask())) { + heroHit(15, j->x + 20); + } + + //Sword collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + spawnCollectable(j->x + 20, j->y); + weaponHit(weapons[i]); + + createEffect(2, j->x - 12, j->y - 12); + enemyDestroy(j->id); + + i = MAX_WEAPONS; + } + } + } +} + +void jellyfishDraw(Jellyfish* j) +{ + int frame = 0; + + //if (j->state == 0) { + int animation[4] = { 0, 1, 0, 2}; + frame = animation[(int)j->imageIndex]; + //} + + if (j->state == 1) { + if (j->angle == 135) { + frame = 3; + } + else if (j->angle == 225) { + frame = 4; + } + else if (j->angle == 315) { + frame = 5; + } + else { + frame = 6; + } + } + + PHL_DrawSurfacePart(j->x, j->y, frame * 40, 520, 40, 40, images[imgEnemies]); +} \ No newline at end of file diff --git a/src/enemies/jellyfish.h b/src/enemies/jellyfish.h new file mode 100644 index 0000000..c247fe3 --- /dev/null +++ b/src/enemies/jellyfish.h @@ -0,0 +1,16 @@ +#ifndef JELLYFISH_H +#define JELLYFISH_H + +typedef struct { + int id; + double x, y; + int ystart; + double spd; + double angle; + int state; + double imageIndex; +} Jellyfish; + +void createJellyfish(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/knight.c b/src/enemies/knight.c new file mode 100644 index 0000000..06df4b7 --- /dev/null +++ b/src/enemies/knight.c @@ -0,0 +1,232 @@ +#include "knight.h" +#include "../enemy.h" +#include "../hero.h" +#include "../PHL.h" +#include "../game.h" +#include + +void knightDestroy(Knight* k); + +void createKnight(int x, int y, int type) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Knight* k = malloc(sizeof *k); + + k->id = i; + k->type = type; + + k->x = x; + k->y = y; + + //They face the player when they are spawned + k->dir = -1; + if (herox > x + 20) { + k->dir = 1; + } + + k->vsp = 0; + k->grav = 0.2; + + k->state = 0; + k->timer = 60 + (((rand() % 5) + 1) * 60); + k->imageIndex = 0; + + k->hp = 2; + //Shield Knight + if (k->type == 1) { + k->hp = 3; + } + + k->invincible = 0; + k->shieldhit = 0; + + k->mask.circle = 0; + k->mask.unused = 0; + k->mask.x = x + 4; + k->mask.y = y + 8; + k->mask.w = 32; + k->mask.h = 32; + + e->data = k; + e->enemyStep = knightStep; + e->enemyDraw = knightDraw; + e->type = 3; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void knightStep(Knight* k) +{ + if (k->shieldhit > 0) { + k->shieldhit -= 1; + } + + if (k->invincible > 0) { + k->invincible -= 1; + } + + if (k->state == 0) { //Walk + k->imageIndex += 0.1; + if (k->imageIndex >= 2) { + k->imageIndex -= 2; + } + + double spd = 1; + if (k->type == 1) { + spd = 0.5; + } + spd *= k->dir; + + k->x += spd; + + k->mask.x = k->x + 4; + k->mask.y = k->y + 8; + + Mask emask; + emask.circle = emask.unused = 0; + emask.w = 16; + emask.h = 32; + emask.x = k->x + 12; + emask.y = k->y + 8; + + //Turn when colliding with a wall + if (checkTileCollision(1, emask)) { + k->dir *= -1; + }else{ + //Turn when on an edge + k->mask.x += k->mask.w * k->dir; + k->mask.y += 1; + PHL_Rect collide = getTileCollision(1, k->mask); + if (collide.x == -1) { + collide = getTileCollision(3, k->mask); + } + if (collide.x == -1) { + k->dir *= -1; + } + } + + if (k->x + 20 >= 640 || k->x + 20 <= 0) { + k->dir *= -1; + } + + k->mask.x = k->x + 4; + k->mask.y = k->y + 8; + + k->timer -= 1; + if (k->timer <= 0) { + k->state = 1; + k->timer = 120; + k->imageIndex = 0; + } + } + else if (k->state == 1) { //Wait + k->timer -= 1; + if (k->timer <= 0) { + k->state = 0; + k->dir = 1; + if (herox < k->x + 20) { + k->dir = -1; + } + k->timer = 60 + (((rand() % 5) + 1) * 60); + } + } + + //Green Sword Knight + if (k->type == 0) { + //Hit player + Mask swordMask; + swordMask.unused = 0; + swordMask.circle = 0; + swordMask.x = k->x + (24 * k->dir); + swordMask.y = k->y + 20; + swordMask.w = 40; + swordMask.h = 10; + + if (checkCollision(getHeroMask(), swordMask)) { + heroHit(30, k->x + 20); + } + } + + if (checkCollision(getHeroMask(), k->mask)) { + heroHit(15, k->x + 20); + } + + //Weapon collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(k->mask, weapons[i]->weaponMask)) { + char gotHit = 1; + + int weapondir = weapons[i]->dir; + weaponHit(weapons[i]); + + //Shield Collision + if (k->type == 1) { + if (weapondir == k->dir * -1) { + gotHit = 0; + k->shieldhit = 15; + PHL_PlaySound(sounds[sndHit03], CHN_WEAPONS); + } + } + + if (gotHit == 1) { + k->hp -= 1; + k->invincible = 15; + + i = MAX_WEAPONS; + } + + if (k->hp <= 0) { + knightDestroy(k); + } + } + } + } + } +} + +void knightDraw(Knight* k) +{ + if (k->invincible % 2 == 0) { + int cx = 0, cy = 200; + + //Green Knight's Sword + if (k->type == 0) { + int swordimg = 0; + if (k->dir == -1) { + swordimg = 1; + } + int posx = 24, posy = 8; + if ((int)k->imageIndex == 1) { + posx -= 2; + posy -= 2; + } + PHL_DrawSurfacePart(k->x + (posx * k->dir), k->y + posy, 160 + (swordimg * 40), 200, 40, 40, images[imgEnemies]); + } + + //Shield Knight + if (k->type == 1) { + cx = 240; + } + + if (k->dir == -1) { + cx += 80; + } + PHL_DrawSurfacePart(k->x, k->y, cx + ((int)k->imageIndex * 40), cy, 40, 40, images[imgEnemies]); + } +} + +void knightDestroy(Knight* k) +{ + createEffect(2, k->x - 12, k->y - 6); + spawnCollectable(k->x + 20, k->y); + enemyDestroy(k->id); +} \ No newline at end of file diff --git a/src/enemies/knight.h b/src/enemies/knight.h new file mode 100644 index 0000000..a709fc8 --- /dev/null +++ b/src/enemies/knight.h @@ -0,0 +1,23 @@ +#ifndef KNIGHT_H +#define KNIGHT_H + +#include "../collision.h" + +typedef struct { + int id, type; + double x, y, + vsp, grav; + int dir, state, timer; + double imageIndex; + int hp, invincible; + int shieldhit; + + Mask mask; +} Knight; + +void createKnight(int x, int y, int type); + +void knightStep(Knight* k); +void knightDraw(Knight* k); + +#endif \ No newline at end of file diff --git a/src/enemies/lolidra.c b/src/enemies/lolidra.c new file mode 100644 index 0000000..15879a4 --- /dev/null +++ b/src/enemies/lolidra.c @@ -0,0 +1,367 @@ +#include "lolidra.h" +#include "../enemy.h" +#include "../game.h" +#include "../hero.h" +#include +#include + +int boss5flag = 38; + +void lolidraDestroy(Lolidra* l); +int getNumOfMinions(); + +void createLolidra(int x, int y) +{ + if (flags[boss5flag] == 0) { //have not beaten boss 5 + PHL_FreeSurface(images[imgBoss]); + images[imgBoss] = PHL_LoadQDA("boss05.bmp"); + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + //Boss start + setBossRoom(); + + Enemy* e = malloc(sizeof *e); + Lolidra* l = malloc(sizeof *l); + l->id = i; + + l->x = x; + l->y = y; + + l->positionY = l->y; + + l->imageIndex = 0; + l->hoverRot = 0; + + l->hp = 100; + //l->hp = 1; + l->state = 0; + l->invincible = 0; + l->visible = 1; + + l->timer = 0; + l->counter = 0; + + l->mask.unused = 0; + l->mask.circle = 1; + l->mask.w = 46; + l->mask.h = 0; + l->mask.x = l->x; + l->mask.y = l->y; + + e->data = l; + e->enemyStep = lolidraStep; + e->enemyDraw = lolidraDraw; + e->type = 44; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } + } +} + +void lolidraStep(Lolidra* l) +{ + char dead = 0; + + l->imageIndex += 0.1; + if (l->imageIndex >= 3) { + l->imageIndex -= 3; + } + + if (l->invincible > 0) { + l->invincible -= 1; + } + + //Spawn minions + if (l->state == 0) + { + if (l->counter < 5) { + l->counter += 1; + }else{ + if (getNumOfMinions() < 10) { + l->counter = 0; + PHL_PlaySound(sounds[sndPi02], CHN_ENEMIES); + createMinion(l->x, l->y - 10); + } + } + + l->timer += 1; + if (l->timer >= 600){ + l->counter = 0; + l->timer = 0; + l->state = 1; + l->invincible = 20; + } + } + //Disappear + else if (l->state == 1 || l->state == 3) + { + if (l->invincible <= 0) { + l->visible = 0; + } + + if (l->timer == 0) { + PHL_PlaySound(sounds[sndPi10], CHN_ENEMIES); + } + + l->timer += 1; + if (l->timer >= 330) { + PHL_PlaySound(sounds[sndPi03], CHN_ENEMIES); + l->timer = 0; + l->visible = 1; + l->invincible = 20; + l->x = herox; + l->positionY = heroy - 40; + + if (l->state == 1) { + l->state = 2; + } + if (l->state == 3) { + l->state = 0; + } + } + } + //Pop-up + else if (l->state == 2) + { + l->timer += 1; + if (l->timer >= 180) { + l->timer = 0; + l->state = 3; + l->invincible = 20; + } + } + //Death + else if (l->state == 4) + { + l->y += 0.2; + + l->timer -= 1; + l->invincible -= 1; + + if (l->timer % 12 == 0) { + createEffect(2, l->x - 64 + (rand() % 128), l->y - 64 + (rand() % 128)); + } + + if (l->timer <= 0) { + lolidraDestroy(l); + dead = 1; + } + } + + if (dead == 0) + { + if (l->state != 4) { + //Hover + l->hoverRot += 5; + if (l->hoverRot >= 360) { + l->hoverRot -= 360; + } + l->y = l->positionY + (5 * sin(l->hoverRot * 3.14159 / 180)); + + //Update Mask + l->mask.x = l->x; + l->mask.y = l->y; + + //Collisions + if (l->visible == 1) { + //Collide with Hero + if (checkCollision(getHeroMask(), l->mask) == 1) { + heroHit(30, l->x); + } + + //Weapon collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(l->mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + l->invincible = 15; + l->hp -= 1; + + //Die + if (l->hp <= 0) { + l->state = 4; + l->timer = 180; + l->invincible = 200; + } + + i = MAX_WEAPONS; + } + } + } + } + } + } + } + +} + +void lolidraDraw(Lolidra* l) +{ + if (l->visible == 1 && l->invincible % 2 == 0) { + PHL_DrawSurfacePart(l->x - 64, l->y - 74, ((int)l->imageIndex) * 128, 0, 128, 128, images[imgBoss]); + } +} + +void lolidraDestroy(Lolidra* l) +{ + enemyDestroy(l->id); + bossDefeatedFlag = 1; + roomSecret = 1; + + flags[boss5flag] = 1; + PHL_StopMusic(); +} + +//Minions +void createMinion(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Minion* m = malloc(sizeof *m); + m->id = i; + + m->state = 0; + m->timer = 0; + + m->x = x; + m->y = y; + + m->positionY = m->y; + + m->dir = rand() % 360; + m->spd = 8; + + m->imageIndex = 0; + + m->mask.circle = 1; + m->mask.unused = 0; + m->mask.w = 10; + m->mask.x = 0; + m->mask.y = 0; + + e->data = m; + e->enemyStep = minionStep; + e->enemyDraw = minionDraw; + e->type = 23; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void minionStep(Minion* m) +{ + char dead = 0; + + m->imageIndex += 0.2; + if (m->imageIndex >= 2) { + m->imageIndex -= 2; + } + + //Slow down + if (m->state == 0) + { + if (m->spd > 0) { + m->spd -= 0.3; + } + + if (m->spd <= 0) { + m->positionY = m->y; + m->spd = 0; + m->dir = 0; + m->state = 1; + } + } + //Hover + else if (m->state == 1) + { + //Hover + m->dir += 5; + if (m->dir >= 360) { + m->dir -= 360; + } + m->y = m->positionY + (10 * sin(m->dir * 3.14159 / 180)); + + m->timer += 1; + if (m->timer >= 120) { + m->timer = 0; + m->state = 2; + m->spd = (rand() % 2) + 1; + m->dir = (atan2(heroy + 20 - m->y, m->x - herox) * 180 / 3.14159) + 270; + } + } + //Suicide + else if (m->state == 2) + { + m->timer += 1; + if (m->timer >= 120) { + createEffect(5, m->x, m->y); + enemyDestroy(m->id); + dead = 1; + } + } + + if (dead == 0) + { + //Movement + if (m->spd != 0) { + m->x += m->spd * sin(m->dir * 3.14159 / 180); + m->y += m->spd * cos(m->dir * 3.14159 / 180); + } + + //Update Mask + m->mask.x = m->x; + m->mask.y = m->y; + + //Collide with Hero + if (checkCollision(getHeroMask(), m->mask) == 1) { + if (heroHit(10, m->x) == 1) { + heroPoison(); + } + } + + //Weapon collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(m->mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + createEffect(2, m->x - 32, m->y - 32); + enemyDestroy(m->id); + + i = MAX_WEAPONS; + } + } + } + } +} + +void minionDraw(Minion* m) +{ + PHL_DrawSurfacePart(m->x - 32, m->y - 32, ((int)m->imageIndex) * 64, 128, 64, 64, images[imgBoss]); +} + +int getNumOfMinions() +{ + int result = 0; + + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] != NULL) { + if (enemies[i]->type == 23) { + result += 1; + } + } + } + + return result; +} \ No newline at end of file diff --git a/src/enemies/lolidra.h b/src/enemies/lolidra.h new file mode 100644 index 0000000..f9a9b51 --- /dev/null +++ b/src/enemies/lolidra.h @@ -0,0 +1,40 @@ +#ifndef LOLIDRA_H +#define LOLIDRA_H + +#include "../collision.h" + +typedef struct { + int id; + double x, y; + double positionY; + double imageIndex, hoverRot; + int hp, state, invincible, + visible, timer, counter; + + Mask mask; +} Lolidra; + +void createLolidra(int x, int y); + +void lolidraStep(Lolidra* l); +void lolidraDraw(Lolidra* l); + +//Minion +typedef struct { + int id; + int state; + int timer; + double x, y; + double positionY; + double imageIndex; + double dir, spd; + + Mask mask; +} Minion; + +void createMinion(int x, int y); + +void minionStep(Minion* m); +void minionDraw(Minion* m); + +#endif \ No newline at end of file diff --git a/src/enemies/pendulum.c b/src/enemies/pendulum.c new file mode 100644 index 0000000..4eda905 --- /dev/null +++ b/src/enemies/pendulum.c @@ -0,0 +1,78 @@ +#include "pendulum.h" +#include "../enemy.h" +#include "../game.h" +#include "../hero.h" +#include +#include + +void createPendulum(int x, int y, int side) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Pendulum* p = malloc(sizeof *p); + p->id = i; + + p->x = x; + p->y = y; + + p->angle = 0; + p->rotCounter = 180; + if (side == 1) { + p->rotCounter += 180; + } + + p->mask.circle = 1; + p->mask.unused = 0; + p->mask.w = 24; + p->mask.x = 0; + p->mask.y = 0; + + e->data = p; + e->enemyStep = pendulumStep; + e->enemyDraw = pendulumDraw; + e->type = 22; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void pendulumStep(Pendulum* p) +{ + p->rotCounter += 2; + if (p->rotCounter >= 360) { + p->rotCounter -= 360; + } + + p->angle += (3.15 * cos(p->rotCounter * 3.14159 / 180)); + + //Update Mask + p->mask.x = p->x + (96 * cos((p->angle + 90) * 3.14159 / 180)); + p->mask.y = p->y + (96 * sin((p->angle + 90) * 3.14159 / 180)); + + //Hit Player + if (checkCollision(p->mask, getHeroMask())) { + heroHit(15, p->mask.x); + } +} + +void pendulumDraw(Pendulum* p) +{ + int drawX = p->x, + drawY = p->y; + + int len[] = {0, 16, 32, 48, 66, 96}; + int cropX[] = {64, 64, 64, 64, 0, 576}; + int cropY[] = {128, 128, 128, 128, 128, 64}; + + int i; + for (i = 0; i < 6; i++) { + drawX = p->x + (len[i] * cos((p->angle + 90) * 3.14159 / 180)); + drawY = p->y + (len[i] * sin((p->angle + 90) * 3.14159 / 180)); + + PHL_DrawSurfacePart(drawX- 32, drawY - 32, cropX[i], cropY[i], 64, 64, images[imgMisc32]); + } +} \ No newline at end of file diff --git a/src/enemies/pendulum.h b/src/enemies/pendulum.h new file mode 100644 index 0000000..faaba05 --- /dev/null +++ b/src/enemies/pendulum.h @@ -0,0 +1,19 @@ +#ifndef PENDULUM_H +#define PENDULUM_H + +#include "../collision.h" + +typedef struct { + int id; + double x, y; + double rotCounter, angle; + + Mask mask; +} Pendulum; + +void createPendulum(int x, int y, int side); + +void pendulumStep(Pendulum* p); +void pendulumDraw(Pendulum* p); + +#endif \ No newline at end of file diff --git a/src/enemies/podoboo.c b/src/enemies/podoboo.c new file mode 100644 index 0000000..1dfc30b --- /dev/null +++ b/src/enemies/podoboo.c @@ -0,0 +1,176 @@ +#include "podoboo.h" +#include "../PHL.h" +#include "../hero.h" +#include "../game.h" +#include "../effect.h" +#include +#include + +void podobooStep(Podoboo* p); +void podobooDraw(Podoboo* p); + +void createPodoboo(int x, int y, int offset, int height) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Podoboo* p = malloc(sizeof *p); + + p->id = i; + + p->x = x; + p->y = p->ystart = y; + + p->hp = 2; + p->blink = 0; + + p->yoffset = p->rot = 0; + + p->vsp = 0; + p->grav = 0.13; + + p->jumpheight = -5; + /* + if (height == 1) { + p->jumpheight = -5.4; + } + */ + if (height == 1) { + p->jumpheight = -7; + } + + p->imageIndex = 0; + + p->timer = 30 * offset; + p->state = 0; + + e->data = p; + e->enemyStep = podobooStep; + e->enemyDraw = podobooDraw; + e->type = 15; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void podobooStep(Podoboo* p) +{ + //Blinking + { + if (p->blink > 0) { + p->blink -= 1; + } + } + + p->timer -= 1; + + //Patterns + { + //Float in lava + if (p->state == 0) + { + //Animate + p->imageIndex += 0.1; + if (p->imageIndex >= 2) { + p->imageIndex -= 2; + } + + //Bob movement + p->rot += 5; + if (p->rot >= 360) { + p->rot -= 360; + } + p->y = p->ystart + (5 * sin(p->rot * 3.14159 / 180)); + + //Jump + if (p->timer <= 0) { + p->state = 1; + createLavaSplash(p->x + 20, p->y); + + p->y = p->ystart; + p->vsp = p->jumpheight; + } + } + //In air + else if (p->state == 1) + { + //Animate + p->imageIndex += 0.25; + if (p->imageIndex >= 3) { + p->imageIndex -= 3; + } + + //Movement + p->y += p->vsp; + p->vsp += p->grav; + + //Land in lava again + if (p->vsp > 0 && p->y >= p->ystart) { + createLavaSplash(p->x + 20, p->y); + p->y = p->ystart; + p->state = 0; + p->vsp = 0; + p->timer = 60; + } + } + + } + + //Create Mask + Mask mask; + { + mask.unused = mask.circle = 0; + mask.w = mask.h = 30; + mask.x = p->x + 5; + mask.y = p->y + 5; + } + + //Collide with hero + { + if (checkCollision(mask, getHeroMask())) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + p->hp -= 1; + p->blink = 15; + + //Death + if (p->hp <= 0) { + createEffect(2, p->x - 12, p->y - 12); + spawnCollectable(p->x + 20, p->y); + enemyDestroy(p->id); + } + + i = MAX_WEAPONS; + } + } + } + } + } + +} + +void podobooDraw(Podoboo* p) +{ + if (p->blink % 2 == 0) { + int thisImage = p->imageIndex; + + if (p->state == 1) { + thisImage += 2; + } + + PHL_DrawSurfacePart(p->x, p->y, 280 + (40 * thisImage), 520, 40, 40, images[imgEnemies]); + } +} \ No newline at end of file diff --git a/src/enemies/podoboo.h b/src/enemies/podoboo.h new file mode 100644 index 0000000..eec8ba6 --- /dev/null +++ b/src/enemies/podoboo.h @@ -0,0 +1,20 @@ +#ifndef PODOBOO_H +#define PODOBOO_H + +typedef struct { + int id; + double x, y; + int ystart; + int hp; + int blink; + int rot; + double yoffset; + double vsp, grav; + double jumpheight; + double imageIndex; + int timer, state; +} Podoboo; + +void createPodoboo(int x, int y, int offset, int height); + +#endif \ No newline at end of file diff --git a/src/enemies/poisonknight.c b/src/enemies/poisonknight.c new file mode 100644 index 0000000..5997ce4 --- /dev/null +++ b/src/enemies/poisonknight.c @@ -0,0 +1,318 @@ +#include "poisonknight.h" +#include "../game.h" +#include "../hero.h" +#include + +void poisonknightStep(Poisonknight* p); +void poisonknightDraw(Poisonknight* p); + +void goopStep(Goop* g); +void goopDraw(Goop* g); + +void createPoisonknight(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Poisonknight* p = malloc(sizeof *p); + p->id = i; + p->hp = 2; + + p->x = x; + p->y = y; + + p->imageIndex = 0; + + p->dir = 1; + if (herox < p->x) { + p->dir = -1; + } + + p->blink = 0; + p->timer = 0; + p->state = 0; + + e->data = p; + e->enemyStep = poisonknightStep; + e->enemyDraw = poisonknightDraw; + e->type = 29; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void poisonknightStep(Poisonknight* p) +{ + char dead = 0; + + //Animate + { + p->imageIndex += 0.1; + if (p->imageIndex >= 2) { + p->imageIndex -= 2; + } + + if (p->blink > 0) { + p->blink -= 1; + } + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 38; + mask.h = 36; + mask.x = p->x + ((40 - mask.w) / 2); + mask.y = p->y + (40 - mask.h); + } + + //Walk + if (p->state == 0) { + double hsp = 1; + + p->x += hsp * p->dir; + mask.x = p->x + ((40 - mask.w) / 2); + + //Hit wall + if (checkTileCollision(1, mask) == 1) { + p->dir *= -1; + } + + //On wall edge + else { + mask.x += mask.w * p->dir; + mask.y += 10; + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + if (collide.x == -1) { + p->dir *= -1; + } + } + + //Hero is close enough + { + if (p->timer <= 0) { + Mask area; + area.circle = area.unused = 0; + area.x = p->x - 110; + area.y = p->y; + area.w = 260; + area.h = 40; + + if (checkCollision(area, getHeroMask()) == 1) { + p->dir = 1; + if (herox < p->x + 20) { + p->dir = -1; + } + p->imageIndex = 1; + p->timer = 0; + p->state = 1; + } + + }else{ + p->timer -= 1; + } + } + } + + //*beat* + else if (p->state == 1) + { + //Animate + p->imageIndex = 1; + + p->timer += 1; + if (p->timer >= 15) { + p->state = 2; + p->timer = 0; + p->imageIndex = 2; + } + } + + //Shoot goop + else if (p->state == 2) + { + //Shoot goop + if (p->timer == 0) { + PHL_PlaySound(sounds[sndPi05], CHN_ENEMIES); + createGoop(p->x + (20 * p->dir), p->y - 2, p->dir); + } + + //Animate + p->imageIndex = 2; + + p->timer += 1; + if (p->timer >= 25) { + p->state = 0; + p->timer = 240; + } + } + + //Update Mask + mask.x = p->x + ((40 - mask.w) / 2); + mask.y = p->y + (40 - mask.h); + + + //Collide with hero + { + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + p->hp -= 1; + p->blink = 15; + + if (p->hp <= 0) { + dead = 1; + createEffect(2, p->x - 12, p->y - 6); + spawnCollectable(p->x + 20, p->y); + } + } + } + } + } + } + + //Destroy object + { + if (dead == 1) { + enemyDestroy(p->id); + } + } +} + +void poisonknightDraw(Poisonknight* p) +{ + if (p->blink % 2 == 0) { + int cropX = (int)p->imageIndex * 40; + + if (p->dir == -1) { + cropX += 120; + } + + PHL_DrawSurfacePart(p->x, p->y, cropX, 280, 40, 40, images[imgEnemies]); + } +} + +//Poison Goop +void createGoop(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Goop* g = malloc(sizeof *g); + g->id = i; + + g->x = x; + g->y = y; + + g->dir = dir; + + g->imageIndex = 0; + + e->data = g; + e->enemyStep = goopStep; + e->enemyDraw = goopDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void goopStep(Goop* g) +{ + char dead = 0; + + //Animate + { + g->imageIndex += 0.33; + if (g->imageIndex >= 3) { + g->imageIndex -= 3; + } + } + + //Movement + { + int hsp = 4; + g->x += hsp * g->dir; + } + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 22; + mask.h = 22; + mask.x = g->x + ((40 - mask.w) / 2); + mask.y = g->y + ((40 - mask.h) / 2); + } + + //Collide with hero + { + //Collide with shield + if (checkCollision(mask, shieldMask) == 1) { + dead = 1; + PHL_PlaySound(sounds[sndHit07], CHN_EFFECTS); + createEffect(1, g->x, g->y); + } + //Collide with hero + else if (checkCollision(mask, getHeroMask()) == 1) { + if (heroHit(10, mask.x + (mask.w / 2)) == 1) { + heroPoison(); + } + } + } + + //Collide with wall + { + if (checkTileCollision(1, mask) == 1) { + dead = 1; + createEffect(1, g->x, g->y); + } + } + + //Destroy if out of room + { + if (g->x + 40 < 0 || g->x > 640) { + dead = 1; + } + } + + //Destroy object + { + if (dead == 1) { + enemyDestroy(g->id); + } + } +} + +void goopDraw(Goop* g) +{ + int cropX = 400 + ((int)g->imageIndex * 40); + + if (g->dir == -1) { + cropX += 120; + } + + PHL_DrawSurfacePart(g->x, g->y, cropX, 520, 40, 40, images[imgMisc20]); +} \ No newline at end of file diff --git a/src/enemies/poisonknight.h b/src/enemies/poisonknight.h new file mode 100644 index 0000000..13350d0 --- /dev/null +++ b/src/enemies/poisonknight.h @@ -0,0 +1,26 @@ +#ifndef POISONKNIGHT_H +#define POISONKNIGHT_H + +typedef struct { + int id; + int hp; + double x, y; + double imageIndex; + int dir; + int blink; + int timer; + int state; +} Poisonknight; + +void createPoisonknight(int x, int y); + +typedef struct { + int id; + double x, y; + int dir; + double imageIndex; +} Goop; + +void createGoop(int x, int y, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/pumpkin.c b/src/enemies/pumpkin.c new file mode 100644 index 0000000..8feec0f --- /dev/null +++ b/src/enemies/pumpkin.c @@ -0,0 +1,375 @@ +#include "pumpkin.h" +#include "../game.h" +#include "../hero.h" +#include + +void pumpkinenemyStep(Pumpkinenemy* p); +void pumpkinenemyDraw(Pumpkinenemy* p); + +void pumpkinheadStep(Pumpkinhead* p); +void pumpkinheadDraw(Pumpkinhead* p); + +void createPumpkinenemy(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Pumpkinenemy* p = malloc(sizeof *p); + + p->id = i; + + p->hp = 3; + p->blink = 0; + + p->x = x; + p->y = y; + + p->dir = 1; + if (herox < p->x + 20) { + p->dir = -1; + } + + p->imageIndex = 0; + + p->state = 0; + p->timer = 0; + + e->data = p; + e->enemyStep = pumpkinenemyStep; + e->enemyDraw = pumpkinenemyDraw; + e->type = 32; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void pumpkinenemyStep(Pumpkinenemy* p) +{ + //Setup Mask + Mask mask; + { + mask.unused = mask.circle = 0; + mask.w = 20; + mask.h = 38; + mask.x = p->x + ((40 - mask.w) / 2); + mask.y = p->y + (40 - mask.h); + } + + //Animate + { + p->imageIndex += 0.1; + if (p->imageIndex >= 2) { + p->imageIndex -= 2; + } + + if (p->blink > 0) { + p->blink -= 1; + } + } + + //Walking + if (p->state == 0) + { + double hsp = 0.5; + p->x += hsp * p->dir; + mask.x = p->x + ((40 - mask.w) / 2); + + //Hit wall + { + if (checkTileCollision(1, mask) == 1 || mask.x > 640 || mask.x + mask.w < 0) { + p->dir *= -1; + } + } + + //On edge + { + mask.x += mask.w * p->dir; + mask.y += 20; + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + if (collide.x == -1) { + p->dir *= -1; + } + } + + //Player is close + { + if (p->timer <= 0) { + Mask area; + { + area.circle = area.unused = 0; + area.w = 240; + area.h = 80; + area.x = p->x - 100; + area.y = p->y - 40; + } + if (checkCollision(area, getHeroMask()) == 1) { + p->state = 1; + p->timer = 0; + p->dir = 1; + if (herox < p->x + 20) { + p->dir = -1; + } + } + + }else{ + p->timer -= 1; + } + } + + } + + //Deheaded + else if (p->state == 1) { + //Animate + { + p->imageIndex = 0; + if (p->timer >= 15) { + p->imageIndex = 2; + } + } + + p->timer += 1; + if (p->timer == 15) { + createPumpkinhead(p->x, p->y - 6, p->dir); + PHL_PlaySound(sounds[sndPi05], CHN_ENEMIES); + } + + if (p->timer >= 40) { + p->state = 0; + p->imageIndex = 0; + p->timer = 300; + } + } + + //Update Mask + mask.x = p->x + ((40 - mask.w) / 2); + mask.y = p->y + (40 - mask.h); + + //Hero Collision + { + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon Collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + p->hp -= 1; + p->blink = 15; + + //Death + if (p->hp <= 0) { + createEffect(2, p->x - 12, p->y - 6); + spawnCollectable(p->x + 20, p->y); + enemyDestroy(p->id); + } + + i = MAX_WEAPONS; + } + } + } + } + } + +} + +void pumpkinenemyDraw(Pumpkinenemy* p) +{ + if (p->blink % 2 == 0) { + int cropX = (int)p->imageIndex * 40; + + if (p->dir == -1) { + cropX += 120; + } + + PHL_DrawSurfacePart(p->x, p->y, cropX, 560, 40, 40, images[imgEnemies]); + } +} + + +//Pumpkin bomb head +void createPumpkinhead(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Pumpkinhead* p = malloc(sizeof *p); + p->id = i; + + p->dir = dir; + + p->x = x; + p->y = y; + + p->vsp = -2; + + p->imageIndex = 0; + + p->state = 0; + p->timer = 0; + + e->data = p; + e->enemyStep = pumpkinheadStep; + e->enemyDraw = pumpkinheadDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void pumpkinheadStep(Pumpkinhead* p) +{ + char dead = 0; + + //Setup Mask + Mask mask; + { + mask.circle = mask.unused = 0; + mask.w = 20; + mask.h = 22; + mask.x = p->x + ((40 - mask.w) / 2); + mask.y = p->y + ((40 - mask.h) / 2); + } + + //Pumpkin head + if (p->state == 0) + { + char explode = 0; + + //Animate + { + p->imageIndex += 0.1; + if (p->imageIndex >= 2) { + p->imageIndex -= 2; + } + } + + //Movement + { + int hsp = 3; + p->x += hsp * p->dir; + mask.x = p->x + ((40 - mask.w) / 2); + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + p->x = collide.x + 20 - ((20 + (mask.w / 2)) * p->dir) - 20; + mask.x = p->x + ((40 - mask.w) / 2); + p->dir *= -1; + } + + double grav = 0.15; + p->y += p->vsp; + p->vsp += grav; + mask.y = p->y + ((40 - mask.h) / 2); + + collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + + if (collide.x != -1) { + p->y = collide.y - 40; + explode = 1; + } + } + + //Update Mask + mask.x = p->x + ((40 - mask.w) / 2); + mask.y = p->y + ((40 - mask.h) / 2); + + //Explode + { + if (explode == 1) { + PHL_PlaySound(sounds[sndBom03], CHN_ENEMIES); + p->state = 1; + p->imageIndex = 0; + p->timer = 0; + } + } + + //Outside of room + { + if (mask.y > 480 || mask.x > 640 || mask.x + mask.w < 0) { + dead = 1; + } + } + + } + + //Explosion + else if (p->state == 1) + { + //Update Mask + { + mask.w = 68; + mask.h = 66; + mask.x = p->x - 44 + 64 - (mask.w / 2); + mask.y = p->y - 44 + (84 - mask.h); + } + + //Animate + { + p->imageIndex += 0.33; + if (p->imageIndex >= 12) { + dead = 1; + } + } + + //Hero Collision + { + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(40, mask.x + (mask.w / 2)); + } + } + } + + //Destroy object + { + if (dead == 1) { + enemyDestroy(p->id); + } + } +} + +void pumpkinheadDraw(Pumpkinhead* p) +{ + if (p->state == 0) { + int cropX = (int)p->imageIndex * 40; + + if (p->dir == -1) { + cropX += 80; + } + + PHL_DrawSurfacePart(p->x, p->y, cropX, 240, 40, 40, images[imgEnemies]); + } + + if (p->state == 1) { + int cropX = (int)p->imageIndex * 128; + int cropY = 0; + + while (cropX >= 640) { + cropX -= 640; + cropY += 96; + } + + PHL_DrawSurfacePart(p->x - 44, p->y - 44, cropX, cropY, 128, 96, images[imgExplosion]); + } +} \ No newline at end of file diff --git a/src/enemies/pumpkin.h b/src/enemies/pumpkin.h new file mode 100644 index 0000000..6a2b87c --- /dev/null +++ b/src/enemies/pumpkin.h @@ -0,0 +1,27 @@ +#ifndef PUMPKIN_H +#define PUMPKIN_H + +typedef struct { + int id; + int hp; + int blink; + double x, y; + int dir; + double imageIndex; + int state, timer; +} Pumpkinenemy; + +void createPumpkinenemy(int x, int y); + +typedef struct { + int id; + int dir; + double x, y; + double vsp; + double imageIndex; + int state, timer; +} Pumpkinhead; + +void createPumpkinhead(int x, int y, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/seal.c b/src/enemies/seal.c new file mode 100644 index 0000000..f0a3fa9 --- /dev/null +++ b/src/enemies/seal.c @@ -0,0 +1,204 @@ +#include "seal.h" +#include "../game.h" +#include "../enemy.h" +#include "../collision.h" +#include "../hero.h" +#include + +void sealStep(Seal* s); +void sealDraw(Seal* s); + +void createSeal(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Seal* s = malloc(sizeof *s); + s->id = i; + s->hp = 2; + + s->x = x; + s->y = y; + + s->imageIndex = 0; + + s->dir = 1; + if (x + 20 > herox) { + s->dir = -1; + } + + s->state = 0; + s->timer = 0; + + s->invincible = 0; + + e->data = s; + e->enemyStep = sealStep; + e->enemyDraw = sealDraw; + e->type = 19; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void sealStep(Seal* s) +{ + if (s->invincible > 0) { + s->invincible -= 1; + } + + Mask mask; + mask.unused = mask.circle = 0; + mask.w = mask.h = 28; + mask.x = s->x + ((40 - mask.w) / 2); + mask.y = s->y + (40 - mask.h); + + //Walk + if (s->state == 0) + { + //Animate + s->imageIndex += 0.1; + if (s->imageIndex >= 2) { + s->imageIndex -= 2; + } + + //Check if hit a wall + if (checkTileCollision(1, mask) == 1) { + s->dir *= -1; + }else{ + //Check if on edge + mask.x += mask.w * s->dir; + mask.y += mask.h; + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + if (collide.x == -1) { + s->dir *= -1; + } + + mask.x = s->x + ((40 - mask.w) / 2); + mask.y = s->y + (40 - mask.h); + } + + //Movement + s->x += 0.5 * s->dir; + + if (s->timer <= 0) { + //Check if player is close enough + Mask area; + area.unused = area.circle = 0; + area.x = s->x - 40; + area.y = s->y; + area.w = 120; + area.h = 120; + + if (checkCollision(area, getHeroMask()) == 1) { + s->state = 1; + s->timer = -1; + } + }else{ + s->timer -= 1; + } + } + + //Rear back + else if (s->state == 1) + { + //Setup + if (s->timer == -1) { + s->imageIndex = 4; + s->timer = 20; + } + + s->timer -= 1; + + if (s->timer <= 0) { + s->state = 2; + s->timer = -1; + s->imageIndex = 0; + } + } + + //Tounge attack + else if (s->state == 2) + { + //Setup + if (s->timer == -1) { + s->timer = 0; + PHL_PlaySound(sounds[sndGet01], CHN_ENEMIES); + } + + //Animate + int animation[41] = {0, 1, 1, 2, 2, 3, 3, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, + 2, 2, 1, 1, 0, 0, 5, 5, 5, 5, 5 }; + + s->imageIndex = animation[(int)s->timer]; + + //Update mask height to fit tounge + int len[6] = { 18, 38, 58, 64, 66, 0}; + mask.h += len[(int)s->imageIndex]; + + s->timer += 1; + + if (s->timer >= 41) { + s->state = 0; + s->timer = 120; + s->imageIndex = 0; + } + } + + //Hit Player + if (checkCollision(mask, getHeroMask())) { + heroHit(10, s->x + 20); + } + + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + s->hp -= 1; + s->invincible = 15; + weaponHit(weapons[i]); + if (s->hp <= 0) { + enemyDestroy(s->id); + createEffect(2, s->x - 12, s->y - 6); + spawnCollectable(s->x + 20, s->y); + } + i = MAX_WEAPONS; + } + } + } + } +} + +void sealDraw(Seal* s) +{ + if (s->invincible % 2 == 0) { + int cx = 400 + ((int)s->imageIndex * 40); + + if (s->state == 0) { + if (s->dir == -1) { + cx += 80; + } + } + + if (s->state == 2) { + cx = 600; + } + + PHL_DrawSurfacePart(s->x, s->y, cx, 200, 40, 40, images[imgEnemies]); + + //Draw tounge + if (s->state == 2) { + PHL_DrawSurfacePart(s->x, s->y + 28, 200 + ((int)s->imageIndex * 40), 0, 40, 80, images[imgMisc2040]); + } + } +} \ No newline at end of file diff --git a/src/enemies/seal.h b/src/enemies/seal.h new file mode 100644 index 0000000..ea1dbe8 --- /dev/null +++ b/src/enemies/seal.h @@ -0,0 +1,14 @@ +#ifndef SEAL_H +#define SEAL_H + +typedef struct { + int id, hp; + double x, y; + double imageIndex; + int dir, state, timer; + int invincible; +} Seal; + +void createSeal(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/skeleton.c b/src/enemies/skeleton.c new file mode 100644 index 0000000..5d88605 --- /dev/null +++ b/src/enemies/skeleton.c @@ -0,0 +1,298 @@ +#include "skeleton.h" +#include "../enemy.h" +#include "../game.h" +#include "../PHL.h" +#include "../hero.h" +#include + +void skeletonStep(Skeleton* s); +void skeletonDraw(Skeleton* s); + +void boneStep(Bone* b); +void boneDraw(Bone* b); + +void createSkeleton(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Skeleton* s = malloc(sizeof *s); + s->id = i; + + s->x = x; + s->y = y; + + s->imageIndex = 0; + + s->dir = 1; + if (dir == 1) { + s->dir = -1; + } + + s->hsp = 0.5 * s->dir; + + s->hp = 2; + + s->state = 0; + s->timer = 0; + s->invincible = 0; + + s->mask.unused = s->mask.circle = 0; + s->mask.w = 24; + s->mask.h = 36; + s->mask.x = s->x + 8; + s->mask.y = s->y + 4; + + e->data = s; + e->enemyStep = skeletonStep; + e->enemyDraw = skeletonDraw; + e->type = 17; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void skeletonStep(Skeleton* s) +{ + if (s->invincible > 0) { + s->invincible -= 1; + } + + //Collide with wall + if (checkTileCollision(1, s->mask) == 1) { + s->hsp *= -1; + }else{ + //Check if on ledge + int tempdir = 1; + if (s->hsp < 0) { + tempdir = -1; + } + s->mask.x += tempdir * s->mask.w; + s->mask.y += 10; + if (checkTileCollision(1, s->mask) == 0 && checkTileCollision(3, s->mask) == 0) { + s->hsp *= -1; + } + s->mask.y -= 10; + s->mask.x = s->x + 8; + } + + s->x += s->hsp; + + if (s->timer >= 0) { + s->timer -= 1; + } + + //Walk around + if (s->state == 0) + { + s->imageIndex += 0.1; + + if (s->hsp < 0) { + s->dir = -1; + s->hsp = -0.5; + }else if (s->hsp > 0) { + s->dir = 1; + s->hsp = 0.5; + }else{ + s->hsp = 0.5 * s->dir; + } + + if (s->timer <= 0) { + //If hero is too close + Mask area; + area.unused = area.circle = 0; + area.x = s->x - 80; + area.y = s->y - 20; + area.w = 200; + area.h = 80; + + if (checkCollision(area, getHeroMask()) == 1) { + s->state = 1; + s->timer = 30; + s->hsp = 0; + + s->dir = 1; + if (herox < s->mask.x + (s->mask.w / 2)) { + s->dir = -1; + } + } + } + } + //Alert + else if (s->state == 1) + { + s->hsp = 0; + s->imageIndex += 0.1; + + if (s->timer <= 0) { + s->state = 2; + s->hsp = 2.5 * -s->dir; + PHL_PlaySound(sounds[sndPi05], CHN_ENEMIES); + } + } + //Slide backwards + else if (s->state == 2) + { + s->imageIndex = 0; + double fric = 0.075; + if (s->hsp > 0) { + s->hsp -= fric; + if (s->hsp <= 0) { s->hsp = 0; } + } + else if (s->hsp < 0) { + s->hsp += fric; + if (s->hsp >= 0) { s->hsp = 0; } + } + + if (s->hsp == 0) { + s->state = 3; + s->timer = 30; + createBone(s->x, s->y, s->dir); + PHL_PlaySound(sounds[sndShot05], CHN_ENEMIES); + } + } + //Throw bone + else if (s->state == 3) + { + s->imageIndex += 0.1; + if (s->timer <= 0) { + s->timer = 0; + s->state = 0; + } + } + + if (s->imageIndex >= 2) { + s->imageIndex -= 2; + } + + //Update mask + s->mask.x = s->x + 8; + + //Hit Player + if (checkCollision(s->mask, getHeroMask())) { + heroHit(10, s->x + 20); + } + + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(s->mask, weapons[i]->weaponMask)) { + s->hp -= 1; + s->invincible = 15; + weaponHit(weapons[i]); + if (s->hp <= 0) { + createRockSmash(s->x + 20, s->y + 20); + spawnCollectable(s->x + 20, s->mask.y + s->mask.h - 40); + enemyDestroy(s->id); + } + i = MAX_WEAPONS; + } + } + } + } +} + +void skeletonDraw(Skeleton* s) +{ + if (s->invincible % 2 == 0) { + int dx = 160 + ((int)s->imageIndex * 40); + + if (s->dir == -1) { + dx += 80; + } + + PHL_DrawSurfacePart(s->x, s->y, dx, 240, 40, 40, images[imgEnemies]); + } +} + + + +void createBone(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Bone* b = malloc(sizeof *b); + b->id = i; + + b->x = x; + b->y = y; + + b->hsp = dir * 0.75; + b->vsp = -4; + b->grav = 0.1; + + b->imageIndex = 0; + + b->mask.unused = 0; + b->mask.circle = 1; + b->mask.w = 12; + b->mask.h = 12; + b->mask.x = b->x + 20; + b->mask.y = b->y + 20; + + e->data = b; + e->enemyStep = boneStep; + e->enemyDraw = boneDraw; + e->type = -1; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void boneStep(Bone* b) +{ + if (b->hsp < 0) { + b->imageIndex += 0.25; + if (b->imageIndex >= 4) { + b->imageIndex -= 4; + } + } + else{ + b->imageIndex -= 0.25; + if (b->imageIndex < 0) { + b->imageIndex += 4; + } + } + + b->x += b->hsp; + + b->y += b->vsp; + b->vsp += b->grav; + + //Update Mask + b->mask.x = b->x + 20; + b->mask.y = b->y + 20; + + if (b->y > 480) { + enemyDestroy(b->id); + } + + //Hit Player + if (checkCollision(b->mask, shieldMask)) { + enemyDestroy(b->id); + PHL_PlaySound(sounds[sndHit07], CHN_EFFECTS); + createEffect(1, b->x, b->y); + }else{ + if (checkCollision(b->mask, getHeroMask())) { + heroHit(10, b->x + 20); + } + } +} + +void boneDraw(Bone* b) +{ + int img = 320 + ((int)b->imageIndex * 40); + if (b->hsp > 0) { + img += 160; + } + + PHL_DrawSurfacePart(b->x, b->y, img, 240, 40, 40, images[imgEnemies]); +} \ No newline at end of file diff --git a/src/enemies/skeleton.h b/src/enemies/skeleton.h new file mode 100644 index 0000000..a7b0816 --- /dev/null +++ b/src/enemies/skeleton.h @@ -0,0 +1,31 @@ +#ifndef SKELETON_H +#define SKELETON_H + +#include "../collision.h" + +typedef struct { + int id; + double x, y; + double hsp; + double imageIndex; + int dir; + int hp; + int state, timer, invincible; + + Mask mask; +} Skeleton; + +void createSkeleton(int x, int y, int dir); + +typedef struct { + int id; + double x, y; + double hsp, vsp, grav; + double imageIndex; + + Mask mask; +} Bone; + +void createBone(int x, int y, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/skull.c b/src/enemies/skull.c new file mode 100644 index 0000000..7ee5836 --- /dev/null +++ b/src/enemies/skull.c @@ -0,0 +1,174 @@ +#include "skull.h" +#include "../enemy.h" +#include "../PHL.h" +#include "../game.h" +#include "../hero.h" +#include +#include + +void skullStep(Skull* s); +void skullDraw(Skull* s); + +void createSkull(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Skull* s = malloc(sizeof *s); + s->id = i; + + //X/Y in center of sprite + s->x = x + 20; + s->y = y + 20; + s->yoffset = 0; + + s->rot = 0; + s->state = 0; + s->timer = 0; + s->imageIndex = 0; + s->dir = 0; + + e->data = s; + e->enemyStep = skullStep; + e->enemyDraw = skullDraw; + e->type = 12; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void skullStep(Skull* s) +{ + double imageSpeed = 0; + + //Wait + if (s->state == 0) + { + imageSpeed = 0.2; + + if (s->timer > 0) { + s->timer -= 1; + }else{ + Mask tempmask; + + tempmask.unused = tempmask.circle = 0; + tempmask.x = s->x - 100; + tempmask.y = s->y - 100; + tempmask.w = tempmask.h = 200; + + if (checkCollisionXY(tempmask, herox, heroy + 20)) { + + //Calculate distance + //int dis = sqrt(pow(s->x - herox, 2) + pow(s->y - (heroy + 20), 2)); + //if (dis <= 100) { + s->state = 1; + //s->dir = (rand() % 8) * 45; + s->dir = (rand() % 360) + 1; + PHL_PlaySound(sounds[sndPi08], CHN_ENEMIES); + s->timer = 130; + } + } + } + + //Chase + else if (s->state == 1) + { + imageSpeed = 0.3; + + int spd = 2; + s->x += (spd * cos(s->dir * 3.14159 / 180)); + s->y += (spd * sin(s->dir * 3.14159 / 180)); + + double herodir = ((atan2((heroy + 20) - s->y, herox - s->x) * 180) / 3.14159); + if (herodir >= 360) { + herodir -= 360; + } + if (herodir < 0) { + herodir += 360; + } + + double tempdir = s->dir - herodir; + if (tempdir < 0) { + tempdir += 360; + } + + if (tempdir < 180) { + s->dir -= 2; + }else{ + s->dir += 2; + } + if (s->dir >= 360) { + s->dir -= 360; + } + if (s->dir < 0) { + s->dir += 360; + } + + s->timer -= 1; + if (s->timer <= 0) { + s->state = 0; + s->timer = 10; + } + } + + //Animate + { + s->imageIndex += imageSpeed; + if (s->imageIndex >= 4) { + s->imageIndex -= 4; + } + } + + //Hover offset + { + s->rot += 5; + if (s->rot >= 360) { + s->rot -= 360; + } + s->yoffset = (5 * sin(s->rot * 3.14159 / 180)); + } + + //Setup Mask + Mask mask; + { + mask.unused = 0; + mask.circle = 1; + mask.x = s->x; + mask.y = s->y; + mask.w = mask.h = 10; + } + + //Hero collision + { + if (checkCollision(mask, getHeroMask())) { + heroHit(15, mask.x); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + createEffect(2, s->x - 32, s->y - 32); + spawnCollectable(s->x, s->y - 20); + enemyDestroy(s->id); + + i = MAX_WEAPONS; + } + } + } + } + +} + +void skullDraw(Skull* s) +{ + PHL_DrawSurfacePart(s->x - 20, s->y + s->yoffset - 20, 480 + ((int)s->imageIndex * 40), 40, 40, 40, images[imgEnemies]); +} \ No newline at end of file diff --git a/src/enemies/skull.h b/src/enemies/skull.h new file mode 100644 index 0000000..b62ea2d --- /dev/null +++ b/src/enemies/skull.h @@ -0,0 +1,15 @@ +#ifndef SKULL_H +#define SKULL_H + +typedef struct { + int id, state, timer; + double x, y; + double yoffset; + int rot; + double dir; + double imageIndex; +} Skull; + +void createSkull(int x, int y); + +#endif \ No newline at end of file diff --git a/src/enemies/slime.c b/src/enemies/slime.c new file mode 100644 index 0000000..297b190 --- /dev/null +++ b/src/enemies/slime.c @@ -0,0 +1,228 @@ +#include "slime.h" +#include +#include "../PHL.h" +#include "../game.h" +#include "../collision.h" +#include "../hero.h" +#include "../enemy.h" +#include "../weapon.h" + +void slimeStep(Slime* s); +void slimeDraw(Slime* s); + +void createSlime(int x, int y, int type, int offset) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* result = malloc(sizeof *result); + Slime* s = malloc(sizeof *s); + + s->id = i; + + s->x = x + 20; + s->y = y; + s->type = type; + s->offset = offset; + + s->hp = 1; + s->state = 0; + s->counter = 0; + s->timer = 0; + s->grav = 0.125; + s->vsp = 0; + s->hsp = 0; + s->imageIndex = 0; + + result->data = s; + result->enemyStep = slimeStep; + result->enemyDraw = slimeDraw; + result->type = 0; + + enemies[i] = result; + i = MAX_ENEMIES; + } + } +} + +void slimeStep(Slime* s) +{ + char dead = 0; + + //Stay within room + { + if (s->x > 640) { + s->x = 640; + } + + if (s->x < 0) { + s->x = 0; + } + } + + //Setup Rectangle Mask + Mask mask; + { + mask.unused = 0; + mask.circle = 0; + mask.w = 24; + mask.h = 24; + mask.x = s->x - (mask.w / 2); + mask.y = s->y + 28 - (mask.h / 2); + } + + //Idle + if (s->state == 0) + { + s->imageIndex += 0.25; + + if (s->imageIndex >= 6) { + s->imageIndex = 0; + + if (s->offset <= 0) { + s->state = 1; + + //Red/Yellow Slime + if (s->type == 1 || s->type == 2) + { + s->hsp = 1; + if (s->type == 2) { + s->hsp = 1.5; + } + if ((int)(rand() % 2) == 0) { + s->hsp *= -1; + } + } + + if (s->counter < 2) { + s->vsp = -2; + s->counter += 1; + }else{ + s->vsp = -4; + s->counter = 0; + } + }else{ + s->offset -= 1; + } + } + } + + //Jump + else if (s->state == 1) + { + //Red/Yellow Slime + if (s->type == 1 || s->type == 2) + { + s->x += s->hsp; + mask.x = s->x - (mask.w / 2); + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + if (s->hsp > 0) { + s->x = collide.x - (mask.w / 2); + }else if (s->hsp < 0) { + s->x = collide.x + 40 + (mask.w / 2); + } + } + + mask.x = s->x - (mask.w / 2); + } + + s->y += s->vsp; + s->vsp += s->grav; + + mask.y = s->y + 28 - (mask.h / 2); + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + if (collide.x != -1) { + if (s->vsp >= 0) { + s->state = 0; + s->hsp = 0; + s->y = collide.y - 40; + }else{ + s->y = collide.y + 40 - (40 - mask.h) + 1; + } + } + } + + //Setup Collision Mask + { + mask.unused = 0; + mask.circle = 1; + mask.w = 12; + mask.x = s->x; + mask.y = s->y + 28; + } + + //Fell in a pit + { + if (s->y > 480) { + dead = 1; + } + } + + //Collide with hero + { + if (checkCollision(mask, heroMask)) { + int dmg[3] = {10, 20, 20}; + + if (heroHit(dmg[s->type], s->x) == 1 && s->type == 2) { + heroStun(); + } + } + } + + //Sword collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + spawnCollectable(s->x, s->y + 6); + createEffect(2, s->x - 32, s->y - 12); + dead = 1; + + i = MAX_WEAPONS; + } + } + } + } + + //Destroy object + { + if (dead == 1) { + enemyDestroy(s->id); + } + } +} + +void slimeDraw(Slime* s) +{ + int cropX = 0, + cropY = 0; + + //Idle + if (s->state == 0) { + int image[6] = { 0, 1, 2, 3, 4, 6 }; + cropX = image[(int)s->imageIndex] * 40; + } + + //Jump + else if (s->state == 1) { + cropX = 200; + if (s->vsp >= 0) { + cropX += 40; + } + } + + //Color offsets + int addX[3] = {0, 280, 0}; + int addY[3] = {0, 0, 480}; + + PHL_DrawSurfacePart(s->x - 20, s->y + 12, cropX + addX[s->type], cropY + addY[s->type], 40, 40, images[imgEnemies]); +} \ No newline at end of file diff --git a/src/enemies/slime.h b/src/enemies/slime.h new file mode 100644 index 0000000..bef215b --- /dev/null +++ b/src/enemies/slime.h @@ -0,0 +1,17 @@ +#ifndef SLIME_H +#define SLIME_H + +typedef struct { + int id; + double x, y; + int type; //0 = blue | 1 = red | 2 = yellow + int offset; + double vsp, hsp, grav; + double imageIndex; + int counter, timer, state; + int hp; +} Slime; + +void createSlime(int x, int y, int type, int offset); + +#endif \ No newline at end of file diff --git a/src/enemies/slug.c b/src/enemies/slug.c new file mode 100644 index 0000000..c8fcc2c --- /dev/null +++ b/src/enemies/slug.c @@ -0,0 +1,164 @@ +#include "slug.h" +#include "../enemy.h" +#include "../game.h" +#include "../PHL.h" +#include "../hero.h" +#include + +void slugStep(Slug* s); +void slugDraw(Slug* s); + +void createSlug(int x, int y, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Slug* s = malloc(sizeof *s); + s->id = i; + + s->x = x; + s->y = y; + + s->imageIndex = 0; + s->vsp = 0; + + s->dir = 1; + if (dir == 1) { + s->dir = -1; + } + + e->data = s; + e->enemyStep = slugStep; + e->enemyDraw = slugDraw; + e->type = 2; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void slugStep(Slug* s) +{ + //Create Mask + Mask mask; + { + mask.circle = 0; + mask.unused = 0; + mask.w = 32; + mask.h = 24; + mask.x = s->x + ((40 - mask.w) / 2); + mask.y = s->y + (40 - mask.h); + } + + //Animate + { + s->imageIndex += 0.1; + if (s->imageIndex >= 4) { + s->imageIndex -= 4; + } + } + + //Check if on ground + int onground = 1; + { + mask.y += 1; + if (checkTileCollision(1, mask) == 0 && checkTileCollision(3, mask) == 0) { + onground = 0; + } + mask.y -= 1; + } + + if (onground == 0) { + double grav = 0.2; + + //Fall + { + s->y += s->vsp; + s->vsp += grav; + } + + //Land on ground + { + mask.y = mask.y + (40 - mask.h); + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + if (collide.x != -1) { + s->y = collide.y - 40; + s->vsp = 0; + } + } + }else{ + //Check if on ledge + { + mask.x += mask.w * s->dir; + mask.y += 1; + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + if (collide.x == -1) { + s->dir *= -1; + } + } + } + + //Horizontal movement + double hsp = 0.5; + { + s->x += s->dir * hsp; + } + + //Check if hit a wall + { + mask.x = s->x + ((40 - mask.w) / 2); + mask.y = s->y + (40 - mask.h); + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + s->dir *= -1; + } + } + + //Hit Player + { + if (checkCollision(mask, getHeroMask()) == 1) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(mask, weapons[i]->weaponMask) == 1) { + weaponHit(weapons[i]); + + createEffect(2, s->x - 12, s->y - 6); + spawnCollectable(s->x + 20, s->y); + enemyDestroy(s->id); + + i = MAX_WEAPONS; + } + } + } + } + +} + +void slugDraw(Slug* s) +{ + int anim[4] = { 1, 0, 2, 0 }; + + int cropx = anim[(int)s->imageIndex] * 40; + if (s->dir == -1) { + cropx += 120; + } + + PHL_DrawSurfacePart(s->x, s->y + 10, cropx, 40, 40, 40, images[imgEnemies]); +} \ No newline at end of file diff --git a/src/enemies/slug.h b/src/enemies/slug.h new file mode 100644 index 0000000..77c2e14 --- /dev/null +++ b/src/enemies/slug.h @@ -0,0 +1,13 @@ +#ifndef SLUG_H +#define SLUG_H + +typedef struct { + int id; + int dir; + double x, y, vsp; + double imageIndex; +} Slug; + +void createSlug(int x, int y, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/thwomp.c b/src/enemies/thwomp.c new file mode 100644 index 0000000..9c58f6b --- /dev/null +++ b/src/enemies/thwomp.c @@ -0,0 +1,265 @@ +#include "thwomp.h" +#include "../enemy.h" +#include "../hero.h" +#include "../game.h" +#include "../PHL.h" +#include "../effect.h" +#include + +void thwompStep(Thwomp* t); +void thwompDraw(Thwomp* t); + +void createThwomp(int x, int y, int type, int offset, int delay, int dir) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Thwomp* t = malloc(sizeof *t); + + t->id = i; + + t->x = x; + t->y = y; + + t->vsp = 0; + t->grav = 0.3; + + t->imageIndex = 0; + + t->type = type; + t->state = 0; + t->timer = offset * 30; + t->delay = delay * 30; + //default delay is 60 + if (delay == 0) { + t->delay = 60; + } + + t->dir = dir; + + t->hp = 3; + t->blink = 0; + + e->data = t; + e->enemyStep = thwompStep; + e->enemyDraw = thwompDraw; + e->type = 16; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void thwompStep(Thwomp* t) +{ + //Animate + { + t->imageIndex += 0.1; + if (t->imageIndex >= 3) { + t->imageIndex -= 3; + } + } + + //Counters + { + if (t->blink > 0) { + t->blink -= 1; + } + } + + //Setup Mask + Mask mask; + { + mask.unused = mask.circle = 0; + mask.w = mask.h = 36; + mask.x = t->x + ((40 - mask.w) / 2); + mask.y = t->y + ((40 - mask.h) / 2); + } + + //Wait + if (t->state == 0) { + t->vsp = 0; + + //Waiter + if (t->type == 0) { + Mask area; + area.unused = area.circle = 0; + area.x = t->x - 40; + area.y = t->y; + area.w = 120; + area.h = 160; + + if (checkCollisionXY(area, herox, heroy)) { + t->state = 1; + } + } + + //Automatic + else{ + t->timer -= 1; + if (t->timer <= 0) { + t->state = 1; + } + } + } + + //Fall + else if (t->state == 1) { + //Down + if (t->dir == 0) { + t->y += t->vsp; + } + //Left + if (t->dir == 1) { + t->x -= t->vsp; + } + //Right + if (t->dir == 2) { + t->x += t->vsp; + } + + t->vsp += t->grav; + + if (t->vsp >= 7) { + t->vsp = 7; + } + + //Update Mask + mask.x = t->x + ((40 - mask.w) / 2); + mask.y = t->y + ((40 - mask.h) / 2); + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + int effX = t->x, + effY = t->y; + //Down + if (t->dir == 0) { + t->y = collide.y - 40; + effY = t->y + 20; + } + //Left + if (t->dir == 1) { + t->x = collide.x + 40; + effX = t->x - 20; + } + //Right + if (t->dir == 2) { + t->x = collide.x - 40; + effX = t->x + 20; + } + + t->state = 2; + t->timer = 60; + createEffect(1, effX, effY); + PHL_PlaySound(sounds[sndHit07], CHN_ENEMIES); + } + } + else if (t->state == 2) { + if (t->type == 1) { //Automatic + t->timer -= 1; + if (t->timer <= 0) { + t->state = 3; + } + } + } + else if (t->state == 3) { //rise up + //Down + if (t->dir == 0) { + t->y -= 2; + } + //Left + if (t->dir == 1) { + t->x += 2; + } + //Right + if (t->dir == 2) { + t->x -= 2; + } + + //Update Mask + mask.x = t->x + ((40 - mask.w) / 2); + mask.y = t->y + ((40 - mask.h) / 2); + + if (t->dir == 0) { + mask.y -= 4; + } + if (t->dir == 1) { + mask.x += 4; + } + if (t->dir == 2) { + mask.x -= 4; + } + + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x != -1) { + //Down + if (t->dir == 0) { + t->y = collide.y + 40 + 2; + } + //Left + if (t->dir == 1) { + t->x = collide.x - 40 + 2; + } + //Right + if (t->dir == 2) { + t->x = collide.x + 40 + 2; + } + + t->state = 0; + t->timer = t->delay; + } + } + + //Update Mask + { + mask.x = t->x + ((40 - mask.w) / 2); + mask.y = t->y + ((40 - mask.h) / 2); + } + + //Hit Player + { + if (checkCollision(mask, getHeroMask())) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + if (hasItem[16] == 1) { //Has blue paper + t->hp -= 1; + t->blink = 15; + + if (t->hp <= 0) { + createRockSmash(t->x + 20, t->y + 20); + spawnCollectable(t->x + 20, t->y); + enemyDestroy(t->id); + } + }else{ + //Tink + PHL_PlaySound(sounds[sndHit03], CHN_WEAPONS); + } + + i = MAX_WEAPONS; + } + } + } + } + } + +} + +void thwompDraw(Thwomp* t) +{ + if (t->blink % 2 == 0) { + PHL_DrawSurfacePart(t->x, t->y, 240 + ((int)t->imageIndex * 40), 400, 40, 40, images[imgMisc20]); + } +} \ No newline at end of file diff --git a/src/enemies/thwomp.h b/src/enemies/thwomp.h new file mode 100644 index 0000000..87edebc --- /dev/null +++ b/src/enemies/thwomp.h @@ -0,0 +1,18 @@ +#ifndef THWOMP_H +#define THWOMP_H + +//#include "../collision.h" + +typedef struct { + int id; + double x, y; + double vsp, grav; + double imageIndex; + int type, state, timer, dir; + int hp, blink; + int delay; +} Thwomp; + +void createThwomp(int x, int y, int type, int offset, int delay, int dir); + +#endif \ No newline at end of file diff --git a/src/enemies/waterjumper.c b/src/enemies/waterjumper.c new file mode 100644 index 0000000..d01d206 --- /dev/null +++ b/src/enemies/waterjumper.c @@ -0,0 +1,356 @@ +#include "waterjumper.h" +#include +#include +#include "../game.h" +#include "../enemy.h" +#include "../hero.h" +#include "../collision.h" + +void waterJumperStep(WaterJumper* w); +void waterJumperDraw(WaterJumper* w); + +void createWaterJumper(int x, int y, int type, int offset, int height) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + WaterJumper* w = malloc(sizeof *w); + w->id = i; + w->type = type; + + w->x = x; + w->y = w->ystart = y; + + w->hp = 1; + + w->hsp = w->vsp = 0; + w->grav = 0.115; + w->yoffset = 0; + + w->imageIndex = 0; + w->blink = 0; + + w->timer = 60; + w->timer += 60 * offset; + + w->rot = 0; + w->state = 0; + + if (type == 1) { + w->hp = 2; + w->timer = 60 * offset; + } + + //Specific offset timer + if (offset > 10) { + w->timer = offset; + } + + w->height = height; + /* + w->mask.unused = w->mask.circle = 0; + w->mask.x = x + 8; + w->mask.y = y + 8; + w->mask.w = 24; + w->mask.h = 24; + */ + e->data = w; + e->enemyStep = waterJumperStep; + e->enemyDraw = waterJumperDraw; + e->type = 14; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void waterJumperStep(WaterJumper* w) +{ + //Counters + { + if (w->blink > 0) { + w->blink -= 1; + } + + if (w->timer > 0) { + w->timer -= 1; + } + } + + //Setup Mask + Mask mask; + { + mask.unused = mask.circle = 0; + mask.w = 24; + mask.h = 24; + mask.x = w->x + ((40 - mask.w) / 2); + mask.y = w->y + ((40 - mask.h) / 2); + } + + //Float + if (w->state == 0) + { + //Animate + { + w->imageIndex += 0.1; + if (w->imageIndex >= 2) { + w->imageIndex -= 2; + } + } + + //Movement + { + w->rot += 5; + if (w->rot >= 360) { + w->rot -= 360; + } + + w->y = w->ystart + (5 * sin(w->rot * 3.14159 / 180)); + } + + //Hop out of water + { + if (w->timer <= 0) { + w->state = 1; + w->timer = -1; + + createSplash(w->x + 20, w->y); + w->y = w->ystart; + } + } + } + //In air + else if (w->state == 1) + { + //Animate + { + w->imageIndex += 0.25; + if (w->imageIndex >= 3) { + w->imageIndex -= 3; + } + } + + //Green type + if (w->type == 0) + { + //State Start + { + if (w->timer == -1) { + w->timer = 0; + + w->vsp = -5.5; + w->hsp = -2; + if (w->x + 20 < herox) { + w->hsp *= -1; + } + } + } + + //Horizontal Movement + w->x += w->hsp; + + //Land in water + { + if (w->vsp > 0 && w->y >= w->ystart) { + createSplash(w->x + 20, w->y); + w->y = w->ystart; + w->state = 0; + w->hsp = w->vsp = 0; + w->timer = 120; + } + } + } + + //Blue type + else + { + //State Start + { + if (w->timer == -1) { + w->timer = 0; + + if (w->height == 2) { + w->vsp = -5.5; + } + else if (w->height == 3) { + w->vsp = -6; + } + else if (w->height == 4) { + w->vsp = -7; + } + else if (w->height == 5) { + w->vsp = -7.5; + } + } + } + + //Land on expected ground + { + if (w->vsp > 0) { + if (w->y >= w->ystart - 22 - (w->height * 40)) { + w->y = w->ystart - 22 - (w->height * 40); + w->imageIndex = 5; + w->state = 2; + w->timer = 240; + w->hsp = 2; + if (herox < w->x + 20) { + w->hsp *= -1; + } + + } + } + } + + } + + //Vertical Movement + w->y += w->vsp; + w->vsp += w->grav; + + } + + //Walk + else if (w->state == 2) { + //Animate + { + w->imageIndex += 0.16; + if (w->imageIndex >= 7) { + w->imageIndex -= 2; + } + } + + //Movement + { + w->x += w->hsp; + mask.x = w->x + ((40 - mask.w) / 2); + } + + //Hit wall + if (checkTileCollision(1, mask) == 1) { + w->hsp *= -1; + } + + //Turn on edge + else{ + mask.x += (mask.w / 2) * w->hsp; + mask.y += mask.h; + PHL_Rect collide = getTileCollision(1, mask); + if (collide.x == -1) { + collide = getTileCollision(3, mask); + } + if (collide.x == -1) { + w->hsp *= -1; + } + } + + //End walk + { + if (w->timer <= 0) { + w->state = 3; + w->timer = -1; + } + } + } + + //Jump Down + else if (w->state == 3) + { + //Setup + { + if (w->timer == -1) { + w->timer = 0; + PHL_PlaySound(sounds[sndPi02], CHN_ENEMIES); + w->vsp = -4; + w->imageIndex = 2; + } + } + + //Animate + { + w->imageIndex += 0.25; + if (w->imageIndex >= 5) { + w->imageIndex -= 3; + } + } + + //Movement + { + w->y += w->vsp; + w->vsp += w->grav; + + if (w->vsp > 6) { + w->vsp = 6; + } + } + + //Land in Water + { + if (w->y >= w->ystart) { + w->state = 0; + createSplash(w->x + 20, w->y); + w->timer = 60; + } + } + } + + //Update Mask + { + mask.x = w->x + ((40 - mask.w) / 2); + mask.y = w->y + ((40 - mask.h) / 2); + } + + //Collide with hero + { + if (checkCollision(mask, getHeroMask())) { + heroHit(15, mask.x + (mask.w / 2)); + } + } + + //Weapon collision + { + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (weapons[i]->cooldown == 0) { + if (checkCollision(mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + + w->blink = 15; + w->hp -= 1; + if (w->hp <= 0) { + createEffect(2, w->x - 12, w->y - 12); + spawnCollectable(w->x + 20, w->y); + enemyDestroy(w->id); + } + + i = MAX_WEAPONS; + } + } + } + } + } + +} + +void waterJumperDraw(WaterJumper* w) +{ + if (w->blink % 2 == 0) { + int cx = (int)w->imageIndex * 40; + + if (w->state == 1) { + cx += 80; + } + + if (w->type == 1) { + cx += 200; + + if (w->state == 2 && w->hsp < 0) { + cx += 80; + } + } + + PHL_DrawSurfacePart(w->x, w->y, cx, 440, 40, 40, images[imgEnemies]); + } +} \ No newline at end of file diff --git a/src/enemies/waterjumper.h b/src/enemies/waterjumper.h new file mode 100644 index 0000000..77823d8 --- /dev/null +++ b/src/enemies/waterjumper.h @@ -0,0 +1,23 @@ +#ifndef WATERJUMPER_H +#define WATERJUMPER_H + +#include "../enemy.h" + +typedef struct { + int id, type; + + double x, y; + int hp; + int blink; + int ystart, rot; + double yoffset; + double hsp, vsp, grav; + + double imageIndex; + int state, timer; + int height; +} WaterJumper; + +void createWaterJumper(int x, int y, int type, int offset, int height); + +#endif \ No newline at end of file diff --git a/src/enemies/wizard.c b/src/enemies/wizard.c new file mode 100644 index 0000000..b496a45 --- /dev/null +++ b/src/enemies/wizard.c @@ -0,0 +1,133 @@ +#include "wizard.h" +#include "../enemy.h" +#include "../game.h" +#include "../hero.h" +#include + +void createWizard(int x, int y) +{ + int i; + for (i = 0; i < MAX_ENEMIES; i++) { + if (enemies[i] == NULL) { + Enemy* e = malloc(sizeof *e); + Wizard* w = malloc(sizeof *w); + w->id = i; + + w->x = x; + w->y = y; + + w->imageIndex = 0; + + w->state = 0; + w->timer = 50; + w->visible = 1; + + w->mask.circle = w->mask.unused = 0; + w->mask.w = 24; + w->mask.h = 38; + w->mask.x = w->x + 8; + w->mask.y = w->y + 2; + + e->data = w; + e->enemyStep = wizardStep; + e->enemyDraw = wizardDraw; + e->type = 21; + + enemies[i] = e; + i = MAX_ENEMIES; + } + } +} + +void wizardStep(Wizard* w) +{ + w->imageIndex += 0.3; + if (w->imageIndex >= 3) { + w->imageIndex -= 3; + } + + //Stand still + if (w->state == 0) { + w->timer -= 1; + + if (w->timer <= 0) { + PHL_PlaySound(sounds[sndPi10], CHN_ENEMIES); + w->state = 1; + w->timer = 15; + } + } + //Flash + else if (w->state == 1 || w->state == 3) { + if (w->visible == 0) { + w->visible = 1; + }else{ + w->visible = 0; + } + + w->timer -= 1; + if (w->timer <= 0) { + if (w->state == 1) { + w->state = 2; + w->timer = 60; + } + else if (w->state == 3) { + w->visible = 1; + w->state = 0; + w->timer = 50; + } + } + } + //Invisible + else if (w->state == 2) { + w->visible = 0; + + w->timer -= 1; + if (w->timer <= 0) { + PHL_PlaySound(sounds[sndPi03], CHN_ENEMIES); + w->state = 3; + w->timer = 15; + + //Horizontal Jump + int gridX = w->x / 40, + gridY = w->y / 40, + lastGridX = gridX; + + do { + gridX = (rand() % 16) + 1; + } while (collisionTiles[gridX][gridY] != 0 || + collisionTiles[gridX][gridY+1] != 1 || + gridX == lastGridX); + + w->x = gridX * 40; + w->mask.x = w->x + 8; + } + } + + if (w->state == 0 || w->state == 3) { + //Hit Player + if (checkCollision(w->mask, getHeroMask())) { + heroHit(15, w->x + 20); + } + + //Weapon Collision + int i; + for (i = 0; i < MAX_WEAPONS; i++) { + if (weapons[i] != NULL) { + if (checkCollision(w->mask, weapons[i]->weaponMask)) { + weaponHit(weapons[i]); + createEffect(2, w->x - 12, w->y - 6); + spawnCollectable(w->x + 20, w->y); + enemyDestroy(w->id); + i = MAX_WEAPONS; + } + } + } + } +} + +void wizardDraw(Wizard* w) +{ + if (w->visible == 1) { + PHL_DrawSurfacePart(w->x, w->y, 520 + (((int)w->imageIndex) * 40), 480, 40, 40, images[imgEnemies]); + } +} \ No newline at end of file diff --git a/src/enemies/wizard.h b/src/enemies/wizard.h new file mode 100644 index 0000000..00fed6d --- /dev/null +++ b/src/enemies/wizard.h @@ -0,0 +1,20 @@ +#ifndef WIZARD_H +#define WIZARD_H + +#include "../collision.h" + +typedef struct { + int id; + double x, y; + double imageIndex; + int state, timer, visible; + + Mask mask; +} Wizard; + +void createWizard(int x, int y); + +void wizardStep(Wizard* w); +void wizardDraw(Wizard* w); + +#endif \ No newline at end of file -- cgit v1.2.3