git-svn-id: https://clonekeenplus.svn.sourceforge.net/svnroot/clonekeenplus/cgenius/trunk@189 4df4b0f3-56ce-47cb-b001-ed939b7d65a6
1514 lines
42 KiB
C++
1514 lines
42 KiB
C++
/* GAME.C
|
|
Main and miscellaneous functions for in-game, contains the main
|
|
game loop, etc.
|
|
*/
|
|
|
|
#include "keen.h"
|
|
#include "demobox.h"
|
|
#include "include/game.h"
|
|
#include "sdl/CVideoDriver.h"
|
|
#include "include/menu.h"
|
|
#include "include/misc.h"
|
|
#include "include/gamedo.h"
|
|
#include "include/gamepdo.h"
|
|
#include "include/gm_pdowm.h"
|
|
#include "include/eseq_ep3.h"
|
|
#include "sdl/CTimer.h"
|
|
#include "sdl/CInput.h"
|
|
#include "sdl/sound/CSound.h"
|
|
#include "include/enemyai.h"
|
|
#include "hqp/CMusic.h"
|
|
#include "vorticon/CPlayer.h"
|
|
#include "vorticon/CHighScores.h"
|
|
#include "hqp/CHQBitmap.h"
|
|
#include "CLogFile.h"
|
|
char otherplayer;
|
|
|
|
// TODO: seperate status boxes for the different players
|
|
// TODO: Your Ship Needs These Parts in multiplayer
|
|
|
|
int playerbaseframes[MAX_PLAYERS] = {0,0,0,0,0,0,0,0};
|
|
|
|
unsigned int max_scroll_x, max_scroll_y;
|
|
char debugmode=0,acceleratemode=0;
|
|
|
|
// and this is where the magic happens
|
|
void gameloop(stCloneKeenPlus *pCKP)
|
|
{
|
|
unsigned int i;
|
|
|
|
int enter,lastquit;
|
|
|
|
// Enable the yorp/garg statues elders switches animations
|
|
for(int i=0 ; i<numtiles ; i++)
|
|
if(TileProperty[i][BEHAVIOR] == 22)
|
|
TileProperty[i][ANIMATION] = 4;
|
|
|
|
if (player[0].x==0 || player[0].y==0)
|
|
|
|
{
|
|
crashflag=1;
|
|
crashflag2 = pCKP->Control.levelcontrol.curlevel;
|
|
crashflag3 = pCKP->Control.levelcontrol.episode;
|
|
why_term_ptr = "No player start position! (flag2=pCKP->Control.levelcontrol.curlevel, flag3=pCKP->Control.levelcontrol.episode)";
|
|
}
|
|
|
|
if (!loadinggame)
|
|
{
|
|
gameloop_initialize(pCKP);
|
|
}
|
|
else
|
|
{
|
|
loadinggame = 0;
|
|
fade.mode = FADE_GO;
|
|
fade.dir = FADE_IN;
|
|
fade.curamt = 0;
|
|
fade.fadetimer = 0;
|
|
fade.rate = FADE_NORM;
|
|
}
|
|
|
|
// fire all guns immediately first time around
|
|
gunfiretimer = (gunfirefreq+1);
|
|
|
|
// if this is Mortimer's Castle, fade in and do the conversation
|
|
// with Mortimer.
|
|
if (pCKP->Control.levelcontrol.episode==3 && pCKP->Control.levelcontrol.curlevel==16)
|
|
{
|
|
for(i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
if (player[i].isPlaying) gamepdo_SelectFrame(i);
|
|
}
|
|
|
|
do
|
|
{
|
|
gamedo_fades();
|
|
gamedo_AnimatedTiles();
|
|
g_pInput->pollEvents();
|
|
g_pTimer->SpeedThrottle();
|
|
gamedo_RenderScreen(pCKP);
|
|
} while(fade.mode!=FADE_COMPLETE /*&& !immediate_keytable[KQUIT]*/);
|
|
|
|
eseq3_Mortimer(pCKP);
|
|
}
|
|
|
|
lastquit = 1;
|
|
g_pInput->flushKeys(); // The Windows need that. I don't know why!
|
|
|
|
// Now, we are ready to loop the game scenes (map and level)
|
|
// Let's create the player objects
|
|
do
|
|
{
|
|
if (primaryplayer==1) otherplayer = 0; else otherplayer = 1;
|
|
|
|
#ifdef NETWORK_PLAY
|
|
// if (numplayers>1) net_getdata();
|
|
if (is_server)
|
|
{
|
|
Net_Server_Run();
|
|
}
|
|
else if (is_client)
|
|
{
|
|
Net_Client_Run();
|
|
}
|
|
#endif
|
|
|
|
gamedo_fades();
|
|
|
|
// periodically make all enemy gun fixtures fire (in ep3)
|
|
// (also ice cannons in ep1) we do this in a global variable
|
|
// so they're all in sync. when gunfiretimer==0 all gun SE
|
|
// objects will fire.
|
|
if (gunfiretimer > gunfirefreq)
|
|
{
|
|
gunfiretimer = 0;
|
|
}
|
|
else gunfiretimer++;
|
|
|
|
// gather input and copy to player[].keytable[] structures
|
|
gamedo_getInput(pCKP);
|
|
|
|
// run the player behaviour for each player in the game
|
|
if (!map.isworldmap)
|
|
{
|
|
for(i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
if (player[i].isPlaying) gamepdo_HandlePlayer(i, pCKP);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
if (player[i].isPlaying) gamepdo_wm_HandlePlayer(i, pCKP);
|
|
}
|
|
}
|
|
|
|
gamedo_AnimatedTiles();
|
|
gamedo_enemyai(pCKP);
|
|
|
|
gamedo_HandleFKeys(pCKP);
|
|
|
|
/* scroll triggers */
|
|
if (!pCKP->Control.levelcontrol.gameovermode && pCKP->Control.levelcontrol.level_done==LEVEL_NOT_DONE)
|
|
{
|
|
ScreenIsScrolling = 0;
|
|
if (gamedo_ScrollTriggers(primaryplayer)) ScreenIsScrolling = 1;
|
|
}
|
|
|
|
|
|
|
|
// do frameskipping, and render/blit the screen if it's time
|
|
gamedo_frameskipping(pCKP);
|
|
|
|
// when we complete a fade out flag to exit the game loop
|
|
if (fade.mode==FADE_COMPLETE)
|
|
{
|
|
if (fade.dir==FADE_OUT)
|
|
{
|
|
demomode = DEMO_NODEMO;
|
|
pCKP->Control.levelcontrol.level_done = LEVEL_COMPLETE;
|
|
pCKP->Control.levelcontrol.command = LVLC_CHANGE_LEVEL;
|
|
if (pCKP->Control.levelcontrol.curlevel != WM_MAP_NUM)
|
|
{ // exiting a level, going back to world map
|
|
for(i=0;i<numplayers;i++)
|
|
{
|
|
player[i].inventory.HasCardYellow = 0;
|
|
player[i].inventory.HasCardBlue = 0;
|
|
player[i].inventory.HasCardGreen = 0;
|
|
player[i].inventory.HasCardRed = 0;
|
|
}
|
|
|
|
if (pCKP->Control.levelcontrol.success==1)
|
|
{ // mark level as completed on world map
|
|
pCKP->Control.levelcontrol.levels_completed[pCKP->Control.levelcontrol.curlevel] = 1;
|
|
}
|
|
pCKP->Control.levelcontrol.chglevelto = WM_MAP_NUM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fade.mode = NO_FADE;
|
|
}
|
|
}
|
|
|
|
// when walking through the exit door don't show keen's sprite past
|
|
// the door frame (so it looks like he walks "through" the door)
|
|
if (pCKP->Control.levelcontrol.level_done==LEVEL_DONE_WALK)
|
|
{
|
|
gamepdo_walkbehindexitdoor(pCKP->Control.levelcontrol.level_finished_by, pCKP);
|
|
}
|
|
|
|
// allow enter to return to main menu
|
|
// if we're in game over mode
|
|
|
|
enter = (g_pInput->getPressedCommand(IC_STATUS));
|
|
if (pCKP->Control.levelcontrol.gameovermode)
|
|
{
|
|
if (enter)
|
|
{
|
|
int cities=0;
|
|
CHighScores *HighScoreTable;
|
|
|
|
if (pCKP->Control.levelcontrol.levels_completed[4]) cities++;
|
|
if (pCKP->Control.levelcontrol.levels_completed[6]) cities++;
|
|
if (pCKP->Control.levelcontrol.levels_completed[7]) cities++;
|
|
if (pCKP->Control.levelcontrol.levels_completed[13]) cities++;
|
|
if (pCKP->Control.levelcontrol.levels_completed[11]) cities++;
|
|
if (pCKP->Control.levelcontrol.levels_completed[9]) cities++;
|
|
if (pCKP->Control.levelcontrol.levels_completed[15]) cities++;
|
|
if (pCKP->Control.levelcontrol.levels_completed[16]) cities++;
|
|
|
|
HighScoreTable = new CHighScores(pCKP);
|
|
|
|
bool extras[4] = {false,false,false,false};
|
|
|
|
// check inventory or saved cities
|
|
if(pCKP->Control.levelcontrol.episode == 1)
|
|
{
|
|
if(player[0].inventory.HasJoystick)
|
|
extras[0] = true;
|
|
if(player[0].inventory.HasBattery)
|
|
extras[1] = true;
|
|
if(player[0].inventory.HasVacuum)
|
|
extras[2] = true;
|
|
if(player[0].inventory.HasFuel)
|
|
extras[3] = true;
|
|
}
|
|
|
|
HighScoreTable->writeHighScore((int)player[0].inventory.score,extras,cities);
|
|
|
|
HighScoreTable->showHighScore();
|
|
|
|
delete HighScoreTable;
|
|
|
|
|
|
if (fade.mode!=FADE_GO && fade.dir!=FADE_OUT)
|
|
{
|
|
fade.dir = FADE_OUT;
|
|
fade.curamt = PAL_FADE_SHADES;
|
|
fade.fadetimer = 0;
|
|
fade.rate = FADE_NORM;
|
|
fade.mode = FADE_GO;
|
|
}
|
|
|
|
pCKP->Control.levelcontrol.command = LVLC_GAME_OVER;
|
|
|
|
}
|
|
|
|
if (fade.mode==FADE_COMPLETE && fade.dir==FADE_OUT)
|
|
{
|
|
pCKP->Control.levelcontrol.command = LVLC_GAME_OVER;
|
|
}
|
|
}
|
|
|
|
#ifdef NETWORK_PLAY
|
|
// if (numplayers>1) net_senddata();
|
|
#endif
|
|
|
|
if (g_pInput->getPressedKey(KQUIT))
|
|
{
|
|
VerifyQuit(pCKP);
|
|
}
|
|
if (QuitState != NO_QUIT) return;
|
|
|
|
// limit frame rate
|
|
if (!acceleratemode)
|
|
{
|
|
g_pInput->pollEvents();
|
|
g_pTimer->SpeedThrottle();
|
|
#ifdef NETWORK_PLAY
|
|
// if (numplayers>1) net_sync();
|
|
#endif
|
|
}
|
|
|
|
if(g_pInput->getExitEvent())
|
|
{
|
|
g_pInput->sendKey(KQUIT);
|
|
g_pInput->cancelExitEvent();
|
|
}
|
|
|
|
} while(!crashflag && pCKP->Control.levelcontrol.command==LVLC_NOCOMMAND);
|
|
|
|
// Cleanup the player structure!
|
|
|
|
}
|
|
|
|
// gives keycard for door doortile to player p
|
|
void give_keycard(int doortile, int p)
|
|
{
|
|
bool keystack = options[OPT_KEYCARDSTACK].value; // True if set
|
|
g_pSound->playSound(SOUND_GET_CARD, PLAY_NOW);
|
|
|
|
if (doortile==DOOR_YELLOW) player[p].inventory.HasCardYellow = keystack ? player[p].inventory.HasCardYellow + 1 : 1;
|
|
else if (doortile==DOOR_RED) player[p].inventory.HasCardRed = keystack ? player[p].inventory.HasCardRed + 1 : 1;
|
|
else if (doortile==DOOR_GREEN) player[p].inventory.HasCardGreen = keystack ? player[p].inventory.HasCardGreen + 1 : 1;
|
|
else if (doortile==DOOR_BLUE) player[p].inventory.HasCardBlue = keystack ? player[p].inventory.HasCardBlue + 1 : 1;
|
|
else
|
|
{
|
|
crashflag = 1;
|
|
crashflag2 = doortile;
|
|
why_term_ptr = "give_keycard(): invalid value for doortile parameter.";
|
|
}
|
|
}
|
|
|
|
// take away the specified keycard from player p
|
|
void take_keycard(int doortile, int p)
|
|
{
|
|
if (doortile==DOOR_YELLOW) player[p].inventory.HasCardYellow--;
|
|
else if (doortile==DOOR_RED) player[p].inventory.HasCardRed--;
|
|
else if (doortile==DOOR_GREEN) player[p].inventory.HasCardGreen--;
|
|
else if (doortile==DOOR_BLUE) player[p].inventory.HasCardBlue--;
|
|
|
|
if(player[p].inventory.HasCardYellow > 9) player[p].inventory.HasCardYellow = 0;
|
|
if(player[p].inventory.HasCardRed > 9) player[p].inventory.HasCardRed = 0;
|
|
if(player[p].inventory.HasCardGreen > 9) player[p].inventory.HasCardGreen = 0;
|
|
if(player[p].inventory.HasCardBlue > 9) player[p].inventory.HasCardBlue = 0;
|
|
}
|
|
|
|
// unregisters all animated tiles with baseframe tile
|
|
void unregister_animtiles(int tile)
|
|
{
|
|
int i;
|
|
for(i=0;i<MAX_ANIMTILES-1;i++)
|
|
{
|
|
if (animtiles[i].baseframe == tile)
|
|
{
|
|
animtiles[i].slotinuse = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void open_door(int doortile, int doorsprite, int mpx, int mpy, int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
int o,chgtotile;
|
|
short tilefix=0;
|
|
|
|
g_pSound->playSound(SOUND_DOOR_OPEN, PLAY_NOW);
|
|
|
|
take_keycard(doortile, cp);
|
|
|
|
/* erase door from map */
|
|
if (pCKP->Control.levelcontrol.episode==3)
|
|
{
|
|
chgtotile = map.mapdata[mpx-1][mpy];
|
|
}
|
|
else
|
|
{
|
|
chgtotile = tiles[map.mapdata[mpx][mpy]].chgtile;
|
|
}
|
|
|
|
if(TileProperty[map.mapdata[mpx][mpy-1]][BEHAVIOR] > 1 &&
|
|
TileProperty[map.mapdata[mpx][mpy-1]][BEHAVIOR] < 6) // This happens because, sometimes the player opens the door
|
|
// from a lower part.
|
|
{
|
|
map_chgtile(mpx, mpy-1, chgtotile);
|
|
tilefix=1;
|
|
|
|
}
|
|
if(TileProperty[map.mapdata[mpx][mpy]][BEHAVIOR] > 1 &&
|
|
TileProperty[map.mapdata[mpx][mpy]][BEHAVIOR] < 6) // This happens because, sometimes the player opens the door
|
|
// from a lower part.
|
|
{
|
|
map_chgtile(mpx, mpy, chgtotile); // upper?
|
|
|
|
}
|
|
if(TileProperty[map.mapdata[mpx][mpy+1]][BEHAVIOR] > 1 &&
|
|
TileProperty[map.mapdata[mpx][mpy+1]][BEHAVIOR] < 6) // This happens because, sometimes the player opens the door
|
|
// from a lower part.
|
|
{
|
|
map_chgtile(mpx, mpy+1, chgtotile); // When he stands in front of the door!
|
|
}
|
|
|
|
// replace the door tiles with a door object, which will do the animation
|
|
o = spawn_object(mpx<<4<<CSF,(mpy-tilefix)<<4<<CSF,OBJ_DOOR);
|
|
objects[o].sprite = doorsprite;
|
|
}
|
|
|
|
// checks if score is > than "extra life at" and award 1-UPs when appropriate
|
|
void extralifeat(int cp)
|
|
{
|
|
if (player[cp].inventory.score > player[cp].inventory.extralifeat)
|
|
{
|
|
g_pSound->playSound(SOUND_EXTRA_LIFE, PLAY_NOW);
|
|
player[cp].inventory.lives++;
|
|
player[cp].inventory.extralifeat += 20000;
|
|
}
|
|
}
|
|
|
|
// have keen pick up the goodie at screen pixel position (px, py)
|
|
void keen_get_goodie(int px, int py, int theplayer, stCloneKeenPlus *pCKP)
|
|
{
|
|
int mpx,mpy,t;
|
|
/*int i;*/
|
|
mpx = px>>4;
|
|
mpy = py>>4;
|
|
t = map.mapdata[mpx][mpy];
|
|
|
|
if ((TileProperty[t][BEHAVIOR] < 17 && TileProperty[t][BEHAVIOR] > 5) ||
|
|
(TileProperty[t][BEHAVIOR] > 17 && TileProperty[t][BEHAVIOR] < 22) ||
|
|
(TileProperty[t][BEHAVIOR] == 27 || TileProperty[t][BEHAVIOR] == 28) ) // All pickupable items
|
|
//if (tiles[t].pickupable)
|
|
{ // pick up the goodie, i.e. erase it from the map
|
|
map_chgtile(mpx, mpy, tiles[t].chgtile);
|
|
//if (tiles[t].isAnimated) map_deanimate(mpx, mpy);
|
|
if (TileProperty[t][ANIMATION] != 1) map_deanimate(mpx, mpy);
|
|
}
|
|
else if (TileProperty[t][BEHAVIOR] == 1)
|
|
//else if (tiles[t].lethal)
|
|
{ // whoah, this "goodie" isn't so good...
|
|
killplayer(theplayer);
|
|
return;
|
|
}
|
|
|
|
// do whatever the goodie is supposed to do...
|
|
procgoodie(t, mpx, mpy, theplayer, pCKP);
|
|
}
|
|
|
|
void initgame(stCloneKeenPlus *pCKP)
|
|
{
|
|
int x,y;
|
|
unsigned int i;
|
|
|
|
animtiletimer = curanimtileframe = 0;
|
|
//PlatExtending = 0;
|
|
|
|
// reset player walk frame widths
|
|
for(i=0;i<numplayers;i++)
|
|
{
|
|
sprites[playerbaseframes[i]+0].xsize = PLAYERSPRITE_WIDTH;
|
|
sprites[playerbaseframes[i]+1].xsize = PLAYERSPRITE_WIDTH;
|
|
sprites[playerbaseframes[i]+2].xsize = PLAYERSPRITE_WIDTH;
|
|
sprites[playerbaseframes[i]+3].xsize = PLAYERSPRITE_WIDTH;
|
|
}
|
|
|
|
// set gun/ice cannon fire freq
|
|
if (pCKP->Control.levelcontrol.episode==1)
|
|
{
|
|
gunfirefreq = ICECANNON_FIRE_FREQ;
|
|
}
|
|
else
|
|
{
|
|
gunfirefreq = GUN_FIRE_FREQ;
|
|
}
|
|
|
|
// reset the ysize attribute of all doors
|
|
sprites[DOOR_YELLOW_SPRITE].ysize = 32;
|
|
sprites[DOOR_RED_SPRITE].ysize = 32;
|
|
sprites[DOOR_GREEN_SPRITE].ysize = 32;
|
|
sprites[DOOR_BLUE_SPRITE].ysize = 32;
|
|
|
|
pCKP->Control.levelcontrol.level_done_timer = 0;
|
|
pCKP->Control.levelcontrol.gameovermode = 0;
|
|
|
|
// all objects -> not exist
|
|
for(i=1;i<MAX_OBJECTS-1;i++) objects[i].exists = 0;
|
|
// clear out AnimTileInUse array
|
|
for(y=0;y<ATILEINUSE_SIZEX-1;y++)
|
|
{
|
|
for(x=0;x<ATILEINUSE_SIZEY-1;x++)
|
|
{
|
|
AnimTileInUse[x][y] = 0;
|
|
}
|
|
}
|
|
// set all animated tile slots to "not in use"
|
|
map_unregister_all_animtiles();
|
|
|
|
// initilize default sprites for objects
|
|
memset(objdefsprites, 0, sizeof(objdefsprites));
|
|
if (pCKP->Control.levelcontrol.episode==1)
|
|
{
|
|
objdefsprites[OBJ_YORP] = OBJ_YORP_DEFSPRITE;
|
|
objdefsprites[OBJ_GARG] = OBJ_GARG_DEFSPRITE;
|
|
objdefsprites[OBJ_BUTLER] = OBJ_BUTLER_DEFSPRITE;
|
|
objdefsprites[OBJ_TANK] = OBJ_TANK_DEFSPRITE;
|
|
objdefsprites[OBJ_ICECHUNK] = OBJ_ICECHUNK_DEFSPRITE;
|
|
objdefsprites[OBJ_ICEBIT] = OBJ_ICEBIT_DEFSPRITE;
|
|
objdefsprites[OBJ_ROPE] = OBJ_ROPE_DEFSPRITE;
|
|
|
|
objdefsprites[OBJ_RAY] = OBJ_RAY_DEFSPRITE_EP1;
|
|
objdefsprites[OBJ_VORT] = OBJ_VORT_DEFSPRITE_EP1;
|
|
}
|
|
else if (pCKP->Control.levelcontrol.episode==2)
|
|
{
|
|
objdefsprites[OBJ_WALKER] = OBJ_WALKER_DEFSPRITE;
|
|
objdefsprites[OBJ_TANKEP2] = OBJ_TANKEP2_DEFSPRITE;
|
|
objdefsprites[OBJ_VORTELITE] = OBJ_VORTELITE_DEFSPRITE;
|
|
|
|
objdefsprites[OBJ_RAY] = OBJ_RAY_DEFSPRITE_EP2;
|
|
objdefsprites[OBJ_VORT] = OBJ_VORT_DEFSPRITE_EP2;
|
|
objdefsprites[OBJ_PLATFORM] = OBJ_PLATFORM_DEFSPRITE_EP2;
|
|
objdefsprites[OBJ_BABY] = OBJ_BABY_DEFSPRITE_EP2;
|
|
}
|
|
else if (pCKP->Control.levelcontrol.episode==3)
|
|
{
|
|
objdefsprites[OBJ_FOOB] = OBJ_FOOB_DEFSPRITE;
|
|
objdefsprites[OBJ_NINJA] = OBJ_NINJA_DEFSPRITE;
|
|
objdefsprites[OBJ_MOTHER] = OBJ_MOTHER_DEFSPRITE;
|
|
objdefsprites[OBJ_MEEP] = OBJ_MEEP_DEFSPRITE;
|
|
objdefsprites[OBJ_BALL] = OBJ_BALL_DEFSPRITE;
|
|
objdefsprites[OBJ_JACK] = OBJ_BALL_DEFSPRITE;
|
|
|
|
objdefsprites[OBJ_RAY] = OBJ_RAY_DEFSPRITE_EP3;
|
|
objdefsprites[OBJ_VORT] = OBJ_VORT_DEFSPRITE_EP3;
|
|
objdefsprites[OBJ_PLATFORM] = OBJ_PLATFORM_DEFSPRITE_EP3;
|
|
objdefsprites[OBJ_BABY] = OBJ_BABY_DEFSPRITE_EP3;
|
|
}
|
|
|
|
objdefsprites[OBJ_DOOR] = DOOR_YELLOW_SPRITE;
|
|
objdefsprites[OBJ_TELEPORTER] = OBJ_TELEPORTER_DEFSPRITE;
|
|
|
|
objdefsprites[OBJ_SECTOREFFECTOR] = BlankSprite;
|
|
|
|
// initilize game variables
|
|
pCKP->Control.levelcontrol.level_done = LEVEL_NOT_DONE;
|
|
animtiletimer = curanimtileframe = 0;
|
|
DemoObjectHandle = 0;
|
|
|
|
for(i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
player[i].isPlaying = 0;
|
|
}
|
|
|
|
for(i=0;i<numplayers;i++)
|
|
{
|
|
player[i].isPlaying = 1;
|
|
player[i].useObject = (i+1); // player objects start at 1 cause DemoObject is index 0
|
|
player[i].dpadcount = player[i].dpadlastcount = 0;
|
|
player[i].hideplayer = 0;
|
|
player[i].mounted = 0;
|
|
player[i].ppogostick = false;
|
|
player[i].pjumping = 0;
|
|
player[i].pfalling = 0;
|
|
player[i].pwalking = player[i].playspeed = 0;
|
|
player[i].pinertia_x = player[i].pinertia_y = 0;
|
|
player[i].pboost_x = 0;
|
|
player[i].playpushed_x = 0;
|
|
player[i].pfiring = 0;
|
|
player[i].psliding = player[i].psemisliding = 0;
|
|
player[i].pdie = 0;
|
|
|
|
player[i].pfrozentime = 0;
|
|
player[i].ankhtime = 0;
|
|
player[i].keyprocstate = 0; // KPROC_IDLE
|
|
player[i].pjustjumped = player[i].pjustfell = 0;
|
|
}
|
|
// each player is tied to a "puppet" object.
|
|
// initialize objects used by players.
|
|
int o;
|
|
for(i=0;i<numplayers;i++)
|
|
{
|
|
o = player[i].useObject;
|
|
if (player[i].isPlaying)
|
|
{
|
|
objects[o].exists = 1;
|
|
objects[o].onscreen = 1;
|
|
objects[o].type = OBJ_PLAYER;
|
|
objects[o].sprite = 0;
|
|
objects[o].onscreen = 1;
|
|
objects[o].AssociatedWithPlayer = i;
|
|
objects[o].honorPriority = 1;
|
|
}
|
|
}
|
|
|
|
|
|
scroll_x = 0;
|
|
scrollx_buf = 0;
|
|
scrollpix = 0;
|
|
mapx = 0;
|
|
mapxstripepos = 0;
|
|
|
|
scroll_y = 0;
|
|
scrolly_buf = 0;
|
|
scrollpixy = 0;
|
|
mapy = 0;
|
|
mapystripepos = 0;
|
|
|
|
thisplayer = 0;
|
|
}
|
|
|
|
int initgamefirsttime(stCloneKeenPlus *pCKP, int s)
|
|
{
|
|
int i;
|
|
|
|
map.firsttime = 1;
|
|
|
|
for(i=0;i<MAX_LEVELS;i++)
|
|
pCKP->Control.levelcontrol.levels_completed[i] = 0;
|
|
|
|
for(i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
player[i].inventory.extralifeat = 20000;
|
|
player[i].inventory.lives = 4;
|
|
player[i].godmode = 0;
|
|
|
|
if (pCKP->Control.levelcontrol.episode==1)
|
|
{
|
|
player[i].inventory.charges = 0;
|
|
}
|
|
else if (pCKP->Control.levelcontrol.episode==2)
|
|
{
|
|
player[i].inventory.charges = 3;
|
|
}
|
|
else
|
|
{
|
|
player[i].inventory.charges = 5;
|
|
}
|
|
if (demomode) player[i].inventory.charges = 100;
|
|
|
|
// start with pogo stick in all episodes but 1
|
|
if (pCKP->Control.levelcontrol.episode!=1 || demomode)
|
|
{ player[i].inventory.HasPogo = 1; }
|
|
else
|
|
{ player[i].inventory.HasPogo = 0; }
|
|
}
|
|
|
|
initsprites(pCKP, s);
|
|
|
|
if (demomode) srand(375);
|
|
|
|
primaryplayer = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
char spawn_object(int x, int y, int otype)
|
|
{
|
|
int i;
|
|
// find an unused object slot
|
|
for(i=1;i<MAX_OBJECTS-1;i++)
|
|
{
|
|
if (!objects[i].exists && objects[i].type != OBJ_PLAYER)
|
|
{
|
|
objects[i].x = x;
|
|
objects[i].y = y;
|
|
objects[i].type = otype;
|
|
objects[i].sprite = objdefsprites[otype];
|
|
objects[i].exists = 1;
|
|
objects[i].needinit = 1;
|
|
objects[i].onscreen = 0;
|
|
objects[i].hasbeenonscreen = 0;
|
|
objects[i].canbezapped = 0;
|
|
objects[i].zapped = 0;
|
|
objects[i].inhibitfall = 0;
|
|
objects[i].honorPriority = 1;
|
|
SetAllCanSupportPlayer(i, 0);
|
|
return i;
|
|
}
|
|
}
|
|
// object could not be created
|
|
|
|
g_pLogFile->textOut(PURPLE, "Warning : Object could not be created.");
|
|
return 0;
|
|
}
|
|
|
|
// common enemy/object ai, such as falling, setting blocked variables,
|
|
// detecting player contact, etc.
|
|
void common_enemy_ai(int o)
|
|
{
|
|
int x,y,xa,ya,xsize,ysize;
|
|
int temp;
|
|
int cplayer;
|
|
|
|
if (objects[o].type==OBJ_DEMOMSG) return;
|
|
|
|
xsize = sprites[objects[o].sprite].xsize;
|
|
ysize = sprites[objects[o].sprite].ysize;
|
|
|
|
// set value of blockedd--should object fall?
|
|
temp = (objects[o].y>>CSF)+ysize;
|
|
if ((temp>>4)<<4 != temp)
|
|
{
|
|
objects[o].blockedd = 0;
|
|
}
|
|
else
|
|
{ // on a tile boundary, test if tile under object is solid
|
|
objects[o].blockedd = 0;
|
|
x = (objects[o].x>>CSF);
|
|
y = (objects[o].y>>CSF)+ysize+1;
|
|
for(xa=0;xa<xsize-2;xa+=16)
|
|
{
|
|
if(TileProperty[getmaptileat(x+xa,y)][BUP] || (TileProperty[getmaptileat(x+xa,y)][BEHAVIOR] > 1 &&
|
|
TileProperty[getmaptileat(x+xa,y)][BEHAVIOR] < 6) )
|
|
{
|
|
objects[o].blockedd = 1;
|
|
goto setblockedd;
|
|
}
|
|
}
|
|
if(TileProperty[getmaptileat(x+xsize-2, y)][BUP] || (TileProperty[getmaptileat(x+xa,y)][BEHAVIOR] > 1 &&
|
|
TileProperty[getmaptileat(x+xa,y)][BEHAVIOR] < 6) )
|
|
{
|
|
objects[o].blockedd = 1;
|
|
}
|
|
setblockedd: ;
|
|
}
|
|
|
|
// set blockedu
|
|
objects[o].blockedu = 0;
|
|
x = (objects[o].x>>CSF);
|
|
y = (objects[o].y>>CSF)-1;
|
|
for(xa=0;xa<xsize;xa+=16)
|
|
{
|
|
if(TileProperty[getmaptileat(x+xa,y)][BDOWN])
|
|
//if (tiles[getmaptileat(x+xa,y)].solidceil)
|
|
{
|
|
objects[o].blockedu = 1;
|
|
goto setblockedu;
|
|
}
|
|
}
|
|
if(TileProperty[getmaptileat(x+xsize-1, y)][BDOWN])
|
|
//if (tiles[getmaptileat(x+xsize-1, y)].solidceil)
|
|
{
|
|
objects[o].blockedu = 1;
|
|
}
|
|
setblockedu: ;
|
|
|
|
// set blockedl
|
|
objects[o].blockedl = 0;
|
|
x = (objects[o].x>>CSF)-1;
|
|
y = (objects[o].y>>CSF)+1;
|
|
for(ya=0;ya<ysize;ya+=16)
|
|
{
|
|
if(TileProperty[getmaptileat(x,y+ya)][BRIGHT])
|
|
//if (tiles[getmaptileat(x,y+ya)].solidr)
|
|
{
|
|
objects[o].blockedl = 1;
|
|
goto setblockedl;
|
|
}
|
|
}
|
|
if(TileProperty[getmaptileat(x, ((objects[o].y>>CSF)+ysize-1))][BRIGHT])
|
|
//if (tiles[getmaptileat(x, ((objects[o].y>>CSF)+ysize-1))].solidr)
|
|
{
|
|
objects[o].blockedl = 1;
|
|
}
|
|
setblockedl: ;
|
|
|
|
// set blockedr
|
|
objects[o].blockedr = 0;
|
|
x = (objects[o].x>>CSF)+xsize;
|
|
y = (objects[o].y>>CSF)+1;
|
|
for(ya=0;ya<ysize;ya+=16)
|
|
{
|
|
if(TileProperty[getmaptileat(x,y+ya)][BRIGHT])
|
|
//if (tiles[getmaptileat(x,y+ya)].solidl)
|
|
{
|
|
objects[o].blockedr = 1;
|
|
goto setblockedr;
|
|
}
|
|
}
|
|
if(TileProperty[getmaptileat(x, ((objects[o].y>>CSF)+ysize-1))][BLEFT])
|
|
//if (tiles[getmaptileat(x, ((objects[o].y>>CSF)+ysize-1))].solidl)
|
|
{
|
|
objects[o].blockedr = 1;
|
|
}
|
|
setblockedr: ;
|
|
|
|
// hit detection with players
|
|
objects[o].touchPlayer = 0;
|
|
for(cplayer=0;cplayer<MAX_PLAYERS;cplayer++)
|
|
{
|
|
if (player[cplayer].isPlaying)
|
|
{
|
|
objects[player[cplayer].useObject].x = player[cplayer].x;
|
|
objects[player[cplayer].useObject].y = player[cplayer].y;
|
|
objects[player[cplayer].useObject].sprite = 0;
|
|
if (!player[cplayer].pdie)
|
|
{
|
|
if (hitdetect(o, player[cplayer].useObject))
|
|
{
|
|
objects[o].touchPlayer = 1;
|
|
objects[o].touchedBy = cplayer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// have object fall if it should
|
|
if (!objects[o].inhibitfall)
|
|
{
|
|
#define OBJFALLSPEED 20
|
|
if (objects[o].blockedd)
|
|
{
|
|
objects[o].yinertia = 0;
|
|
}
|
|
else
|
|
{
|
|
#define OBJ_YINERTIA_RATE 5
|
|
if (objects[o].yinertiatimer>OBJ_YINERTIA_RATE)
|
|
{
|
|
if (objects[o].yinertia < OBJFALLSPEED) objects[o].yinertia++;
|
|
objects[o].yinertiatimer = 0;
|
|
} else objects[o].yinertiatimer++;
|
|
}
|
|
objects[o].y += objects[o].yinertia;
|
|
}
|
|
}
|
|
|
|
// returns nonzero if object1 overlaps object2
|
|
char hitdetect(int object1, int object2)
|
|
{
|
|
int s1, s2;
|
|
unsigned int rect1x1, rect1y1, rect1x2, rect1y2;
|
|
unsigned int rect2x1, rect2y1, rect2x2, rect2y2;
|
|
|
|
// get the sprites used by the two objects
|
|
s1 = objects[object1].sprite;
|
|
s2 = objects[object2].sprite;
|
|
|
|
// get the bounding rectangle of the first object
|
|
rect1x1 = objects[object1].x + sprites[s1].bboxX1;
|
|
rect1y1 = objects[object1].y + sprites[s1].bboxY1;
|
|
rect1x2 = objects[object1].x + sprites[s1].bboxX2;
|
|
rect1y2 = objects[object1].y + sprites[s1].bboxY2;
|
|
|
|
// get the bounding rectangle of the second object
|
|
rect2x1 = objects[object2].x + sprites[s2].bboxX1;
|
|
rect2y1 = objects[object2].y + sprites[s2].bboxY1;
|
|
rect2x2 = objects[object2].x + sprites[s2].bboxX2;
|
|
rect2y2 = objects[object2].y + sprites[s2].bboxY2;
|
|
|
|
// find out if the rectangles overlap
|
|
if ((rect1x1 < rect2x1) && (rect1x2 < rect2x1)) return 0;
|
|
if ((rect1x1 > rect2x2) && (rect1x2 > rect2x2)) return 0;
|
|
if ((rect1y1 < rect2y1) && (rect1y2 < rect2y1)) return 0;
|
|
if ((rect1y1 > rect2y2) && (rect1y2 > rect2y2)) return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void killplayer(int theplayer)
|
|
{
|
|
if (player[theplayer].godmode || g_pInput->getHoldedKey(KTAB)) return;
|
|
if (player[theplayer].ankhtime) return;
|
|
if (!player[theplayer].pdie)
|
|
{
|
|
player[theplayer].pdie = PDIE_DYING;
|
|
player[theplayer].pdieframe = 0;
|
|
player[theplayer].pdietimer = 0;
|
|
player[theplayer].pdietillfly = DIE_TILL_FLY_TIME;
|
|
player[theplayer].pdie_xvect = rand()%(DIE_MAX_XVECT*2);
|
|
player[theplayer].pdie_xvect -= DIE_MAX_XVECT;
|
|
player[theplayer].inventory.lives--;
|
|
player[theplayer].y += (8<<CSF);
|
|
gamepdo_SelectFrame(theplayer);
|
|
g_pMusicPlayer->stop();
|
|
g_pSound->playSound(SOUND_KEEN_DIE, PLAY_NOW);
|
|
}
|
|
}
|
|
|
|
void freezeplayer(int theplayer)
|
|
{
|
|
if (player[theplayer].godmode /*|| immediate_keytable[KTAB]*/) return;
|
|
if (player[theplayer].ankhtime) return;
|
|
// give the player a little "kick"
|
|
player[theplayer].pjumptime = PJUMP_NORMALTIME_1;
|
|
player[theplayer].pjumpupdecreaserate = PJUMP_UPDECREASERATE_1;
|
|
player[theplayer].pjumpupspeed = 15;
|
|
player[theplayer].pjumping = PJUMPUP;
|
|
player[theplayer].pjumpupspeed_decreasetimer = 0;
|
|
player[theplayer].pjustjumped = 1;
|
|
|
|
// and freeze him (stun him on ep2/3)
|
|
player[theplayer].pfrozentime = PFROZEN_TIME;
|
|
player[theplayer].pfrozenframe = 0;
|
|
player[theplayer].pfrozenanimtimer = 0;
|
|
player[theplayer].ppogostick = false;
|
|
}
|
|
|
|
|
|
void PlayerTouchedExit(int theplayer, stCloneKeenPlus *pCKP)
|
|
{
|
|
/*int i;*/
|
|
if (!player[theplayer].pjumping && !player[theplayer].pfalling\
|
|
&& !player[theplayer].ppogostick && \
|
|
pCKP->Control.levelcontrol.level_done==LEVEL_NOT_DONE)
|
|
{
|
|
// don't allow player to walk through a door if he's standing
|
|
// on an object such as a platform or an enemy
|
|
if (player[theplayer].psupportingobject)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if player has ankh shut it off
|
|
if (player[theplayer].ankhtime)
|
|
{
|
|
player[theplayer].ankhtime = 0;
|
|
objects[player[theplayer].ankhshieldobject].exists = 0;
|
|
}
|
|
|
|
player[theplayer].ppogostick = false;
|
|
|
|
g_pMusicPlayer->stop();
|
|
g_pSound->playSound(SOUND_LEVEL_DONE, PLAY_NOW);
|
|
pCKP->Control.levelcontrol.level_done = LEVEL_DONE_WALK;
|
|
pCKP->Control.levelcontrol.level_finished_by = theplayer;
|
|
}
|
|
}
|
|
|
|
void endlevel(int success, stCloneKeenPlus *pCKP)
|
|
{
|
|
if (fade.mode == NO_FADE)
|
|
{
|
|
fade.dir = FADE_OUT;
|
|
fade.curamt = PAL_FADE_SHADES;
|
|
fade.fadetimer = 0;
|
|
fade.rate = FADE_NORM;
|
|
fade.mode = FADE_GO;
|
|
pCKP->Control.levelcontrol.success = success;
|
|
pCKP->Control.levelcontrol.tobonuslevel = 0;
|
|
}
|
|
}
|
|
|
|
void SetGameOver(stCloneKeenPlus *pCKP)
|
|
{
|
|
/*int x,y,bmnum;*/
|
|
if (!pCKP->Control.levelcontrol.gameovermode)
|
|
{
|
|
pCKP->Control.levelcontrol.gameovermode = 1;
|
|
g_pSound->playSound(SOUND_GAME_OVER, PLAY_NOW);
|
|
}
|
|
}
|
|
|
|
|
|
// this is so objects can block the player,
|
|
// player can stand on them, etc.
|
|
// x and y are the CSFed coordinates to check (e.g. playx and playy)
|
|
// returns nonzero if there is a solid object
|
|
// at that point
|
|
char checkobjsolid(unsigned int x, unsigned int y, unsigned int cp)
|
|
{
|
|
int o;
|
|
|
|
for(o=1;o<MAX_OBJECTS;o++)
|
|
{
|
|
if (objects[o].exists && objects[o].cansupportplayer[cp])
|
|
{
|
|
if (x >= objects[o].x+sprites[objects[o].sprite].bboxX1)
|
|
if (x <= objects[o].x+sprites[objects[o].sprite].bboxX2)
|
|
if (y >= objects[o].y+sprites[objects[o].sprite].bboxY1)
|
|
if (y <= objects[o].y+sprites[objects[o].sprite].bboxY2)
|
|
return o;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// returns 1 if player cp has the card to door t, which -> door
|
|
char CheckDoorBlock(int t, int cp, int which,stCloneKeenPlus *pCKP)
|
|
{
|
|
if (which==DOOR_YELLOW)
|
|
{
|
|
if (!player[cp].inventory.HasCardYellow)
|
|
{
|
|
player[cp].blockedby = t;
|
|
return 1;
|
|
}
|
|
}
|
|
else if (which==DOOR_RED)
|
|
{
|
|
if (!player[cp].inventory.HasCardRed)
|
|
{
|
|
player[cp].blockedby = t;
|
|
return 1;
|
|
}
|
|
}
|
|
else if (which==DOOR_GREEN)
|
|
{
|
|
if (!player[cp].inventory.HasCardGreen)
|
|
{
|
|
player[cp].blockedby = t;
|
|
return 1;
|
|
}
|
|
}
|
|
else if (which==DOOR_BLUE)
|
|
{
|
|
if (!player[cp].inventory.HasCardBlue)
|
|
{
|
|
player[cp].blockedby = t;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// checks if tile at (x,y) is solid to the player walking left into it.
|
|
// returns 1 and sets blockedby if so.
|
|
char checkissolidl(int x, int y, int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
int t;
|
|
t = getmaptileat(x, y);
|
|
|
|
if(TileProperty[t][BLEFT] || x < 0)
|
|
//if (tiles[t].solidl)
|
|
{
|
|
player[cp].blockedby = t;
|
|
return 1;
|
|
}
|
|
if (checkobjsolid(x<<CSF,y<<CSF,cp))
|
|
{
|
|
player[cp].blockedby = 0;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
// don't let player walk through doors he doesn't have the key to
|
|
if (CheckDoorBlock(t, cp, TileProperty[t][BEHAVIOR], pCKP))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// checks if tile at (x,y) is solid to the player walking right into it.
|
|
// returns 1 and sets blockedby if so.
|
|
char checkissolidr(int x, int y, int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
int t;
|
|
t = getmaptileat(x, y);
|
|
if(TileProperty[t][BRIGHT])
|
|
//if (tiles[t].solidr)
|
|
{
|
|
player[cp].blockedby = t;
|
|
return 1;
|
|
}
|
|
else if (checkobjsolid(x<<CSF,y<<CSF,cp))
|
|
{
|
|
player[cp].blockedby = 0;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
// don't let player walk through doors he doesn't have the key to
|
|
if (CheckDoorBlock(t, cp, TileProperty[t][BEHAVIOR], pCKP))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// copies tile data into a sprite. multiple tiles can be copied into
|
|
// a single sprite--they will be stacked up vertically.
|
|
void CopyTileToSprite(int t, int s, int ntilestocopy, int transparentcol)
|
|
{
|
|
int x,y1,y2,tboundary;
|
|
sprites[s].xsize = 16;
|
|
sprites[s].ysize = 16 * ntilestocopy;
|
|
sprites[s].bboxX1 = sprites[s].bboxY1 = 0;
|
|
sprites[s].bboxX2 = (sprites[s].xsize << CSF);
|
|
sprites[s].bboxY2 = (sprites[s].ysize << CSF);
|
|
|
|
tboundary = 0;
|
|
y2 = 0;
|
|
for(y1=0;y1<(16*ntilestocopy);y1++)
|
|
{
|
|
for(x=0;x<16;x++)
|
|
{
|
|
sprites[s].imgdata[y1][x] = tiledata[t][y2][x];
|
|
if (sprites[s].imgdata[y1][x] != transparentcol)
|
|
{
|
|
sprites[s].maskdata[y1][x] = 15;
|
|
}
|
|
else
|
|
{
|
|
sprites[s].maskdata[y1][x] = 0;
|
|
}
|
|
}
|
|
y2++;
|
|
if (y2>=16)
|
|
{
|
|
y2 = 0;
|
|
t++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// creates a mask from a sourcetile, places it in desttile
|
|
void MakeMask(int sourcetile, int desttile, int transparentcol)
|
|
{
|
|
int x,y,c;
|
|
for(y=0;y<16;y++)
|
|
{
|
|
for(x=0;x<16;x++)
|
|
{
|
|
c = tiledata[sourcetile][y][x];
|
|
if (c != transparentcol) c = 0; else c = 15;
|
|
tiledata[desttile][y][x] = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
// replaces all instances of color find in sprite s with
|
|
// color replace, as long as the y is greater than miny
|
|
void ReplaceSpriteColor(int s, int find, int replace, int miny)
|
|
{
|
|
int x,y;
|
|
|
|
for(y=miny;y<sprites[s].ysize;y++)
|
|
{
|
|
for(x=0;x<sprites[s].xsize;x++)
|
|
{
|
|
if (sprites[s].imgdata[y][x]==find)
|
|
{
|
|
sprites[s].imgdata[y][x] = replace;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// duplicates sprite source to dest
|
|
void CopySprite(int source, int dest)
|
|
{
|
|
int x,y;
|
|
|
|
sprites[dest].xsize = sprites[source].xsize;
|
|
sprites[dest].ysize = sprites[source].ysize;
|
|
|
|
for(y=0;y<sprites[source].ysize;y++)
|
|
{
|
|
for(x=0;x<sprites[source].xsize;x++)
|
|
{
|
|
sprites[dest].imgdata[y][x] = sprites[source].imgdata[y][x];
|
|
sprites[dest].maskdata[y][x] = sprites[source].maskdata[y][x];
|
|
}
|
|
}
|
|
}
|
|
|
|
// initilize sprites that come from tiles, such as the doors
|
|
void initsprites(stCloneKeenPlus *pCKP, int s)
|
|
{
|
|
int x,y,i,indx;
|
|
|
|
|
|
for(i=0 ; i < numtiles ; i++ )
|
|
{
|
|
if(TileProperty[i][BEHAVIOR] == DOOR_YELLOW)
|
|
CopyTileToSprite(i-1, DOOR_YELLOW_SPRITE, 2, 7);
|
|
|
|
if(TileProperty[i][BEHAVIOR] == DOOR_RED)
|
|
CopyTileToSprite(i-1, DOOR_RED_SPRITE, 2, 7);
|
|
|
|
if(TileProperty[i][BEHAVIOR] == DOOR_GREEN)
|
|
CopyTileToSprite(i-1, DOOR_GREEN_SPRITE, 2, 7);
|
|
|
|
if(TileProperty[i][BEHAVIOR] == DOOR_BLUE)
|
|
CopyTileToSprite(i-1, DOOR_BLUE_SPRITE, 2, 7);
|
|
}
|
|
|
|
|
|
// create BLANKSPRITE
|
|
BlankSprite = s;
|
|
sprites[BlankSprite].xsize = sprites[BlankSprite].ysize = 0;
|
|
|
|
// create DEMOSPRITE (the "demo" message the appears on the screen
|
|
// during a demo--the graphics for it are stored in demobox.h)
|
|
s++;
|
|
DemoSprite = s;
|
|
sprites[DemoSprite].xsize = 48;
|
|
sprites[DemoSprite].ysize = 16;
|
|
indx = 0;
|
|
for(y=0;y<DEMOBOX_HEIGHT;y++)
|
|
{
|
|
for(x=0;x<DEMOBOX_WIDTH;x++)
|
|
{
|
|
sprites[DemoSprite].imgdata[y][x] = demobox_image[indx];
|
|
sprites[DemoSprite].maskdata[y][x] = demobox_mask[indx];
|
|
indx++;
|
|
}
|
|
}
|
|
|
|
// create the sprites for player 2
|
|
s++;
|
|
playerbaseframes[1] = s;
|
|
for(i=0;i<48;i++)
|
|
{
|
|
CopySprite(i, s);
|
|
ReplaceSpriteColor(s, 13, 10, 0);
|
|
ReplaceSpriteColor(s, 5, 2, 0);
|
|
ReplaceSpriteColor(s, 9, 14, 8);
|
|
ReplaceSpriteColor(s, 1, 6, 8);
|
|
ReplaceSpriteColor(s, 12, 11, 0);
|
|
ReplaceSpriteColor(s, 4, 3, 0);
|
|
s++;
|
|
}
|
|
|
|
// create the sprites for player 3
|
|
playerbaseframes[2] = s;
|
|
for(i=0;i<48;i++)
|
|
{
|
|
CopySprite(i, s);
|
|
ReplaceSpriteColor(s, 12, 6, 0);
|
|
ReplaceSpriteColor(s, 4, 6, 0);
|
|
ReplaceSpriteColor(s, 13, 12, 0);
|
|
ReplaceSpriteColor(s, 5, 4, 0);
|
|
ReplaceSpriteColor(s, 9, 12, 8);
|
|
ReplaceSpriteColor(s, 1, 4, 8);
|
|
s++;
|
|
}
|
|
|
|
// set up mask for ice cannons (ep1)
|
|
if (pCKP->Control.levelcontrol.episode==1)
|
|
{
|
|
|
|
// MakeMask(443, 155, 7);
|
|
// tiles[443].masktile = 155;
|
|
// tiles[428].masktile = 155;
|
|
}
|
|
}
|
|
|
|
void procgoodie(int t, int mpx, int mpy, int theplayer, stCloneKeenPlus *pCKP)
|
|
{
|
|
if ((TileProperty[t][BEHAVIOR] > 5 && TileProperty[t][BEHAVIOR] < 11) ||
|
|
(TileProperty[t][BEHAVIOR] > 17 && TileProperty[t][BEHAVIOR] < 22) )
|
|
{
|
|
if((player[theplayer].x*player[theplayer].y) % 2 == 1)
|
|
g_pSound->playStereofromCoord(SOUND_GET_BONUS, PLAY_NOW, 0);
|
|
else
|
|
g_pSound->playStereofromCoord(SOUND_GET_BONUS, PLAY_NOW, 320);
|
|
}
|
|
else if (TileProperty[t][BEHAVIOR] > 10 && TileProperty[t][BEHAVIOR] < 16) g_pSound->playSound(SOUND_GET_ITEM, PLAY_NOW);
|
|
switch(TileProperty[t][BEHAVIOR])
|
|
{
|
|
// keycards
|
|
case 18: give_keycard(DOOR_YELLOW, theplayer); break;
|
|
case 19: give_keycard(DOOR_RED, theplayer); break;
|
|
case 20: give_keycard(DOOR_GREEN, theplayer); break;
|
|
case 21: give_keycard(DOOR_BLUE, theplayer); break;
|
|
|
|
case DOOR_YELLOW:
|
|
if (player[theplayer].inventory.HasCardYellow)
|
|
open_door(DOOR_YELLOW, DOOR_YELLOW_SPRITE, mpx, mpy, theplayer, pCKP);
|
|
break;
|
|
case DOOR_RED:
|
|
if (player[theplayer].inventory.HasCardRed)
|
|
open_door(DOOR_RED, DOOR_RED_SPRITE, mpx, mpy, theplayer, pCKP);
|
|
break;
|
|
case DOOR_GREEN:
|
|
if (player[theplayer].inventory.HasCardGreen)
|
|
open_door(DOOR_GREEN, DOOR_GREEN_SPRITE, mpx, mpy, theplayer, pCKP);
|
|
break;
|
|
case DOOR_BLUE:
|
|
if (player[theplayer].inventory.HasCardBlue)
|
|
open_door(DOOR_BLUE, DOOR_BLUE_SPRITE, mpx, mpy, theplayer, pCKP);
|
|
break;
|
|
|
|
case 7: // What gives you 100 Points
|
|
player[theplayer].inventory.score += 100; extralifeat(theplayer);
|
|
break;
|
|
case 8: // What gives you 200 Points
|
|
player[theplayer].inventory.score += 200; extralifeat(theplayer);
|
|
break;
|
|
case 6: // What gives you 500 Points
|
|
player[theplayer].inventory.score += 500; extralifeat(theplayer);
|
|
break;
|
|
case 9: // What gives you 1000 Points
|
|
player[theplayer].inventory.score += 1000; extralifeat(theplayer);
|
|
break;
|
|
case 10: // What gives you 5000 Points
|
|
player[theplayer].inventory.score += 5000; extralifeat(theplayer);
|
|
break;
|
|
|
|
case 15: // raygun
|
|
player[theplayer].inventory.charges += 5;
|
|
break;
|
|
case 16: // the Holy Pogo Stick
|
|
player[theplayer].inventory.HasPogo = 1;
|
|
g_pSound->playSound(SOUND_GET_PART, PLAY_NOW);
|
|
break;
|
|
|
|
case 11:
|
|
player[theplayer].inventory.HasJoystick = 1;
|
|
g_pSound->playSound(SOUND_GET_PART, PLAY_NOW);
|
|
break;
|
|
|
|
case 12:
|
|
player[theplayer].inventory.HasBattery = 1;
|
|
g_pSound->playSound(SOUND_GET_PART, PLAY_NOW);
|
|
break;
|
|
case 13:
|
|
|
|
player[theplayer].inventory.HasVacuum = 1;
|
|
g_pSound->playSound(SOUND_GET_PART, PLAY_NOW);
|
|
break;
|
|
case 14:
|
|
player[theplayer].inventory.HasFuel = 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);
|
|
pCKP->Control.levelcontrol.tobonuslevel = 1;
|
|
break;
|
|
|
|
case YORPSTATUEHEAD:
|
|
if(pCKP->Control.levelcontrol.episode == 1)
|
|
{
|
|
youseeinyourmind(mpx, mpy, pCKP);
|
|
}
|
|
else if(pCKP->Control.levelcontrol.episode == 2)
|
|
if(!player[theplayer].blockedr && !player[theplayer].blockedl)
|
|
VorticonElder(mpx, mpy, pCKP);
|
|
break;
|
|
|
|
case 27:
|
|
if(pCKP->Control.levelcontrol.episode == 3)
|
|
GiveAnkh(theplayer);
|
|
break;
|
|
case 28:
|
|
player[theplayer].inventory.charges++;
|
|
g_pSound->playSound(SOUND_GET_ITEM, PLAY_NOW);
|
|
break;
|
|
|
|
case 17:
|
|
if (pCKP->Control.levelcontrol.canexit && (!pCKP->Control.levelcontrol.isfinallevel || player[theplayer].inventory.HasFuel))
|
|
{
|
|
pCKP->Control.levelcontrol.exitXpos = (mpx+2)<<4;
|
|
PlayerTouchedExit(theplayer, pCKP);
|
|
}
|
|
break;
|
|
|
|
case 23:break; // these are switches. They cannot not be picked up!
|
|
case 25:break; // Refer to JumpandPogo to check the activation code
|
|
case 26:break;
|
|
|
|
// we fell off the bottom of the map
|
|
case TILE_FELLOFFMAP_EP1:
|
|
if (!player[theplayer].pdie)
|
|
{
|
|
g_pSound->playSound(SOUND_KEEN_FALL, PLAY_FORCE);
|
|
player[theplayer].ankhtime = 0;
|
|
player[theplayer].godmode = 0;
|
|
player[theplayer].pdie = PDIE_FELLOFFMAP;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
why_term_ptr = "procgoodie_ep1: Unknown goodie-- value given in flag2.";
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// yorp/walker etc "bump".
|
|
// if solid = 0, player can possibly force his way through.
|
|
// if solid = 1, object acts like a solid "wall".
|
|
void bumpplayer(int p, int pushamt, bool solid)
|
|
{
|
|
player[p].playpushed_x = pushamt;
|
|
|
|
if (solid)
|
|
{
|
|
if (pushamt > 0)
|
|
{
|
|
if (player[p].pinertia_x < 0)
|
|
player[p].pinertia_x = 0;
|
|
}
|
|
else
|
|
{
|
|
if (player[p].pinertia_x > 0)
|
|
player[p].pinertia_x = 0;
|
|
}
|
|
}
|
|
|
|
player[p].playpushed_decreasetimer = 0;
|
|
if (!player[p].pjumping)
|
|
{
|
|
player[p].pdir = player[p].pshowdir = (pushamt<0)?LEFT:RIGHT;
|
|
}
|
|
}
|
|
|
|
void GiveAnkh(int cp)
|
|
{
|
|
int o;
|
|
if (!player[cp].ankhtime)
|
|
{
|
|
o = spawn_object(player[cp].x, player[cp].y, OBJ_SECTOREFFECTOR);
|
|
objects[o].ai.se.type = SE_ANKHSHIELD;
|
|
player[cp].ankhshieldobject = o;
|
|
}
|
|
|
|
g_pSound->playSound(SOUND_ANKH, PLAY_NOW);
|
|
player[cp].ankhtime = PLAY_ANKH_TIME;
|
|
gamepdo_ankh(cp);
|
|
}
|
|
|
|
void gameloop_initialize(stCloneKeenPlus *pCKP)
|
|
{
|
|
unsigned int x,y,i/*,tl*/;
|
|
int timeout;
|
|
|
|
if (pCKP->Control.levelcontrol.episode == 3)
|
|
{
|
|
// coat the top of the map ("oh no!" border) with a non-solid tile
|
|
// so keen can jump partially off the top of the screen
|
|
for(x=1;x<map.xsize-2;x++)
|
|
{
|
|
map.mapdata[x][1] = 143;
|
|
}
|
|
|
|
// make it lethal to fall off the bottom of the map
|
|
// the "oh no" border is set solidceil=0, and here we're
|
|
// going to coat the bottom of the map, below the "oh no"
|
|
// border, with a special tile that has standgoodie set
|
|
// and will trigger the "fell off the map" kill
|
|
y = map.ysize;
|
|
for(x=2;x<map.xsize-2;x++)
|
|
{
|
|
map.mapdata[x][y] = TILE_FELLOFFMAP_EP3;
|
|
}
|
|
}
|
|
|
|
if (pCKP->Control.levelcontrol.episode==1)
|
|
{
|
|
// coat the bottom of the map below the border.
|
|
// since the border has solidceil=1 this provides
|
|
// a platform to catch yorps that fall off the map
|
|
y = map.ysize;
|
|
for(x=2;x<map.xsize-2;x++)
|
|
{
|
|
map.mapdata[x][y] = TILE_FELLOFFMAP_EP1;
|
|
}
|
|
}
|
|
|
|
// draw map first time
|
|
drawmap();
|
|
|
|
// generate other player's start positions
|
|
// don't generate player positions for the world
|
|
// map, except the first time
|
|
if (!map.isworldmap || map.firsttime)
|
|
{
|
|
x = player[0].x;
|
|
for(i=1;i<numplayers;i++)
|
|
{
|
|
if (player[0].x>>CSF>>4 < (map.xsize/2) || pCKP->Control.levelcontrol.episode==1)
|
|
{
|
|
x += (18<<CSF);
|
|
}
|
|
else
|
|
{
|
|
x -= (18<<CSF);
|
|
}
|
|
player[i].x = x;
|
|
player[i].y = player[0].y;
|
|
}
|
|
}
|
|
map.firsttime = 0;
|
|
// set the maximum amount the map is allowed to scroll
|
|
max_scroll_x = (map.xsize-2-(320/16))<<4;
|
|
max_scroll_y = (map.ysize<<4)-200-32;
|
|
|
|
// players start facing left if the start position
|
|
// is on the left half of the map, else right
|
|
// for ep1 it's always facing right (because of level08)
|
|
for(i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
if (map.isworldmap)
|
|
player[i].pdir = player[i].pshowdir = DOWN;
|
|
else
|
|
{
|
|
if (player[i].x>>CSF>>4 < (map.xsize/2) || pCKP->Control.levelcontrol.episode==1)
|
|
player[i].pdir = player[i].pshowdir = RIGHT;
|
|
else
|
|
player[i].pdir = player[i].pshowdir = LEFT;
|
|
}
|
|
}
|
|
|
|
// scroll past the first two tiles (the level border), they'll
|
|
// now never be visible because you're not allowed to scroll
|
|
// left past X=32.
|
|
for(i=0;i<2*16;i++)
|
|
{
|
|
map_scroll_right();
|
|
map_scroll_down();
|
|
}
|
|
|
|
// scroll the screen until the primary player is onscreen
|
|
// enough to where he doesn't set off the scroll triggers
|
|
for(timeout=0;timeout<10000;timeout++)
|
|
{
|
|
if (!gamedo_ScrollTriggers(primaryplayer)) break;
|
|
}
|
|
|
|
// initiate fade-in
|
|
fade.mode = FADE_GO;
|
|
fade.dir = FADE_IN;
|
|
fade.rate = FADE_NORM;
|
|
fade.curamt = 0;
|
|
fade.fadetimer = 0;
|
|
|
|
// "keens left" when returning to world map after dying
|
|
if (pCKP->Control.levelcontrol.dokeensleft)
|
|
{
|
|
keensleft(pCKP);
|
|
pCKP->Control.levelcontrol.dokeensleft = 0;
|
|
}
|
|
|
|
}
|