diff --git a/changelog.txt b/changelog.txt index de0510bd3..a47b00953 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ Commander Genius Pre-Release v0.3 (CloneKeenPlus): --------------------------------------------------- +-------------------------------------------------- +05-08-2009 +- fixed crash when game in EP1 or 2 was ending +- fixed fonts problems in story section. + 04-08-2009 - Fixed some minor issues with mods [Sprite Replacement] (Tulip) - Added Ordering Info to Episode 1. Only works there! diff --git a/data/resolutions.cfg b/data/resolutions.cfg index 2f956d1a9..8b88ca8c2 100644 --- a/data/resolutions.cfg +++ b/data/resolutions.cfg @@ -4,6 +4,6 @@ 800x600x32 1024x768x32 1280x1024x32 -1600x1050x32 1600x1200x32 +1680x1050x32 1920x1200x32 diff --git a/src/ai/enemydata.h b/src/ai/enemydata.h new file mode 100644 index 000000000..6bb634618 --- /dev/null +++ b/src/ai/enemydata.h @@ -0,0 +1,340 @@ +/* + * enemydata.h + * + * Created on: 05.08.2009 + * Author: gerstrong + */ + +#ifndef ENEMYDATA_H_ +#define ENEMYDATA_H_ + +// Structs used for different enemy's variables. These are in a union. + +typedef struct stYorpData +{ + unsigned char state; + + unsigned char looktimes,lookposition; + unsigned char timer, dietimer; + unsigned char walkframe; + unsigned int dist_traveled; + signed int yorpdie_inertia_y; + + unsigned char movedir; +} stYorpData; + + +typedef struct stGargData +{ + unsigned char state; + + unsigned char looktimes,lookframe; + unsigned char timer, dietimer, keenonsameleveltimer; + unsigned char about_to_charge; + unsigned char walkframe; + unsigned int dist_traveled; + signed int gargdie_inertia_y; + + unsigned char movedir; + unsigned char detectedPlayer, detectedPlayerIndex; +} stGargData; + + +typedef struct stVortData +{ + unsigned char state; + + unsigned char timer,timer2; + unsigned int animtimer; + unsigned char palflashtimer, palflashamt; + unsigned char frame; + unsigned int dist_traveled; + signed int inertiay; + + char ep1style; // episode 1 style four-shots-to-kill + + unsigned char movedir; + // these hold the animation frames indexes since they're + // different for each episode + int WalkLeftFrame; + int WalkRightFrame; + int LookFrame; + int JumpRightFrame; + int JumpLeftFrame; + int DyingFrame; + int DeadFrame; +} stVortData; + + +// Vorticon Elite = the purple guys that shoot you in ep2 +typedef struct stVortEliteData +{ + unsigned char state; + + unsigned char timer,timer2; + unsigned int animtimer; + unsigned char frame; + signed int inertiay; + unsigned char movedir; + unsigned int timesincefire; + + int dist_traveled; +} stVortEliteData; + + +// Butler = the little pushing robots in ep1 +typedef struct stButlerData +{ + unsigned char state; + unsigned char timer,animtimer; + unsigned char frame; + unsigned int dist_traveled; + + unsigned char movedir; +} stButlerData; + + +// this same struct is used for both ep1 and ep2 "tanks", although +// they have seperate ai modules +typedef struct stTankData +{ + char state; + + uint timer,animtimer; + unsigned char frame; + uint dist_traveled; + + unsigned char movedir; + + uint ponsameleveltime; + unsigned char alreadyfiredcauseonsamelevel; + unsigned char fireafterlook; + + unsigned char detectedPlayer; // 1 if player on same level + unsigned char detectedPlayerIndex; // index of player that was detected + + // for tank2 + uint timetillnextshot; + uint firetimes; + uint timetillcanfire; + uint timetillcanfirecauseonsamelevel; + uint turnaroundtimer; + int pausetime; +} stTankData; + + +// a shot from keen's raygun or an enemy that shoots rayguns +// also used for various other types of projectiles +typedef struct stRayData +{ + char state; + char direction; + char zapzottimer; + + unsigned char dontHitEnable; + unsigned int dontHit; // index of an object type ray will not harm + + // for soundwave + int animframe, animtimer; + int offscreentime; + + // for earth chunks + int baseframe; +} stRayData; + + +typedef struct stDoorData +{ + char timer; +} stDoorData; + + +typedef struct stIceChunk +{ + int vector_x, vector_y; + int veloc_x, veloc_y; +} stIceChunk; + + +typedef struct stTeleportData +{ + char animtimer; + char animframe; + char numframechanges; + + char direction; + int whichplayer; + unsigned int destx; + signed int desty; + + int baseframe; + int idleframe; + + char NoExitingTeleporter; + char snap; + + char fadeamt; + char fadetimer; +} stTeleportData; + + +// the rope that you cut to kill the vorticon at the end of ep1 +typedef struct stRopeData +{ + char state; + int droptimer; + int stoneX, stoneY; + int bgtile; +} stRopeData; + + +typedef struct stWalkerData +{ + unsigned char state; + + unsigned char animtimer, dietimer; + unsigned char walkframe; + signed int walkerdie_inertia_y; + int fallinctimer,fallspeed; + + unsigned char walkdir; + unsigned char kickedplayer[MAX_PLAYERS]; +} stWalkerData; + + +// a moving platform +typedef struct stPlatformData +{ + unsigned char state; + unsigned char animframe; + unsigned int animtimer; + unsigned int waittimer; + + unsigned char movedir; + unsigned char kickedplayer[MAX_PLAYERS]; +} stPlatformData; + + +// many different assorted things, many sector-effectors affect tiles +// in the level instead of being a sprite. named for the similiarity +// to the object in duke3d +typedef struct stSEData +{ + unsigned int type; + + unsigned char state; + unsigned int timer; + unsigned int platx, platy; + unsigned int bgtile; + unsigned int dir; + + int counter,destroytiles; + unsigned int frame; + int mx,my; + int blowx,blowy; +} stSEData; + + + +typedef struct stBabyData +{ + char state; + char dir; + signed int inertia_x, inertia_y; + int jumpdectimer, xdectimer; + int jumpdecrate; + int dietimer; + + char walkframe; + int walktimer; +} stBabyData; + + + +typedef struct stFoobData +{ + char state; + char dir; + + int animframe, animtimer; + int OnSameLevelTime; + int OffOfSameLevelTime; + int spooktimer; + int SpookedByWho; + int blockedtime; +} stFoobData; + + +// Ninja = the kung-fu bears in ep3 +typedef struct stNinjaData +{ + char state; + char dir; + + int animframe, animtimer; + unsigned int timetillkick; + + signed int XInertia, YInertia; + unsigned int XFrictionTimer, YFrictionTimer; + unsigned int XFrictionRate, YFrictionRate; + int KickMoveTimer; + int isdying; + int dietimer; +} stNinjaData; + + +// vorticon mother +typedef struct stMotherData +{ + char state; + char dir; + char hittimes; + + int animframe, animtimer; + int timer; +} stMotherData; + + + +typedef struct stMeepData +{ + char state; + char dir; + + int animframe, animtimer; + int timer; +} stMeepData; + + + +typedef struct stBallJackData +{ + char dir; + int animframe, animtimer; + int speed; +} stBallJackData; + + + +#define NESSIETRAILLEN 5 +typedef struct stNessieData +{ + char state; + char leftrightdir, updowndir; + unsigned int baseframe; + + unsigned int tiletrailX[NESSIETRAILLEN+1]; + unsigned int tiletrailY[NESSIETRAILLEN+1]; + int tiletrailhead; + + char animframe, animtimer; + unsigned int destx, desty; + + unsigned int pausetimer; + unsigned int pausex, pausey; + + unsigned int mortimer_swim_amt; + unsigned int mounted[MAX_PLAYERS]; +} stNessieData; + +#endif /* ENEMYDATA_H_ */ diff --git a/src/ai/foob.cpp b/src/ai/foob.cpp index 17c5edb92..25fb8f9fa 100644 --- a/src/ai/foob.cpp +++ b/src/ai/foob.cpp @@ -3,20 +3,24 @@ #include "../include/game.h" // AI for the foobs (yellow "scaredy cat" creatures) (ep3) -#define FOOB_WALK 0 -#define FOOB_SPOOK 1 -#define FOOB_FLEE 2 -#define FOOB_EXPLODE 3 -#define FOOB_DEAD 4 +enum FOOB_ACTIONS{ +FOOB_WALK, +FOOB_SPOOK, +FOOB_FLEE, +FOOB_EXPLODE, +FOOB_DEAD +}; #define FOOB_WALK_SPEED 2 #define FOOB_WALK_ANIM_RATE 80 -#define FOOB_FLEE_SPEED 20 +#define FOOB_FLEE_SPEED 18 #define FOOB_FLEE_ANIM_RATE 16 #define FOOB_SPOOK_SHOW_TIME 50 +#define FOOB_HARDMODE_BLOCK_TIME 150 + #define FOOB_EXPLODE_ANIM_RATE 35 #define FOOB_SPOOK_TIME 150 @@ -28,7 +32,7 @@ #define FOOB_EXPLODE_FRAME 97 #define FOOB_DEAD_FRAME 101 -void foob_ai(int o, stCloneKeenPlus *pCKP) +void foob_ai(int o, bool hardmode) { int onsamelevel; unsigned int i; @@ -41,9 +45,12 @@ unsigned int i; objects[o].ai.foob.OnSameLevelTime = 0; objects[o].blockedr = 0; objects[o].canbezapped = 1; + objects[o].dead = 0; objects[o].needinit = 0; } if (objects[o].ai.foob.state==FOOB_DEAD) return; + if (!objects[o].hasbeenonscreen) return; + if (objects[o].zapped || objects[o].touchPlayer) { @@ -54,7 +61,7 @@ unsigned int i; objects[o].ai.foob.state = FOOB_EXPLODE; objects[o].canbezapped = 0; if (objects[o].onscreen) g_pSound->playStereofromCoord(SOUND_YORP_DIE, PLAY_NOW, objects[o].scrx); - if (pCKP->Control.levelcontrol.hardmode && objects[o].touchPlayer) + if (hardmode && objects[o].touchPlayer) { killplayer(objects[o].touchedBy); } @@ -142,6 +149,9 @@ unsigned int i; { objects[o].ai.foob.dir = LEFT; } + // in hard mode run TOWARDS the player (he's deadly in hard mode) + if (hardmode) objects[o].ai.foob.dir ^= 1; + } else objects[o].ai.foob.spooktimer++; break; @@ -153,8 +163,10 @@ unsigned int i; { if (objects[o].ai.foob.OffOfSameLevelTime > FOOB_RELAX_TIME) { +relax: ; objects[o].ai.foob.state = FOOB_WALK; objects[o].ai.foob.OnSameLevelTime = 0; + break; } else objects[o].ai.foob.OffOfSameLevelTime++; } @@ -165,16 +177,34 @@ unsigned int i; objects[o].sprite = FOOB_WALK_RIGHT_FRAME + objects[o].ai.foob.animframe; if (!objects[o].blockedr) { - objects[o].x += FOOB_FLEE_SPEED; + objects[o].x += FOOB_FLEE_SPEED; + objects[o].ai.foob.blockedtime = 0; } + else if (hardmode) + { + if (++objects[o].ai.foob.blockedtime >= FOOB_HARDMODE_BLOCK_TIME) + { + objects[o].ai.foob.blockedtime = 0; + goto relax; + } + } } else { // walking left objects[o].sprite = FOOB_WALK_LEFT_FRAME + objects[o].ai.foob.animframe; if (!objects[o].blockedl) { - objects[o].x -= FOOB_FLEE_SPEED; + objects[o].x -= FOOB_FLEE_SPEED; + objects[o].ai.foob.blockedtime = 0; } + else if (hardmode) + { + if (++objects[o].ai.foob.blockedtime >= FOOB_HARDMODE_BLOCK_TIME) + { + objects[o].ai.foob.blockedtime = 0; + goto relax; + } + } } /* walk animation */ @@ -192,6 +222,7 @@ unsigned int i; if (objects[o].sprite==FOOB_DEAD_FRAME) { objects[o].ai.foob.state = FOOB_DEAD; + objects[o].dead = 1; } else { @@ -205,3 +236,4 @@ unsigned int i; break; } } + diff --git a/src/ai/icebit.cpp b/src/ai/icebit.cpp deleted file mode 100644 index 5e48a2afb..000000000 --- a/src/ai/icebit.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "../keen.h" - -#include "../include/enemyai.h" - -// the little pieces that break off of an OBJ_ICECHUNK when it hits -// a wall or a player. (Ep1) - -#define ICEBIT_SPEED 20 - -void icebit_ai(int o) -{ - if (objects[o].needinit) - { // first time initilization - objects[o].inhibitfall = 1; - objects[o].canbezapped = 0; - objects[o].needinit = 0; - } - - switch(objects[o].ai.icechunk.movedir) - { - case DUPRIGHT: - objects[o].x += ICEBIT_SPEED; - objects[o].y -= ICEBIT_SPEED; - break; - case DUPLEFT: - objects[o].x -= ICEBIT_SPEED; - objects[o].y -= ICEBIT_SPEED; - break; - case DDOWNRIGHT: - objects[o].x += ICEBIT_SPEED; - objects[o].y += ICEBIT_SPEED; - break; - case DDOWNLEFT: - objects[o].x -= ICEBIT_SPEED; - objects[o].y += ICEBIT_SPEED; - break; - break; - } -} diff --git a/src/ai/icecannon.cpp b/src/ai/icecannon.cpp new file mode 100644 index 000000000..8996be757 --- /dev/null +++ b/src/ai/icecannon.cpp @@ -0,0 +1,169 @@ +#include "../keen.h" +#include "../include/game.h" + +#include "../include/enemyai.h" + +#include "icecannon.h" + +#include "../sdl/sound/CSound.h" + +// the chunks of ice shot out by an ice cannon (ep1) +#define ICECHUNK_SPEED 15 +#define ICECHUNK_STRAIGHT_SPEED 20 +#define ICECHUNK_WAIT_TIME 150 + +void icechunk_ai(int o) +{ + + if (objects[o].needinit) + { // first time initialization + int speed; + + if (objects[o].ai.icechunk.vector_x && objects[o].ai.icechunk.vector_y) + speed = ICECHUNK_SPEED; + else + speed = ICECHUNK_STRAIGHT_SPEED; + + objects[o].ai.icechunk.veloc_x = speed * objects[o].ai.icechunk.vector_x; + objects[o].ai.icechunk.veloc_y = speed * objects[o].ai.icechunk.vector_y; + objects[o].inhibitfall = 1; + objects[o].canbezapped = 0; + objects[o].needinit = 0; + } + + // freeze the player if it touches him + if (objects[o].touchPlayer) + { + if (!player[objects[o].touchPlayer].pfrozentime) + { + // make him start sliding in the direction of the impact + if (objects[o].ai.icechunk.vector_x > 0) + { + player[objects[o].touchedBy].pdir = player[objects[o].touchedBy].pshowdir = RIGHT; + player[objects[o].touchedBy].pinertia_x = PMAXSPEED; + } + else if (objects[o].ai.icechunk.vector_x < 0) + { + player[objects[o].touchedBy].pdir = player[objects[o].touchedBy].pshowdir = LEFT; + player[objects[o].touchedBy].pinertia_x = -PMAXSPEED; + } + else // perfectly vertical ice cannons + { + #define UPDNCANNON_PUSHAMT 4 + if (player[objects[o].touchedBy].pinertia_x < UPDNCANNON_PUSHAMT) + { + if (rnd()&1) + player[objects[o].touchedBy].pinertia_x = UPDNCANNON_PUSHAMT; + else + player[objects[o].touchedBy].pinertia_x = -UPDNCANNON_PUSHAMT; + } + } + } + + freezeplayer(objects[o].touchedBy); + smash(o); + return; + } + + // smash the chunk if it hits something + if (objects[o].ai.icechunk.vector_x > 0) + { + if (objects[o].blockedr) { smash(o); return; } + } + else if (objects[o].ai.icechunk.vector_x < 0) + { + if (objects[o].blockedl) { smash(o); return; } + } + + if (objects[o].ai.icechunk.vector_y > 0) + { + if (objects[o].blockedd) { smash(o); return; } + } + else if (objects[o].ai.icechunk.vector_y < 0) + { + if (objects[o].blockedu) { smash(o); return; } + } + + // fly through the air + objects[o].x += objects[o].ai.icechunk.veloc_x; + objects[o].y += objects[o].ai.icechunk.veloc_y; +} + + +static void smash(int o) +{ +int newobject; + + if (objects[o].onscreen) + { + g_pSound->playStereofromCoord(SOUND_CHUNKSMASH, PLAY_NOW, objects[o].x); + + // upleft + newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICEBIT); + objects[newobject].ai.icechunk.vector_x = -1; + objects[newobject].ai.icechunk.vector_y = -1; + + // upright + newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICEBIT); + objects[newobject].ai.icechunk.vector_x = 1; + objects[newobject].ai.icechunk.vector_y = -1; + + // downleft + newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICEBIT); + objects[newobject].ai.icechunk.vector_x = -1; + objects[newobject].ai.icechunk.vector_y = 1; + + // downright + newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICEBIT); + objects[newobject].ai.icechunk.vector_x = 1; + objects[newobject].ai.icechunk.vector_y = 1; + } + + delete_object(o); +} + + +// the little pieces that break off of an OBJ_ICECHUNK when it hits +// a wall or a player. (Ep1) +#define ICEBIT_SPEED 20 + +void icebit_ai(int o) +{ + if (objects[o].needinit) + { // first time initilization + objects[o].ai.icechunk.veloc_x = ICEBIT_SPEED * objects[o].ai.icechunk.vector_x; + objects[o].ai.icechunk.veloc_y = ICEBIT_SPEED * objects[o].ai.icechunk.vector_y; + objects[o].inhibitfall = 1; + objects[o].canbezapped = 0; + objects[o].needinit = 0; + } + + objects[o].x += objects[o].ai.icechunk.veloc_x; + objects[o].y += objects[o].ai.icechunk.veloc_y; + + if (!objects[o].onscreen) + { + delete_object(o); + } +} + + +// the ice cannon itself +void icecannon_ai(int o) +{ +int newobject; + + // keep spawner object invisible and properly positioned + objects[o].sprite = BLANKSPRITE; + objects[o].inhibitfall = 1; + + if (!gunfiretimer) + { + newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICECHUNK); + + objects[newobject].ai.icechunk.vector_x = objects[o].ai.icechunk.vector_x; + objects[newobject].ai.icechunk.vector_y = objects[o].ai.icechunk.vector_y; + } +} + + diff --git a/src/ai/icecannon.h b/src/ai/icecannon.h new file mode 100644 index 000000000..363f18853 --- /dev/null +++ b/src/ai/icecannon.h @@ -0,0 +1,18 @@ +/* + * icecannon.h + * + * Created on: 06.08.2009 + * Author: gerstrong + */ + +#ifndef ICECANNON_H_ +#define ICECANNON_H_ + +void delete_object(int o); + +unsigned int rnd(void); + +static void smash(int o); + + +#endif /* ICECANNON_H_ */ diff --git a/src/ai/icechunk.cpp b/src/ai/icechunk.cpp deleted file mode 100644 index ee82d7b6a..000000000 --- a/src/ai/icechunk.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "../sdl/sound/CSound.h" - -#include "../keen.h" -#include "../include/game.h" - -#include "../include/enemyai.h" - -// the chunks of ice shot out by an ice cannon (ep1) - -#define ICECHUNK_FLY 0 - -#define ICECHUNK_SPEED 15 -#define ICECHUNK_WAIT_TIME 150 - -void icechunk_ai(int o) -{ -int newobject,blockedlr; - if (objects[o].needinit) - { // first time initilization - objects[o].ai.icechunk.state = ICECHUNK_FLY; - objects[o].ai.icechunk.timer = 0; - objects[o].inhibitfall = 1; - objects[o].needinit = 0; - objects[o].canbezapped = 0; - } - - if (objects[o].touchPlayer && objects[o].ai.icechunk.state==ICECHUNK_FLY &&\ - !player[objects[o].touchedBy].pfrozentime) - { - switch(objects[o].ai.icechunk.movedir) - { - case DUPRIGHT: - case DDOWNRIGHT: - player[objects[o].touchedBy].pdir = player[objects[o].touchedBy].pshowdir = RIGHT; - player[objects[o].touchedBy].pinertia_x = PMAXSPEED; - break; - case DUPLEFT: - case DDOWNLEFT: - player[objects[o].touchedBy].pdir = player[objects[o].touchedBy].pshowdir = LEFT; - player[objects[o].touchedBy].pinertia_x = -PMAXSPEED; - break; - } - freezeplayer(objects[o].touchedBy); - goto smash; - } - - switch(objects[o].ai.icechunk.state) - { - case ICECHUNK_FLY: - blockedlr = 0; - if (objects[o].ai.icechunk.movedir==DUPRIGHT || objects[o].ai.icechunk.movedir==DDOWNRIGHT) - { - if (objects[o].blockedr) blockedlr = 1; - } - if (objects[o].ai.icechunk.movedir==DUPLEFT || objects[o].ai.icechunk.movedir==DDOWNLEFT) - { - if (objects[o].blockedl) blockedlr = 1; - } - - if (blockedlr) - { - smash: ; - if (objects[o].onscreen) - { - g_pSound->playStereofromCoord(SOUND_CHUNKSMASH, PLAY_NOW, objects[o].scrx); - newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICEBIT); - objects[newobject].ai.icechunk.movedir = DUPLEFT; - newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICEBIT); - objects[newobject].ai.icechunk.movedir = DUPRIGHT; - newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICEBIT); - objects[newobject].ai.icechunk.movedir = DDOWNLEFT; - newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICEBIT); - objects[newobject].ai.icechunk.movedir = DDOWNRIGHT; - } - - objects[o].exists = 0; - return; - } - - switch(objects[o].ai.icechunk.movedir) - { - case DUPRIGHT: - objects[o].x += ICECHUNK_SPEED; - objects[o].y -= ICECHUNK_SPEED; - break; - case DUPLEFT: - objects[o].x -= ICECHUNK_SPEED; - objects[o].y -= ICECHUNK_SPEED; - break; - case DDOWNLEFT: - objects[o].x -= ICECHUNK_SPEED; - objects[o].y += ICECHUNK_SPEED; - break; - case DDOWNRIGHT: - objects[o].x += ICECHUNK_SPEED; - objects[o].y += ICECHUNK_SPEED; - break; - break; - } - break; - } -} diff --git a/src/ai/nessie.cpp b/src/ai/nessie.cpp index 35fe2cf64..5d4ef3156 100644 --- a/src/ai/nessie.cpp +++ b/src/ai/nessie.cpp @@ -283,7 +283,7 @@ int oneoflasttiles; oneoflasttiles = 0; for(i=0;i> CSF >> 4) - 4; - objects[o].ai.rope.stoneY = (objects[o].y >> CSF >> 4) + 1; - // hide the rope - objects[o].sprite = BlankSprite; - } - break; - case ROPE_DROP: - if (objects[o].ai.rope.droptimer > STONE_DROP_RATE) - { - rope_movestone(o); - objects[o].ai.rope.droptimer = 0; + objects[o].needinit = 0; + } - objects[o].ai.rope.droptimes++; - if (objects[o].ai.rope.droptimes >= STONE_DROP_TIMES) - { - // just to make sure the boss is dead -// killboss(o); - objects[o].exists = 0; - } - } - else objects[o].ai.rope.droptimer++; + switch(objects[o].ai.rope.state) + { + case ROPE_IDLE: + if (objects[o].zapped) + { + // rope got broke! time to drop the stone + objects[o].ai.rope.state = ROPE_DROPSTONE; + objects[o].ai.rope.droptimer = 0; + // hide the rope + objects[o].sprite = BLANKSPRITE; + // get upper left corner of the stone + objects[o].ai.rope.stoneX = (objects[o].x >> CSF >> 4) - 4; + objects[o].ai.rope.stoneY = (objects[o].y >> CSF >> 4) + 1; + // get color of background + objects[o].ai.rope.bgtile = getmaptileat(objects[o].x>>CSF, objects[o].y>>CSF); + } + break; + + case ROPE_DROPSTONE: + if (!objects[o].ai.rope.droptimer) + { + objects[o].ai.rope.droptimer = STONE_DROP_RATE; + rope_movestone(o); + + // check if we've hit the ground yet + for(x=0;x0;y--) - { - for(x=0;x= objects[vb].x) - { - if (ypix <= objects[vb].y && ypix+(16<= objects[vb].y) - { - killboss(o); - } - } - } - } - // clear the space at the top with black - for(x=0;x0;y--) + { + for(x=0;xplayStereofromCoord(SOUND_VORT_DIE, PLAY_NOW, objects[o].scrx); - } -} diff --git a/src/ai/se.cpp b/src/ai/se.cpp index b5e032f91..7d1f8764a 100644 --- a/src/ai/se.cpp +++ b/src/ai/se.cpp @@ -1,10 +1,13 @@ #include "../sdl/sound/CSound.h" #include "../keen.h" #include "ray.h" +#include "se.h" #include "../include/game.h" #include "../include/enemyai.h" +#include "../CLogFile.h" + char PlatExtending=0; // "Sector Effector" object (The name comes from D3D)...it's basically @@ -13,111 +16,98 @@ char PlatExtending=0; // around it. Used where it wasn't worth it to create a whole new object // (or where I was too lazy to do it). -void se_extend_plat(int o); // extends a platform (ep2) -void se_spark(int o, stLevelControl *p_levelcontrol); // spark in Tantalus Ray (ep2) -void se_gun(int o); // periodically-firing guns (ep3) -void se_ankhshield(int o); // Ankh Shield (ep3) -void se_icecannon(int o); // ice cannon (ep1) -void se_mortimer_arm(int o); // Mortimer's arms (ep3) -void se_mortimer_leg_left(int o); // Mortimer's left leg (ep3) -void se_mortimer_leg_right(int o, stCloneKeenPlus *pCKP); // Mortimer's right leg (ep3) -void se_mortimer_spark(int o, stCloneKeenPlus *pCKP); // spark in Mortimer's machine (ep3) -void se_mortimer_heart(int o); // Mortimer's "heart" (ep3) -void se_mortimer_zapsup(int o, stCloneKeenPlus *pCKP); // causes zaps to roll up Mortimer's machine (ep3) -void se_mortimer_randomzaps(int o); // spawns random zaps on Mortimer's machine (ep3) -void se_retract_plat(int o); +// this also contains the AI for the Spark object void set_mortimer_surprised(int yes); -int mortimer_surprisedcount=0; +int mortimer_surprisedcount = 0; void se_ai(int o, stCloneKeenPlus *pCKP) { - switch(objects[o].ai.se.type) - { - case SE_EXTEND_PLATFORM: se_extend_plat(o); break; - case SE_RETRACT_PLATFORM: se_retract_plat(o); break; - case SE_SPARK: se_spark(o, (&pCKP->Control.levelcontrol)); break; - case SE_GUN_VERT: se_gun(o); break; - case SE_GUN_RIGHT: se_gun(o); break; - case SE_ANKHSHIELD: se_ankhshield(o); break; - case SE_ICECANNON: se_icecannon(o); break; - case SE_MORTIMER_ARM: se_mortimer_arm(o); break; - case SE_MORTIMER_LEG_LEFT: se_mortimer_leg_left(o); break; - case SE_MORTIMER_LEG_RIGHT: se_mortimer_leg_right(o, pCKP); break; - case SE_MORTIMER_SPARK: se_mortimer_spark(o, pCKP); break; - case SE_MORTIMER_HEART: se_mortimer_heart(o); break; - case SE_MORTIMER_ZAPSUP: se_mortimer_zapsup(o, pCKP); break; - case SE_MORTIMER_RANDOMZAPS: se_mortimer_randomzaps(o); break; + switch(objects[o].ai.se.type) + { + case SE_EXTEND_PLATFORM: se_extend_plat(o, &(pCKP->Control.levelcontrol.PlatExtending) ); break; + case SE_RETRACT_PLATFORM: se_retract_plat(o, &(pCKP->Control.levelcontrol.PlatExtending) ); break; + case SE_ANKHSHIELD: se_ankhshield(o, pCKP->Control.levelcontrol.episode); break; + case SE_MORTIMER_ARM: se_mortimer_arm(o); break; + case SE_MORTIMER_LEG_LEFT: se_mortimer_leg_left(o); break; + case SE_MORTIMER_LEG_RIGHT: se_mortimer_leg_right(o); break; + case SE_MORTIMER_SPARK: se_mortimer_spark(o, &(pCKP->Control.levelcontrol) ); break; + case SE_MORTIMER_HEART: se_mortimer_heart(o, &(pCKP->Control.levelcontrol) ); break; + case SE_MORTIMER_ZAPSUP: se_mortimer_zapsup(o, &(pCKP->Control.levelcontrol) ); break; + case SE_MORTIMER_RANDOMZAPS: se_mortimer_randomzaps(o); break; - default: crashflag = 1; - crashflag2 = objects[o].ai.se.type; - why_term_ptr = "Invalid sector effector type (shown in flag2)"; - break; - } + default: + g_pLogFile->ftextOut("Invalid sector effector type %d", objects[o].ai.se.type); + break; + } } -void se_extend_plat(int o) +void se_extend_plat(int o, bool *p_PlatExtending) { -//char buf[80]; #define PLAT_EXTEND_RATE 30 - if (objects[o].needinit) - { - objects[o].ai.se.timer = 0; - objects[o].inhibitfall = 1; - objects[o].canbezapped = 0; - objects[o].sprite = BlankSprite; - objects[o].needinit = 0; + if (objects[o].needinit) + { + objects[o].ai.se.timer = 0; + objects[o].inhibitfall = 1; + objects[o].canbezapped = 0; + objects[o].sprite = BLANKSPRITE; - // figure out which direction the bridge is supposed to go - if (!TileProperty[map.mapdata[objects[o].ai.se.platx+1][objects[o].ai.se.platy]][BLEFT]) - //if (!tiles[map.mapdata[objects[o].ai.se.platx+1][objects[o].ai.se.platy]].solidl) - objects[o].ai.se.dir = RIGHT; - else - objects[o].ai.se.dir = LEFT; - } + // if the platform is already extended, turn ourselves + // into an se_retract_plat() + //lprintf(">se_extend:check=%d expect=%d",getmaptileat(objects[o].x>>CSF,objects[o].y>>CSF),TILE_EXTENDING_PLATFORM); + if (map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy]==\ + TILE_EXTENDING_PLATFORM) + { + objects[o].ai.se.type = SE_RETRACT_PLATFORM; + se_retract_plat(o, p_PlatExtending); + return; + } - if (!objects[o].ai.se.timer) - { - if (!TileProperty[map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy]][BUP]) - //if (!tiles[map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy]].solidfall) - { - map_chgtile(objects[o].ai.se.platx, objects[o].ai.se.platy, TILE_EXTENDING_PLATFORM); + // figure out which direction the bridge is supposed to go + if (!TileProperty[map.mapdata[objects[o].ai.se.platx+1][objects[o].ai.se.platy]][BLEFT]) + objects[o].ai.se.dir = RIGHT; + else + objects[o].ai.se.dir = LEFT; - if (objects[o].ai.se.dir==RIGHT) - objects[o].ai.se.platx++; - else - objects[o].ai.se.platx--; + objects[o].needinit = 0; + } - objects[o].ai.se.timer = PLAT_EXTEND_RATE; + if (!objects[o].ai.se.timer) + { + if (!TileProperty[map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy]][BUP]) + { + map_chgtile(objects[o].ai.se.platx, objects[o].ai.se.platy, TILE_EXTENDING_PLATFORM); + kill_all_intersecting_tile(objects[o].ai.se.platx, objects[o].ai.se.platy); + + if (objects[o].ai.se.dir==RIGHT) + objects[o].ai.se.platx++; + else + objects[o].ai.se.platx--; + + objects[o].ai.se.timer = PLAT_EXTEND_RATE; + } + else + { + delete_object(o); + *p_PlatExtending = false; + return; } - else - { - objects[o].exists = 0; - PlatExtending = 0; - } - } - else objects[o].ai.se.timer--; - + } + else objects[o].ai.se.timer--; } -void se_retract_plat(int o) +void se_retract_plat(int o, bool *p_PlatExtending) { - if (objects[o].needinit) - { - objects[o].ai.se.timer = PLAT_EXTEND_RATE; - objects[o].inhibitfall = 1; - objects[o].canbezapped = 0; - objects[o].sprite = BlankSprite; - objects[o].needinit = 0; - - // get the background tile from the tile above the starting point - objects[o].ai.se.bgtile = map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy-1]; - // figure out which direction the bridge is supposed to go - if (map.mapdata[objects[o].ai.se.platx-1][objects[o].ai.se.platy] != TILE_EXTENDING_PLATFORM) + if (objects[o].needinit) + { + // get the background tile from the tile above the starting point + objects[o].ai.se.bgtile = map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy-1]; + // figure out which direction the bridge is supposed to go + if (map.mapdata[objects[o].ai.se.platx-1][objects[o].ai.se.platy] != TILE_EXTENDING_PLATFORM) objects[o].ai.se.dir = LEFT; else objects[o].ai.se.dir = RIGHT; @@ -127,67 +117,67 @@ void se_retract_plat(int o) // it was extended) do { - if (map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy] != TILE_EXTENDING_PLATFORM) - { // we've found the end of the platform - break; - } - if (objects[o].ai.se.dir==LEFT) - { - if (objects[o].ai.se.platx==map.xsize) - { - crashflag = 1; - why_term_ptr = "SE_RETRACT_PLATFORM: Failed to find end of platform when scanning right."; - return; - } - objects[o].ai.se.platx++; - } - else - { // platform will be removed in a right-going direction - if (objects[o].ai.se.platx==0) - { - crashflag = 1; - why_term_ptr = "SE_RETRACT_PLATFORM: Failed to find end of platform when scanning left."; - return; - } + if (map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy] != TILE_EXTENDING_PLATFORM) + { // we've found the end of the platform + break; + } + if (objects[o].ai.se.dir==LEFT) + { + if (objects[o].ai.se.platx==map.xsize) + { + g_pLogFile->ftextOut("SE_RETRACT_PLATFORM: Failed to find end of platform when scanning right."); + return; + } + objects[o].ai.se.platx++; + } + else + { // platform will be removed in a right-going direction + if (objects[o].ai.se.platx==0) + { + g_pLogFile->ftextOut("SE_RETRACT_PLATFORM: Failed to find end of platform when scanning left."); + return; + } objects[o].ai.se.platx--; } - } while(1); + } while(1); // when we were scanning we went one tile too far, go back one if (objects[o].ai.se.dir==LEFT) objects[o].ai.se.platx--; else objects[o].ai.se.platx++; - } - if (objects[o].ai.se.timer >= PLAT_EXTEND_RATE) - { - if (map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy]==TILE_EXTENDING_PLATFORM) - { - map_chgtile(objects[o].ai.se.platx, objects[o].ai.se.platy, objects[o].ai.se.bgtile); + objects[o].needinit = 0; + } - if (objects[o].ai.se.dir==RIGHT) - objects[o].ai.se.platx++; - else - objects[o].ai.se.platx--; + if (!objects[o].ai.se.timer) + { + if (map.mapdata[objects[o].ai.se.platx][objects[o].ai.se.platy]==TILE_EXTENDING_PLATFORM) + { + map_chgtile(objects[o].ai.se.platx, objects[o].ai.se.platy, objects[o].ai.se.bgtile); - objects[o].ai.se.timer = 0; - } - else - { - objects[o].exists = 0; - PlatExtending = 0; - } - } - else objects[o].ai.se.timer++; + if (objects[o].ai.se.dir==RIGHT) + objects[o].ai.se.platx++; + else + objects[o].ai.se.platx--; + objects[o].ai.se.timer = PLAT_EXTEND_RATE; + } + else + { + delete_object(o); + *p_PlatExtending = false; + } + } + else objects[o].ai.se.timer--; } -void se_spark(int o, stLevelControl *p_levelcontrol) +// AI for the Spark object in the Tantalus Ray Machine's of ep2 +void spark_ai(int o, int *p_sparks_left) { int newobject; int mx,my,x,y; - #define SPARK_BASEFRAME 128 + #define SPARK_BASEFRAME OBJ_SPARK_DEFSPRITE_EP2 #define SPARK_ANIMRATE 20 #define SPARK_ANIMATE 0 @@ -197,6 +187,8 @@ int mx,my,x,y; #define SPARK_BLOW_DELAY 100 + #define BG_GREY 143 + if (objects[o].needinit) { objects[o].ai.se.state = SPARK_ANIMATE; @@ -215,9 +207,14 @@ int mx,my,x,y; } else { - objects[o].sprite = BlankSprite; + objects[o].sprite = BLANKSPRITE; } + if (objects[o].touchPlayer) + { + killplayer(objects[o].touchedBy); + } + switch(objects[o].ai.se.state) { case SPARK_ANIMATE: @@ -230,19 +227,18 @@ int mx,my,x,y; if (objects[o].zapped) { - g_pSound->playStereofromCoord(SOUND_SHOT_HIT, PLAY_NOW, objects[o].scrx); + g_pSound->playStereofromCoord(SOUND_SHOT_HIT,PLAY_NOW, objects[o].x); + // break the glass and blow out the electric arcs map_chgtile(objects[o].ai.se.mx - 2, objects[o].ai.se.my, 492); map_chgtile(objects[o].ai.se.mx - 1, objects[o].ai.se.my, 546); map_chgtile(objects[o].ai.se.mx, objects[o].ai.se.my, 547); map_chgtile(objects[o].ai.se.mx + 1, objects[o].ai.se.my, 548); map_chgtile(objects[o].ai.se.mx + 2, objects[o].ai.se.my, 492); - map_deanimate(objects[o].ai.se.mx - 2, objects[o].ai.se.my); - map_deanimate(objects[o].ai.se.mx + 2, objects[o].ai.se.my); // remove the unneeded dome tiles - map_chgtile(objects[o].ai.se.mx - 1, objects[o].ai.se.my-1, BG_GRAY); - map_chgtile(objects[o].ai.se.mx, objects[o].ai.se.my-1, BG_GRAY); - map_chgtile(objects[o].ai.se.mx + 1, objects[o].ai.se.my-1, BG_GRAY); + map_chgtile(objects[o].ai.se.mx - 1, objects[o].ai.se.my-1, BG_GREY); + map_chgtile(objects[o].ai.se.mx, objects[o].ai.se.my-1, BG_GREY); + map_chgtile(objects[o].ai.se.mx + 1, objects[o].ai.se.my-1, BG_GREY); // break the switch map_chgtile(objects[o].ai.se.mx - 3, objects[o].ai.se.my + 4, 506); @@ -251,12 +247,8 @@ int mx,my,x,y; objects[o].ai.se.timer = 0; objects[o].ai.se.blowy = 0; - fade.mode = FADE_GO; - fade.dir = FADE_IN; - fade.curamt = PAL_FADE_WHITEOUT; - fade.fadetimer = 0; - fade.rate = FADE_NORM; - } + //fade(FADE_FLASH, FADE_NORM); + } break; case SPARK_BLOWUP1: // one by one blow out the purple thingies below the device @@ -266,14 +258,14 @@ int mx,my,x,y; mx = objects[o].ai.se.mx; my = objects[o].ai.se.my+3+objects[o].ai.se.blowy; map_chgtile(mx, my, 505); - map_deanimate(mx, my); // spawn a ZAP! or a ZOT! newobject = spawn_object(mx<<4<playStereofromCoord(SOUND_SHOT_HIT, PLAY_NOW, objects[o].scrx); + g_pSound->playStereofromCoord(SOUND_SHOT_HIT,PLAY_NOW, objects[newobject].x); + objects[o].ai.se.blowy++; if (objects[o].ai.se.blowy >= 3) @@ -299,11 +291,10 @@ int mx,my,x,y; for(x=0;x<3;x++) { map_chgtile(mx+x,my+y,533); - map_animate(mx+x,my+y); } } - objects[o].exists = 0; - p_levelcontrol->canexit = 1; // ok, you can exit now + delete_object(o); + *p_sparks_left--; return; } @@ -313,7 +304,6 @@ int mx,my,x,y; { my = objects[o].ai.se.my+3+y; map_chgtile(mx, my, 549); - map_deanimate(mx, my); // spawn a ZAP! or a ZOT! newobject = spawn_object(mx<<4<playStereofromCoord(SOUND_SHOT_HIT, PLAY_NOW, objects[o].scrx); + g_pSound->playStereofromCoord(SOUND_SHOT_HIT, PLAY_NOW, objects[newobject].x); objects[o].ai.se.blowx++; } else objects[o].ai.se.timer++; @@ -330,37 +320,7 @@ int mx,my,x,y; } -void se_gun(int o) -{ -int newobject; - if (objects[o].needinit) - { - objects[o].ai.se.timer = 0; - objects[o].sprite = BlankSprite; - objects[o].inhibitfall = 1; - objects[o].needinit = 0; - } - - if (!gunfiretimer) - { - if (objects[o].ai.se.type==SE_GUN_VERT) - { - newobject = spawn_object(objects[o].x+(4< 0) && (objects[o].scrx < 255)) - g_pSound->playStereofromCoord(SOUND_TANK_FIRE, PLAY_NOW, objects[o].scrx); - } - } - -void se_ankhshield(int o) +void se_ankhshield(int o, int episode) { #define ANKH_FLICKER_FREQ 12 @@ -382,7 +342,7 @@ void se_ankhshield(int o) break; case ANKH_STATE_FLICKERFAST: if (objects[o].ai.se.frame&1) - objects[o].sprite = BlankSprite; + objects[o].sprite = BLANKSPRITE; else { if (objects[o].ai.se.frame&2) @@ -393,9 +353,9 @@ void se_ankhshield(int o) break; case ANKH_STATE_FLICKERSLOW: if (objects[o].ai.se.frame>4) - objects[o].sprite = BlankSprite; + objects[o].sprite = BLANKSPRITE; else - objects[o].sprite = ANKH_SHIELD_FRAME; + objects[o].sprite = (episode==3)?ANKH_SHIELD_FRAME:YORPSHIELD_SPRITE; break; } @@ -406,20 +366,6 @@ void se_ankhshield(int o) objects[o].ai.se.timer = 0; } else objects[o].ai.se.timer++; - - -} - - -void se_icecannon(int o) -{ -int newobject; - - if (!gunfiretimer) - { - newobject = spawn_object(objects[o].x, objects[o].y, OBJ_ICECHUNK); - objects[newobject].ai.icechunk.movedir = objects[o].ai.se.dir; - } } #define ARM_GO 0 @@ -541,7 +487,7 @@ int mx,my; #define MSPARK_IDLE 0 #define MSPARK_DESTROYARMS 1 -void se_mortimer_spark(int o, stCloneKeenPlus *pCKP) +void se_mortimer_spark(int o, stLevelControl *p_levelcontrol) { int x,mx,i; int newobject; @@ -576,56 +522,54 @@ int newobject; if (objects[o].zapped) { - set_mortimer_surprised(1); + set_mortimer_surprised(1); + SetVibrateTime(200, 0, p_levelcontrol); - // if there are any sparks left, destroy the spark, - // else destroy mortimer's arms - for(i=0;iplayStereofromCoord(SOUND_SHOT_HIT, PLAY_NOW, objects[o].x); for(x=0;x<3;x++) { mx = MORTIMER_LEFT_ARM_X+x; - if (map.mapdata[mx][objects[o].ai.se.my]!=169) + if (map.mapdata[mx][objects[o].ai.se.my] != 169) { - //if (tiles[map.mapdata[mx][objects[o].ai.se.my]].isAnimated) - if (TileProperty[map.mapdata[mx][objects[o].ai.se.my]][ANIMATION] > 1) - { - map_deanimate(mx, objects[o].ai.se.my); - } map_chgtile(mx, objects[o].ai.se.my, 169); // spawn a ZAP! or a ZOT! newobject = spawn_object(((mx<<4)+4)< 1) - { - map_deanimate(mx, objects[o].ai.se.my); - } map_chgtile(mx, objects[o].ai.se.my, 169); // spawn a ZAP! or a ZOT! newobject = spawn_object(((mx<<4)+4)< MORTIMER_ARMS_YEND) { - objects[o].exists = 0; + delete_object(o); set_mortimer_surprised(0); } } @@ -685,57 +625,59 @@ int newobject; #define ZAPSUP_NORMAL 0 #define ZAPSUP_ABOUTTOFADEOUT 1 -void se_mortimer_heart(int o) +void se_mortimer_heart(int o, stLevelControl *p_levelcontrol) { -int i,x; +int x,i; int newobject; if (objects[o].needinit) { - objects[o].ai.se.timer = 0; - objects[o].ai.se.frame = 0; - objects[o].ai.se.state = HEART_IDLE; - objects[o].inhibitfall = 1; - objects[o].canbezapped = 1; - objects[o].needinit = 0; - mortimer_surprisedcount = 0; + objects[o].ai.se.timer = 0; + objects[o].ai.se.frame = 0; + objects[o].ai.se.state = HEART_IDLE; + objects[o].inhibitfall = 1; + objects[o].canbezapped = 1; + objects[o].needinit = 0; + mortimer_surprisedcount = 0; } switch(objects[o].ai.se.state) { - case HEART_IDLE: - objects[o].sprite = MORTIMER_HEART_BASEFRAME + objects[o].ai.se.frame; + case HEART_IDLE: + objects[o].sprite = MORTIMER_HEART_BASEFRAME + objects[o].ai.se.frame; - if (objects[o].ai.se.timer > HEART_ANIMRATE) - { - objects[o].ai.se.frame ^= 1; - objects[o].ai.se.timer = 0; - } - else objects[o].ai.se.timer++; + if (objects[o].ai.se.timer > HEART_ANIMRATE) + { + objects[o].ai.se.frame ^= 1; + objects[o].ai.se.timer = 0; + } + else objects[o].ai.se.timer++; - if (objects[o].zapped) - { - objects[o].sprite = BlankSprite; - set_mortimer_surprised(1); + if (objects[o].zapped) + { + objects[o].sprite = BLANKSPRITE; + set_mortimer_surprised(1); - /* destroy Mortimer's machine */ + /* destroy Mortimer's machine */ + SetVibrateTime(1500, 0, p_levelcontrol); - // kill all enemies - for(i=0;i MORTIMER_NUMZAPWAVES) { objects[newobject].ai.se.destroytiles = 1; - objects[o].exists = 0; + delete_object(o); } else objects[o].ai.se.counter++; } @@ -764,11 +706,6 @@ int newobject; for(x=MORTIMER_MACHINE_XSTART;x MORTIMER_MACHINE_YEND) { - objects[o].exists = 0; + delete_object(o); } else objects[o].ai.se.my++; } @@ -790,8 +727,8 @@ int newobject; } } -#define TIME_AFTER_DESTROY_BEFORE_FADEOUT 500 -void se_mortimer_zapsup(int o, stCloneKeenPlus *pCKP) +#define TIME_AFTER_DESTROY_BEFORE_FADEOUT 2300 +void se_mortimer_zapsup(int o, stLevelControl *levelcontrol) { int x, newobject; @@ -799,12 +736,13 @@ int x, newobject; { if (objects[o].ai.se.state==ZAPSUP_ABOUTTOFADEOUT) { - pCKP->Control.levelcontrol.level_done = LEVEL_DONE_FADEOUT; - endlevel(1,pCKP); - objects[o].exists = 0; + levelcontrol->level_done = LEVEL_DONE_FADEOUT; + endlevel(WON_LEVEL, levelcontrol); + delete_object(o); return; } + g_pSound->playStereofromCoord(SOUND_SHOT_HIT, PLAY_NOW, objects[o].x); for(x=MORTIMER_MACHINE_XSTART;xplayStereofromCoord(SOUND_FOOTSLAM, PLAY_NOW, objects[o].scrx); + g_pSound->playStereofromCoord(SOUND_FOOTSLAM, PLAY_NOW, objects[o].x); } else { @@ -954,13 +887,9 @@ int mx,my; } } -void se_mortimer_leg_right(int o, stCloneKeenPlus *pCKP) +void se_mortimer_leg_right(int o) { int mx,my; - if (objects[o].touchPlayer) - { - killplayer(objects[o].touchedBy); - } if (objects[o].needinit) { objects[o].ai.se.dir = UP; @@ -970,6 +899,11 @@ int mx,my; objects[o].needinit = 0; } + if (objects[o].touchPlayer) + { + killplayer(objects[o].touchedBy); + } + switch(objects[o].ai.se.state) { case LEG_GO: @@ -990,7 +924,7 @@ int mx,my; { objects[o].ai.se.timer = 0; objects[o].ai.se.state = LEG_WAIT; - g_pSound->playStereofromCoord(SOUND_FOOTSLAM, PLAY_NOW, objects[o].scrx); + g_pSound->playStereofromCoord(SOUND_FOOTSLAM, PLAY_NOW, objects[o].x); } else { @@ -1065,7 +999,7 @@ int x,y; int newobject; if (objects[o].needinit) { - objects[o].sprite = BlankSprite; + objects[o].sprite = BLANKSPRITE; objects[o].ai.se.counter = 0; objects[o].ai.se.timer = 0; objects[o].needinit = 0; @@ -1073,8 +1007,8 @@ int newobject; if (!objects[o].ai.se.timer) { - x = rand()%((MORTIMER_MACHINE_XEND*16)-(MORTIMER_MACHINE_XSTART*16))+(MORTIMER_MACHINE_XSTART*16); - y = rand()%((MORTIMER_MACHINE_YENDNOLEGS*16)-(MORTIMER_MACHINE_YSTART*16))+(MORTIMER_MACHINE_YSTART*16); + x = rnd()%((MORTIMER_MACHINE_XEND*16)-(MORTIMER_MACHINE_XSTART*16))+(MORTIMER_MACHINE_XSTART*16); + y = rnd()%((MORTIMER_MACHINE_YENDNOLEGS*16)-(MORTIMER_MACHINE_YSTART*16))+(MORTIMER_MACHINE_YSTART*16); // spawn a ZAP! or a ZOT! newobject = spawn_object(x< NUM_RANDOM_ZAPS) { set_mortimer_surprised(0); - objects[o].exists = 0; + delete_object(o); } else objects[o].ai.se.counter++; } @@ -1106,23 +1040,17 @@ void set_mortimer_surprised(int yes) if (mortimer_surprisedcount) { - //12,6 -> 610 -- give mortimer his "surprised" face - map_chgtile(12,6,610); - map_deanimate(12,6); - // deanimate mortimer's hands - map_chgtile(11,6,613); - map_deanimate(11,6); - map_chgtile(13,6,615); - map_deanimate(13,6); + //12,6 -> 610 -- give mortimer his "surprised" face + map_chgtile(12,6,610); + // deanimate mortimer's hands + map_chgtile(11,6,613); map_deanimate(11,6); + map_chgtile(13,6,615); map_deanimate(13,6); } else { - // give mortimer his normal face - map_chgtile(12,6,607); - map_animate(12,6); - // reanimate things - map_animate(11,6); - map_chgtile(13,6,616); - map_animate(13,6); + // give mortimer his normal face again + map_chgtile(12,6,607); + map_chgtile(11,6,613); + map_chgtile(13,6,616); } } diff --git a/src/ai/se.h b/src/ai/se.h new file mode 100644 index 000000000..a67289565 --- /dev/null +++ b/src/ai/se.h @@ -0,0 +1,60 @@ +/* + * se.h + * + * Created on: 06.08.2009 + * Author: gerstrong + */ + +#ifndef SE_H_ +#define SE_H_ + +/* located in game.c */ + +//----------------------[referenced from ai/se.c]--------------------// +void kill_all_intersecting_tile(int mpx, int mpy); +void delete_object(int o); +void killplayer(int theplayer); +unsigned char spawn_object(int x, int y, int otype); + + +/* located in map.c */ + +//----------------------[referenced from ai/se.c]--------------------// +unsigned int getmaptileat(unsigned int x, unsigned int y); +void map_chgtile(unsigned int x,unsigned int y, int newtile); +void map_deanimate(int x, int y); + +//----------------------[referenced from ai/se.c]--------------------// +//void fade(uchar type, uchar rate); + + +/* located in misc.c */ + +//----------------------[referenced from ai/se.c]--------------------// +uint rnd(void); + +/* located in cinematics/blowupworld.c */ + +//----------------------[referenced from ai/se.c]--------------------// +void SetVibrateTime(int vibetime, char pausegame, stLevelControl *levelcontrol); + + +/* located in ai/se.c */ + +//----------------------[referenced from ai/se.c]--------------------// +void se_ai(int o); +void se_extend_plat(int o, bool *p_PlatExtending); +void se_retract_plat(int o, bool *p_PlatExtending); +void spark_ai(int o, int *p_sparks_left); +void se_ankhshield(int o, int episode); +void se_mortimer_arm(int o); +void se_mortimer_spark(int o, stLevelControl *p_levelcontrol); +void se_mortimer_heart(int o, stLevelControl *p_levelcontrol); +void se_mortimer_zapsup(int o, stLevelControl *levelcontrol); +void se_mortimer_leg_left(int o); +void se_mortimer_leg_right(int o); +void se_mortimer_randomzaps(int o); +void set_mortimer_surprised(int yes); + + +#endif /* SE_H_ */ diff --git a/src/ai/vortelite.h b/src/ai/vortelite.h index fb1262b8e..786cb8211 100644 --- a/src/ai/vortelite.h +++ b/src/ai/vortelite.h @@ -1,5 +1,5 @@ void killplayer(int theplayer); -char spawn_object(int x, int y, int otype); +unsigned char spawn_object(int x, int y, int otype); unsigned int getmaptileat(unsigned int x, unsigned int y); diff --git a/src/cinematics/blowupworld.cpp b/src/cinematics/blowupworld.cpp new file mode 100644 index 000000000..9441a0b0c --- /dev/null +++ b/src/cinematics/blowupworld.cpp @@ -0,0 +1,283 @@ +/* + * blowupworld.cpp + * + * Created on: 06.08.2009 + * Author: Caitlin Shaw + * Ported by: Gerstrong + */ + +// cinematic of what happens when you hit the on switch on a tantalus ray +// (episode 2) + +#include "../keen.h" + +#define VIBRATE_MINSTRENGTH 1 +#define VIBRATE_MORTIMERSTRENGTH 4 +#define VIBRATE_MAXSTRENGTH 16 +#define VIBRATE_GETWORSETIME 150 + +int vibe_xamt, vibe_yamt, vibe_xdir, vibe_ydir; +int vibe_strength, vibe_getworsetimer; +void SetVibrateTime(int vibetime, char pausegame, stLevelControl *levelcontrol) +{ + vibe_xamt = vibe_yamt = 0; + if (levelcontrol->episode==3) + vibe_strength = VIBRATE_MORTIMERSTRENGTH; + else + vibe_strength = VIBRATE_MINSTRENGTH; + + vibe_getworsetimer = 0; + levelcontrol->vibratetime = vibetime; + levelcontrol->vibratepause = pausegame; +} + +/*void static vibescroller(void) +{ +int i; + // scroll the map to make it look like it's vibrating + if (vibe_xdir) + for(i=0;i VIBRATE_GETWORSETIME) + { + vibe_getworsetimer = 0; + if (vibe_strength < VIBRATE_MAXSTRENGTH) vibe_strength++; + } + } + else // vibration is over, blow up the world + { + if (levelcontrol.episode==2) // vibration also used in ep3 end level + { + dispmsgstring("EP2_UhOhString", 1); + message_SetDismissalCallback(blow_up_world); + } + } +} +void blow_up_world(void) { endlevel(HIT_TANTALUS_SWITCH); } + + + +// start x,y map scroll position +#define TANTALUS_X 0 +#define TANTALUS_Y 0 +#define TANTALUS_SPRITE 58 + +#define SHOT_SPD_X (21*1.5) +#define SHOT_SPD_Y (9*1.5) + +int shot_x, shot_y, shot_obj, shot_frame, shot_frametimer; +int fire_timer; +char tant_hitearth; +void seq_tantalus_start(void) +{ +int i; +int x,y; + lprintf(">seq_tantalus_start()\n"); + overlay.tantalus = 1; + sound_stop_all(); + + initgame(); + showmapatpos(81, TANTALUS_X, TANTALUS_Y); + + AllPlayersInvisible(); + + // there are no objects in this scene + for(i=0;i 0) + { + fire_timer--; + if (!fire_timer) + { + shot_obj = spawn_object(shot_x, shot_y, OBJ_RAY); + objects[shot_obj].onscreen = 1; + sound_play(SOUND_KEEN_FIRE, PLAY_FORCE); + } + else return; + } + + objects[shot_obj].sprite = TANTALUS_SPRITE + shot_frame; + if (++shot_frametimer > 45) + { + shot_frametimer = 0; + shot_frame ^= 1; + } + + objects[shot_obj].x = shot_x; + objects[shot_obj].y = shot_y; + + tl = getmaptileat((shot_x>>CSF)+(sprites[TANTALUS_SPRITE].xsize/2), \ + (shot_y>>CSF)+(sprites[TANTALUS_SPRITE].ysize/2)); + if (tl==586) + { // it hit center of earth + tant_hitearth = 1; + delete_object(shot_obj); + srnd(300); + spawn_object(shot_x, shot_y, OBJ_EXPLOSION); + sound_play(SOUND_EARTHPOW, PLAY_NOW); + timer = spawnedcount = 0; + } + else + { + shot_x += SHOT_SPD_X; + shot_y += SHOT_SPD_Y; + } +} + +#define EARTHCHUNK_BIG_UP 64 +#define EARTHCHUNK_BIG_DN 66 +#define EARTHCHUNK_SMALL_UP 68 +#define EARTHCHUNK_SMALL_DN 70 +void tant_earthexplode(void) +{ +int i,o; + gamedo_enemyai(); + + if (!timer) + { + if (spawnedcount<16) + o = spawn_object(shot_x+((rnd()%32)< 4) + { + objects[o].sprite = EARTHCHUNK_SMALL_DN; + } + else + { + objects[o].sprite = EARTHCHUNK_SMALL_UP; + } + } + + break; + case 6: + o = spawn_object(shot_x+(16<Control.levelcontrol.episode==1) { - o = spawn_object((((curmapx+1)<<4)+4)<Control.levelcontrol.episode==2) { @@ -343,11 +341,10 @@ int o,x; case 7: // spark (ep2) ball (ep3) if (pCKP->Control.levelcontrol.episode==2) { - o = spawn_object(curmapx<<4<Control.levelcontrol.canexit = 0; // can't exit till spark is shot } - else + else if (pCKP->Control.levelcontrol.episode==3) { o = spawn_object(curmapx<<4<Control.levelcontrol.episode==1) { - o = spawn_object(((curmapx<<4)-4)<Control.levelcontrol.episode==3) { @@ -406,7 +400,7 @@ int o,x; if (pCKP->Control.levelcontrol.episode==3) { o = spawn_object(curmapx<<4<Control.levelcontrol.episode==3) { o = spawn_object(curmapx<<4<ftextOut("Object of type %d could not be created at %d,%d (out of object slots)
",otype,x,y); return 0; } @@ -681,10 +680,26 @@ int i; } } -// anything (players/enemies) occupying the map tile at [mpx,mpy] is killed -/*void kill_all_intersecting_tile(int mpx, int mpy) +void killobject(int o) { -int xpix,ypix; + if (objects[o].exists) + { + if (objects[o].type==OBJ_PLAYER) + { + killplayer(o); + } + else + { + if (objects[o].zapped < 500 && objects[o].canbezapped) + objects[o].zapped += 500; + } + } +} + +// anything (players/enemies) occupying the map tile at [mpx,mpy] is killed +void kill_all_intersecting_tile(int mpx, int mpy) +{ +unsigned int xpix,ypix; int i; xpix = mpx<Control.levelcontrol.success = success; - pCKP->Control.levelcontrol.tobonuslevel = 0; + levelcontrol->success = reason_for_leaving; + levelcontrol->tobonuslevel = 0; } } @@ -1211,27 +1226,31 @@ void procgoodie(int t, int mpx, int mpy, int theplayer, stCloneKeenPlus *pCKP) case 11: player[theplayer].inventory.HasJoystick = 1; + pCKP->Control.levelcontrol.canexit = 1; g_pSound->playSound(SOUND_GET_PART, PLAY_NOW); break; case 12: player[theplayer].inventory.HasBattery = 1; + pCKP->Control.levelcontrol.canexit = 1; g_pSound->playSound(SOUND_GET_PART, PLAY_NOW); break; case 13: player[theplayer].inventory.HasVacuum = 1; + pCKP->Control.levelcontrol.canexit = 1; g_pSound->playSound(SOUND_GET_PART, PLAY_NOW); break; case 14: player[theplayer].inventory.HasFuel = 1; + pCKP->Control.levelcontrol.canexit = 1; g_pSound->playSound(SOUND_GET_PART, PLAY_NOW); break; // in-level teleporter // (in level13.ck1 that takes you to the bonus level) case 24: - endlevel(0, pCKP); + endlevel(0, &(pCKP->Control.levelcontrol) ); pCKP->Control.levelcontrol.tobonuslevel = 1; break; diff --git a/src/gamedo.cpp b/src/gamedo.cpp index 7feabac5a..f0854a062 100644 --- a/src/gamedo.cpp +++ b/src/gamedo.cpp @@ -14,6 +14,7 @@ #include "sdl/CTimer.h" #include "sdl/CInput.h" #include "sdl/sound/CSound.h" +#include "CLogFile.h" #include "CGraphics.h" #include "externals.h" #include "StringUtils.h" @@ -69,7 +70,7 @@ unsigned int msb, lsb; if (byt & 32)player[0].playcontrol[PA_STATUS] = 1; if (byt & 64) { // demo STOP command - if (fade.mode!=FADE_GO) endlevel(1, pCKP); + if (fade.mode!=FADE_GO) endlevel(1, &(pCKP->Control.levelcontrol) ); } } else @@ -83,12 +84,12 @@ unsigned int msb, lsb; { if (g_pInput->getPressedKey(i)) { - if (fade.mode!=FADE_GO) endlevel(0, pCKP); + if (fade.mode!=FADE_GO) endlevel(0, &(pCKP->Control.levelcontrol) ); } } if (g_pInput->getPressedCommand(IC_STATUS)) { - if (fade.mode!=FADE_GO) endlevel(0, pCKP); + if (fade.mode!=FADE_GO) endlevel(0, &(pCKP->Control.levelcontrol) ); } return; @@ -262,7 +263,7 @@ int i, topobj; case OBJ_TANK: tank_ai(i, pCKP->Control.levelcontrol.hardmode); break; case OBJ_RAY: ray_ai(i, pCKP, pCKP->Control.levelcontrol); break; case OBJ_DOOR: door_ai(i, pCKP->Control.levelcontrol.cepvars.DoorOpenDir); break; - //case OBJ_ICECANNON: icecannon_ai(i); break; TODO: Add this AI + case OBJ_ICECANNON: icecannon_ai(i); break; case OBJ_ICECHUNK: icechunk_ai(i); break; case OBJ_ICEBIT: icebit_ai(i); break; case OBJ_TELEPORTER: teleporter_ai(i, pCKP->Control.levelcontrol); break; @@ -277,7 +278,7 @@ int i, topobj; pCKP->Control.levelcontrol.hardmode); break; case OBJ_EXPLOSION: explosion_ai(i); break; case OBJ_EARTHCHUNK: earthchunk_ai(i); break; - //case OBJ_SPARK: spark_ai(i); break; TODO: Add this AI + case OBJ_SPARK: spark_ai(i, &(pCKP->Control.levelcontrol.sparks_left) ); break; //KEEN3 case OBJ_FOOB: foob_ai(i, pCKP); break; case OBJ_NINJA: ninja_ai(i, pCKP); break; @@ -296,6 +297,7 @@ int i, topobj; case OBJ_DEMOMSG: break; default: //crash("gamedo_enemy_ai: Object %d is of invalid type %d\n", i, objects[i].type); + g_pLogFile->ftextOut("gamedo_enemy_ai: Object %d is of invalid type %d\n", i, objects[i].type); break; } @@ -819,7 +821,7 @@ int i; // F9 - exit level immediately if(g_pInput->getPressedKey(KF9)) { - endlevel(1, pCKP); + endlevel(1, &(pCKP->Control.levelcontrol) ); } // F6 - onscreen debug--toggle through debug/radar/off if(g_pInput->getPressedKey(KF6)) diff --git a/src/gamepdo.cpp b/src/gamepdo.cpp index 80feaed10..9ac7e90ea 100644 --- a/src/gamepdo.cpp +++ b/src/gamepdo.cpp @@ -165,7 +165,7 @@ void gamepdo_dieanim(int cp, stCloneKeenPlus *pCKP) } else { - endlevel(0,pCKP); + endlevel(0,&(pCKP->Control.levelcontrol)); } } } @@ -243,7 +243,7 @@ void gamepdo_ProcessInput(unsigned int cp, stCloneKeenPlus *pCKP) if (p_levelcontrol->level_done != LEVEL_DONE_FADEOUT) { p_levelcontrol->level_done = LEVEL_DONE_FADEOUT; - endlevel(1, pCKP); + endlevel(1, &(pCKP->Control.levelcontrol)); } } else if (p_levelcontrol->level_done_timer > LEVEL_DONE_STOPWALKING_TIME) diff --git a/src/gm_pdowm.cpp b/src/gm_pdowm.cpp index c3fdd9e01..8a8c3a4be 100644 --- a/src/gm_pdowm.cpp +++ b/src/gm_pdowm.cpp @@ -315,7 +315,7 @@ p_levelcontrol = &(pCKP->Control.levelcontrol); default: // a regular level p_levelcontrol->chglevelto = (lvl & 0x7fff); - endlevel(1, pCKP); + endlevel(1, &(pCKP->Control.levelcontrol)); g_pMusicPlayer->stop(); g_pSound->playStereofromCoord(SOUND_ENTER_LEVEL, PLAY_NOW, objects[player[cp].useObject].scrx); diff --git a/src/include/declarations.h b/src/include/declarations.h index 3e5e97d3c..96d50d45b 100644 --- a/src/include/declarations.h +++ b/src/include/declarations.h @@ -63,7 +63,21 @@ struct stLevelControl unsigned int level_done, level_done_timer; unsigned int level_finished_by; // index of player that finished level unsigned int exitXpos; + + // for ep2: how many sparks (tantalus ray machines) are left + // you must destroy the tantalus ray generator before exiting + int sparks_left; + + // if 1, a moving platform is currently extending/retracting (ep2) + bool PlatExtending; + + // if > 0, the screen will shake and it will decrement each frame. + // used when you push a switch on a tantalus ray (ep2), and Mortimer's machine + int vibratetime; + // if 1, then while vibrating the game will be paused + char vibratepause; + // as long as we only have POD stLevelControl() { memset(this, 0, sizeof(stLevelControl)); } diff --git a/src/include/enemyai.h b/src/include/enemyai.h index 1cc6c1687..b27600055 100644 --- a/src/include/enemyai.h +++ b/src/include/enemyai.h @@ -15,6 +15,7 @@ void tank_ai(int o, bool hardmode); void ray_ai(int o, stCloneKeenPlus *pCKP, stLevelControl levelcontrol); void icechunk_ai(int o); void icebit_ai(int o); +void icecannon_ai(int o); void door_ai(int o, char DoorOpenDir); void teleporter_ai(int o, stLevelControl levelcontrol); void rope_ai(int o); @@ -26,8 +27,9 @@ void vortelite_ai(int o, bool darkness); void se_ai(int o, stCloneKeenPlus *pCKP); void explosion_ai(int o); void earthchunk_ai(int o); +void spark_ai(int o, int *p_sparks_left); // ep3 -void foob_ai(int o, stCloneKeenPlus *pCKP); +void foob_ai(int o, bool hardmode); void ninja_ai(int o, stCloneKeenPlus *pCKP); void meep_ai(int o, stLevelControl levelcontrol); void sndwave_ai(int o, stCloneKeenPlus *pCKP); diff --git a/src/include/game.h b/src/include/game.h index 3ed7257b1..dce2d2cd4 100644 --- a/src/include/game.h +++ b/src/include/game.h @@ -1,10 +1,13 @@ // various states we go through when a level is completed // to do the walking out the exit door animation -#define LEVEL_NOT_DONE 0 // not completed -#define LEVEL_DONE_WALK 1 // walking through exit door -#define LEVEL_DONE_WAIT 2 // finished walk through door, wait a bit -#define LEVEL_DONE_FADEOUT 3 // fading out -#define LEVEL_COMPLETE 4 // on to the next level! +enum levelstate +{ +LEVEL_NOT_DONE, // not completed +LEVEL_DONE_WALK, // walking through exit door +LEVEL_DONE_WAIT, // finished walk through door, wait a bit +LEVEL_DONE_FADEOUT, // fading out +LEVEL_COMPLETE // on to the next level! +}; // width of the player sprite, used for walking "behind" the exit door frame #define PLAYERSPRITE_WIDTH 16 @@ -34,10 +37,12 @@ #define ICECANNON_FIRE_FREQ 400 // direction defines used for various things -#define RIGHT 0 -#define LEFT 1 -#define UP 2 -#define DOWN 3 +enum directions{ +RIGHT, +LEFT, +UP, +DOWN +}; #define ANKH_SHIELD_FRAME 61 @@ -191,10 +196,43 @@ #define DOOR_GREEN 4 #define DOOR_BLUE 5 -#define DOOR_RED_SPRITE (MAX_SPRITES-5) -#define DOOR_GREEN_SPRITE (MAX_SPRITES-4) -#define DOOR_BLUE_SPRITE (MAX_SPRITES-3) -#define DOOR_YELLOW_SPRITE (MAX_SPRITES-2) +// special sprites (they either weren't in the game originally, +// or are used for internal engine stuff). +#define BLANKSPRITE (MAX_SPRITES-1) +#define DOOR_YELLOW_SPRITE (MAX_SPRITES-2) // opening door (yellow) +#define DOOR_BLUE_SPRITE (MAX_SPRITES-3) +#define DOOR_GREEN_SPRITE (MAX_SPRITES-4) +#define DOOR_RED_SPRITE (MAX_SPRITES-5) +#define PT5000_SPRITE (MAX_SPRITES-6) // rising bonus pts (+5000) +#define PT1000_SPRITE (MAX_SPRITES-7) +#define PT500_SPRITE (MAX_SPRITES-8) +#define PT200_SPRITE (MAX_SPRITES-9) +#define PT100_SPRITE (MAX_SPRITES-10) +#define PT1UP_SPRITE (MAX_SPRITES-11) // rising 1up bonus +#define PTCARDY_SPRITE (MAX_SPRITES-12) // rising access card +#define PTCARDR_SPRITE (MAX_SPRITES-13) +#define PTCARDG_SPRITE (MAX_SPRITES-14) +#define PTCARDB_SPRITE (MAX_SPRITES-15) +#define SHOTUP_SPRITE (MAX_SPRITES-16) // rising single shot (ep3) +#define GUNUP_SPRITE (MAX_SPRITES-17) // rising ray gun +#define YORPSHIELD_SPRITE (MAX_SPRITES-18) // invincibility force field, used in editor and in "yorps have forcefields" mode +#define VERSION_SPRITE (MAX_SPRITES-19) // version text shown in lower-right corner at startup +#define DEMOBOX_SPRITE (MAX_SPRITES-20) // says "Demo" +#define OSD_LIVES_SPRITE (MAX_SPRITES-21) +#define OSD_AMMO_SPRITE (MAX_SPRITES-22) +#define OSD_YORPS_SPRITE (MAX_SPRITES-23) +#define TITLE_LOGO1_SPRITE (MAX_SPRITES-24) // left half of the logo +#define TITLE_LOGO2_SPRITE (MAX_SPRITES-25) // right half of the logo +#define TITLE_FLOOR_SPRITE (MAX_SPRITES-26) // more stuff for the logo +#define TITLE_VORT_SPRITE (MAX_SPRITES-27) +#define ARROWLR_SPRITE (MAX_SPRITES-28) // these are directional arrows used in the editor +#define ARROWUD_SPRITE (MAX_SPRITES-29) +#define ARROWUR_SPRITE (MAX_SPRITES-30) +#define ARROWUL_SPRITE (MAX_SPRITES-31) +#define ARROWU_SPRITE (MAX_SPRITES-32) +#define ARROWD_SPRITE (MAX_SPRITES-33) +#define LAST_SPECIAL_SPRITE (MAX_SPRITES-34) + #define YORPSTATUEHEAD 22 #define YORPSTATUEHEADUSED 485 #define YORPSTATUEBOTTOM 22 @@ -224,7 +262,7 @@ void open_door(int doortile, int doorsprite, int mpx, int mpy, int cp, stCloneKe void killplayer(int theplayer); void PlayerTouchedExit(int theplayer, stCloneKeenPlus *pCKP); -void endlevel(int success, stCloneKeenPlus *pCKP); +void endlevel(int reason_for_leaving, stLevelControl *levelcontrol); void SetGameOver(stCloneKeenPlus *pCKP); char checkissolidl(int x, int y, int cp, stCloneKeenPlus *pCKP); diff --git a/src/keen.h b/src/keen.h index a11756d3b..8155399c6 100644 --- a/src/keen.h +++ b/src/keen.h @@ -30,7 +30,7 @@ #define WM_MAP_NUM 80 -#define MAX_SPRITES 300 +#define MAX_SPRITES 500 #define MAX_FONT 256 #define MAX_BITMAPS 20 @@ -89,6 +89,8 @@ typedef struct stFade #define WORLD_MAP 80 #define FINAL_MAP 16 +// values returned by gameloop() + #define LVLC_NOCOMMAND 0 #define LVLC_CHANGE_LEVEL 1 #define LVLC_END_SEQUENCE 2 @@ -96,6 +98,8 @@ typedef struct stFade #define LVLC_TANTALUS_RAY 4 // switch on tantalus ray pressed #define LVLC_START_LEVEL 5 +#define WON_LEVEL LVLC_CHANGE_LEVEL + typedef struct stMap { @@ -157,297 +161,8 @@ struct stString unsigned int attrvalues[MAX_ATTRIBUTES+1]; }; -/* Structs used for different enemies data, these are in a union */ -struct stYorpData -{ - unsigned char state; - - unsigned char looktimes,lookposition; - unsigned char timer, dietimer; - unsigned char walkframe; - unsigned int dist_traveled; - signed int yorpdie_inertia_y; - - unsigned char movedir; -}; - -struct stGargData -{ - unsigned char state; - - unsigned char looktimes,lookframe; - unsigned char timer, dietimer, keenonsameleveltimer; - unsigned char about_to_charge; - unsigned char walkframe; - unsigned int dist_traveled; - signed int gargdie_inertia_y; - - unsigned char movedir; - unsigned char detectedPlayer, detectedPlayerIndex; -}; - -struct stVortData -{ - unsigned char state; - - unsigned char timer,timer2; - unsigned int animtimer; - unsigned char palflashtimer, palflashamt; - unsigned char frame; - unsigned int dist_traveled; - signed int inertiay; - - char ep1style; // episode 1 style four-shots-to-kill - - unsigned char movedir; - // these hold the animation frames indexes since they're - // different for each episode - int WalkLeftFrame; - int WalkRightFrame; - int LookFrame; - int JumpRightFrame; - int JumpLeftFrame; - int DyingFrame; - int DeadFrame; -}; - -struct stVortEliteData -{ - unsigned char state; - - unsigned char timer,timer2; - unsigned int animtimer; - unsigned char frame; - signed int inertiay; - unsigned char movedir; - unsigned int timesincefire; - unsigned int running; - - int dist_traveled; -}; - -struct stButlerData -{ - unsigned char state; - unsigned char timer,animtimer; - unsigned char frame; - unsigned int dist_traveled; - - unsigned char movedir; -}; - -struct stTankData -{ - unsigned char state; - - unsigned char timer,animtimer; - unsigned char frame; - unsigned int dist_traveled; - - unsigned char movedir; - - int ponsameleveltime; - unsigned char alreadyfiredcauseonsamelevel; - unsigned char fireafterlook; - - unsigned char detectedPlayer; // 1 if player on same level - unsigned char detectedPlayerIndex; // index of player that was detected - - // for tank2 - unsigned int timetillnextshot; - unsigned int firetimes; - unsigned int timetillcanfire; - unsigned int timetillcanfirecauseonsamelevel; -}; - -struct stRayData -{ - char state; - char direction; - char zapzottimer; - - char dontHitEnable; - unsigned int dontHit; // index of an object type ray will not harm - - // for soundwave - int animframe, animtimer; - int offscreentime; - - // for earth chunks - int baseframe; -}; - -struct stDoorData -{ - char timer; - char distance_traveled; -}; - -struct stIceChunk -{ - char movedir; - char state; - unsigned int originalX, originalY; - int timer; -}; - -struct stTeleportData -{ - char animtimer; - char animframe; - char numframechanges; - - char direction; - int whichplayer; - unsigned int destx; - signed int desty; - - int baseframe; - int idleframe; - - char NoExitingTeleporter; - char snap; - - char fadeamt; - char fadetimer; -}; - -struct stRopeData -{ - char state; - int droptimer; - int droptimes; - int stoneX, stoneY; - int vortboss; -}; - -struct stWalkerData -{ - unsigned char state; - - unsigned char animtimer, dietimer; - unsigned char walkframe; - signed int walkerdie_inertia_y; - int fallinctimer,fallspeed; - - unsigned char walkdir; - unsigned char kickedplayer[MAX_PLAYERS]; -}; - -struct stPlatformData -{ - unsigned char state; - unsigned char animframe; - unsigned int animtimer; - unsigned int waittimer; - - unsigned char movedir; - unsigned char kickedplayer[MAX_PLAYERS]; -}; - -struct stSEData -{ - unsigned int type; - - unsigned char state; - unsigned int timer; - unsigned int platx, platy; - unsigned int bgtile; - unsigned int dir; - - int counter,destroytiles; - unsigned int frame; - int mx,my; - int blowx,blowy; -}; - -struct stBabyData -{ - char state; - char dir; - signed int inertia_x, inertia_y; - int jumpdectimer, xdectimer; - int jumpdecrate; - int dietimer; - - char walkframe; - int walktimer; -}; - -struct stFoobData -{ - char state; - char dir; - - int animframe, animtimer; - int OnSameLevelTime; - int OffOfSameLevelTime; - int spooktimer; - int SpookedByWho; -}; - -struct stNinjaData -{ - char state; - char dir; - - int animframe, animtimer; - unsigned int timetillkick; - - signed int XInertia, YInertia; - unsigned int XFrictionTimer, YFrictionTimer; - unsigned int XFrictionRate, YFrictionRate; - int KickMoveTimer; - int isdying; - int dietimer; -}; - -typedef struct stMotherData -{ - char state; - char dir; - char hittimes; - - int animframe, animtimer; - int timer; -} stMotherData; - -typedef struct stMeepData -{ - char state; - char dir; - - int animframe, animtimer; - int timer; -} stMeepData; - -typedef struct stBallJackData -{ - char dir; - int animframe, animtimer; - int speed; -} stBallJackData; - -#define NESSIETRAILLEN 5 -typedef struct stNessieData -{ - char state; - char leftrightdir, updowndir; - unsigned int baseframe; - - unsigned int tiletrailX[NESSIETRAILLEN+1]; - unsigned int tiletrailY[NESSIETRAILLEN+1]; - int tiletrailhead; - - char animframe, animtimer; - unsigned int destx, desty; - - unsigned int pausetimer; - unsigned int pausex, pausey; - - unsigned int mortimer_swim_amt; - unsigned int mounted[MAX_PLAYERS]; -} stNessieData; +// structures for each AI module's data +#include "ai/enemydata.h" // and the object structure containing the union of the above structs typedef struct stObject @@ -496,30 +211,30 @@ typedef struct stObject // what kind of object it is union ai { - // ep1 - stYorpData yorp; - stGargData garg; - stVortData vort; - stButlerData butler; - stTankData tank; - stRayData ray; - stDoorData door; - stIceChunk icechunk; - stTeleportData teleport; - stRopeData rope; - // ep2 - stWalkerData walker; - stPlatformData platform; - stVortEliteData vortelite; - stSEData se; - stBabyData baby; - // ep3 - stFoobData foob; - stNinjaData ninja; - stMeepData meep; - stMotherData mother; - stBallJackData bj; - stNessieData nessie; + // ep1 + stYorpData yorp; + stGargData garg; + stVortData vort; + stButlerData butler; + stTankData tank; + stRayData ray; + stDoorData door; + stIceChunk icechunk; + stTeleportData teleport; + stRopeData rope; + // ep2 + stWalkerData walker; + stPlatformData platform; + stVortEliteData vortelite; + stSEData se; + stBabyData baby; + // ep3 + stFoobData foob; + stNinjaData ninja; + stMeepData meep; + stMotherData mother; + stBallJackData bj; + stNessieData nessie; } ai; unsigned char erasedata[64][64]; // backbuffer to erase this object } stObject; @@ -537,43 +252,56 @@ typedef struct stAnimTile } stAnimTile; #define NUM_OBJ_TYPES 40 + +enum enumerated_Objects{ // ** objects from KEEN1 -#define OBJ_YORP 1 -#define OBJ_GARG 2 -#define OBJ_VORT 3 -#define OBJ_BUTLER 4 -#define OBJ_TANK 5 -#define OBJ_RAY 6 // keen's raygun blast -#define OBJ_DOOR 7 // an opening door -#define OBJ_ICECHUNK 8 // ice chunk from ice cannon -#define OBJ_ICEBIT 9 // piece of shattered ice chunk -#define OBJ_PLAYER 10 -#define OBJ_TELEPORTER 11 // world map teleporter -#define OBJ_ROPE 12 +OBJ_YORP, +OBJ_GARG, +OBJ_VORT, +OBJ_BUTLER, +OBJ_TANK, +OBJ_RAY, // keen's raygun blast +OBJ_DOOR, // an opening door +OBJ_ICECHUNK, // ice chunk from ice cannon +OBJ_ICEBIT, // piece of shattered ice chunk +OBJ_PLAYER, +OBJ_TELEPORTER, // world map teleporter +OBJ_ROPE, // ** objects from KEEN2 (some of these are in ep3 as well) -#define OBJ_WALKER 13 -#define OBJ_TANKEP2 14 -#define OBJ_PLATFORM 15 -#define OBJ_VORTELITE 16 -#define OBJ_SECTOREFFECTOR 17 -#define OBJ_BABY 18 -#define OBJ_EXPLOSION 19 -#define OBJ_EARTHCHUNK 20 +OBJ_WALKER, +OBJ_TANKEP2, +OBJ_PLATFORM, +OBJ_VORTELITE, +OBJ_SECTOREFFECTOR, +OBJ_BABY, +OBJ_EXPLOSION, +OBJ_EARTHCHUNK, +OBJ_SPARK, // ** objects from KEEN3 -#define OBJ_FOOB 21 -#define OBJ_NINJA 22 -#define OBJ_MEEP 23 -#define OBJ_SNDWAVE 24 -#define OBJ_MOTHER 25 -#define OBJ_FIREBALL 26 -#define OBJ_BALL 27 -#define OBJ_JACK 28 -#define OBJ_PLATVERT 29 -#define OBJ_NESSIE 30 +OBJ_FOOB, +OBJ_NINJA, +OBJ_MEEP, +OBJ_SNDWAVE, +OBJ_MOTHER, +OBJ_FIREBALL, +OBJ_BALL, +OBJ_JACK, +OBJ_PLATVERT, +OBJ_NESSIE, -#define OBJ_DEMOMSG 31 +OBJ_DEMOMSG, + +// the guns that shoot periodically +OBJ_AUTORAY, +OBJ_AUTORAY_V, +OBJ_ICECANNON, + +OBJ_GOTPOINTS, //this thing is the rising point numbers +OBJ_GHOST, //ghosted object from map editor +LAST_OBJ_TYPE +}; // default sprites...when an object is spawned it's sprite is set to this // sprite. the object AI will immediately reset the sprite frame, so it @@ -594,8 +322,9 @@ typedef struct stAnimTile #define OBJ_RAY_DEFSPRITE_EP3 102 #define OBJ_ICECHUNK_DEFSPRITE 112 #define OBJ_ICEBIT_DEFSPRITE 113 +#define OBJ_ROPE_DEFSPRITE 114 #define OBJ_TELEPORTER_DEFSPRITE 180 -#define OBJ_ROPE_DEFSPRITE 184 + #define OBJ_PLATFORM_DEFSPRITE_EP2 126 #define OBJ_PLATFORM_DEFSPRITE_EP3 107 @@ -615,6 +344,7 @@ typedef struct stAnimTile #define OBJ_BABY_DEFSPRITE_EP2 52 #define OBJ_BABY_DEFSPRITE_EP3 51 +#define OBJ_SPARK_DEFSPRITE_EP2 128 // some directions (mostly for OBJ_ICECHUNK and OBJ_ICEBIT) #define DUPRIGHT 0 @@ -789,20 +519,18 @@ typedef struct stShipQueue #define TILE_EXTENDING_PLATFORM 270 // "Sector Effector" types -#define SE_EXTEND_PLATFORM 1 -#define SE_RETRACT_PLATFORM 2 -#define SE_SPARK 3 -#define SE_GUN_VERT 4 -#define SE_GUN_RIGHT 5 -#define SE_ANKHSHIELD 6 -#define SE_ICECANNON 7 -#define SE_MORTIMER_ARM 8 -#define SE_MORTIMER_LEG_LEFT 9 -#define SE_MORTIMER_LEG_RIGHT 10 -#define SE_MORTIMER_SPARK 11 -#define SE_MORTIMER_HEART 12 -#define SE_MORTIMER_ZAPSUP 13 -#define SE_MORTIMER_RANDOMZAPS 14 +enum sector_effector_type{ +SE_EXTEND_PLATFORM, +SE_RETRACT_PLATFORM, +SE_ANKHSHIELD, +SE_MORTIMER_ARM, +SE_MORTIMER_LEG_LEFT, +SE_MORTIMER_LEG_RIGHT, +SE_MORTIMER_SPARK, +SE_MORTIMER_HEART, +SE_MORTIMER_ZAPSUP, +SE_MORTIMER_RANDOMZAPS +}; // animation rate of animated tiles #define ANIM_TILE_TIME 40 diff --git a/src/vorticon/CEGASprit.cpp b/src/vorticon/CEGASprit.cpp index a1a3bb32c..d97235008 100644 --- a/src/vorticon/CEGASprit.cpp +++ b/src/vorticon/CEGASprit.cpp @@ -117,7 +117,7 @@ bool CEGASprit::loadData(const std::string& filename, bool compresseddata) // TODO: Create surfaces which can be blitted directly to the blit surface or maybe screen. // load the image data - sprites = new stSprite[301]; + sprites = new stSprite[MAX_SPRITES+1]; char c; for(int p=0 ; p<4 ; p++) {