aboutsummaryrefslogtreecommitdiff
path: root/src/enemies
diff options
context:
space:
mode:
authorptitSeb2017-11-30 22:49:38 +0100
committerptitSeb2017-11-30 22:49:38 +0100
commitde29b11a88dbdd3af0824e59b51528b91ee73c54 (patch)
treee1aabf8752043998663279fae4359a18c4b4af07 /src/enemies
parentd87f450f51372ddf013e6bac09f1ef588e6f8bea (diff)
downloadhydracastlelabyrinth-de29b11a88dbdd3af0824e59b51528b91ee73c54.tar.gz
hydracastlelabyrinth-de29b11a88dbdd3af0824e59b51528b91ee73c54.tar.bz2
hydracastlelabyrinth-de29b11a88dbdd3af0824e59b51528b91ee73c54.zip
First commit. Version works on Linux (keyboard only, not configurable)
Diffstat (limited to 'src/enemies')
-rw-r--r--src/enemies/bat.c172
-rw-r--r--src/enemies/bat.h16
-rw-r--r--src/enemies/batboss.c319
-rw-r--r--src/enemies/batboss.h18
-rw-r--r--src/enemies/bee.c214
-rw-r--r--src/enemies/bee.h16
-rw-r--r--src/enemies/boar.c221
-rw-r--r--src/enemies/boar.h18
-rw-r--r--src/enemies/boomknight.c285
-rw-r--r--src/enemies/boomknight.h27
-rw-r--r--src/enemies/crab.c509
-rw-r--r--src/enemies/crab.h30
-rw-r--r--src/enemies/devil.c458
-rw-r--r--src/enemies/devil.h28
-rw-r--r--src/enemies/dodo.c587
-rw-r--r--src/enemies/dodo.h22
-rw-r--r--src/enemies/dog.c294
-rw-r--r--src/enemies/dog.h17
-rw-r--r--src/enemies/firewheel.c272
-rw-r--r--src/enemies/firewheel.h18
-rw-r--r--src/enemies/fish.c139
-rw-r--r--src/enemies/fish.h24
-rw-r--r--src/enemies/garm.c578
-rw-r--r--src/enemies/garm.h31
-rw-r--r--src/enemies/gas.c116
-rw-r--r--src/enemies/gas.h18
-rw-r--r--src/enemies/ghoul.c218
-rw-r--r--src/enemies/ghoul.h22
-rw-r--r--src/enemies/golem.c199
-rw-r--r--src/enemies/golem.h16
-rw-r--r--src/enemies/gyra.c332
-rw-r--r--src/enemies/gyra.h19
-rw-r--r--src/enemies/heads.c838
-rw-r--r--src/enemies/heads.h89
-rw-r--r--src/enemies/hydra.c1127
-rw-r--r--src/enemies/hydra.h65
-rw-r--r--src/enemies/jellyfish.c195
-rw-r--r--src/enemies/jellyfish.h16
-rw-r--r--src/enemies/knight.c232
-rw-r--r--src/enemies/knight.h23
-rw-r--r--src/enemies/lolidra.c367
-rw-r--r--src/enemies/lolidra.h40
-rw-r--r--src/enemies/pendulum.c78
-rw-r--r--src/enemies/pendulum.h19
-rw-r--r--src/enemies/podoboo.c176
-rw-r--r--src/enemies/podoboo.h20
-rw-r--r--src/enemies/poisonknight.c318
-rw-r--r--src/enemies/poisonknight.h26
-rw-r--r--src/enemies/pumpkin.c375
-rw-r--r--src/enemies/pumpkin.h27
-rw-r--r--src/enemies/seal.c204
-rw-r--r--src/enemies/seal.h14
-rw-r--r--src/enemies/skeleton.c298
-rw-r--r--src/enemies/skeleton.h31
-rw-r--r--src/enemies/skull.c174
-rw-r--r--src/enemies/skull.h15
-rw-r--r--src/enemies/slime.c228
-rw-r--r--src/enemies/slime.h17
-rw-r--r--src/enemies/slug.c164
-rw-r--r--src/enemies/slug.h13
-rw-r--r--src/enemies/thwomp.c265
-rw-r--r--src/enemies/thwomp.h18
-rw-r--r--src/enemies/waterjumper.c356
-rw-r--r--src/enemies/waterjumper.h23
-rw-r--r--src/enemies/wizard.c133
-rw-r--r--src/enemies/wizard.h20
66 files changed, 11257 insertions, 0 deletions
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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <math.h>
+#include <stdlib.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <stdlib.h>
+#include <math.h>
+
+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 <stdlib.h>
+#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 <stdlib.h>
+
+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 <stdlib.h>
+
+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 <math.h>
+#include <stdlib.h>
+#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 <stdlib.h>
+
+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