git-svn-id: https://clonekeenplus.svn.sourceforge.net/svnroot/clonekeenplus/cgenius/trunk@79 4df4b0f3-56ce-47cb-b001-ed939b7d65a6
448 lines
16 KiB
C++
448 lines
16 KiB
C++
/* GM_PDOWM.C
|
|
Contains all of the gamepdo_wm_xxx functions...which are called from the
|
|
main game loop. These functions perform some task that is done each
|
|
time around the game loop and is related to the player when he is on
|
|
the world map. Basically gamepdo.c but has slightly different versions
|
|
of some functions to handle the world map.
|
|
*/
|
|
|
|
#include "keen.h"
|
|
|
|
#include "sdl/CInput.h"
|
|
#include "include/game.h"
|
|
#include "include/misc.h"
|
|
#include "include/gm_pdowm.h"
|
|
#include "include/gamepdo.h"
|
|
#include "sdl/sound/CSound.h"
|
|
#include "hqp/CMusic.h"
|
|
|
|
void AllowMountUnmountNessie(int cp);
|
|
void MountNessieIfAvailable(int cp);
|
|
|
|
// player handler mother-function, calls all
|
|
// needed "gamepdo" functions for player cp,
|
|
// this is the world map version
|
|
void gamepdo_wm_HandlePlayer(int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
player[cp].inhibitwalking = 0;
|
|
player[cp].inhibitfall = 0;
|
|
|
|
gamepdo_StatusBox(cp, pCKP);
|
|
|
|
gamepdo_ProcessInput(cp, pCKP);
|
|
gamepdo_wm_setdir(cp, pCKP);
|
|
|
|
gamepdo_wm_setblockedlrud(cp, pCKP);
|
|
gamepdo_wm_AllowEnterLevel(cp, pCKP);
|
|
|
|
gamepdo_walking(cp, pCKP);
|
|
gamepdo_walkinganim(cp, pCKP);
|
|
|
|
if (fade.mode==NO_FADE)
|
|
{
|
|
gamepdo_InertiaAndFriction_X(cp, pCKP);
|
|
gamepdo_InertiaAndFriction_Y(cp, pCKP);
|
|
}
|
|
|
|
if (pCKP->Control.levelcontrol.episode==3)
|
|
{
|
|
AllowMountUnmountNessie(cp);
|
|
}
|
|
|
|
gamepdo_wm_SelectFrame(cp, pCKP);
|
|
|
|
// copy player's keytable to lastkeytable
|
|
memcpy(&player[cp].lastkeytable, &player[cp].keytable, sizeof(player[cp].lastkeytable));
|
|
}
|
|
|
|
// select the appropriate player frame based on what he's doing
|
|
void gamepdo_wm_SelectFrame(int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
// select base frame for current direction
|
|
if (player[cp].pshowdir==RIGHT) player[cp].playframe = PMAPRIGHTFRAME;
|
|
else if (player[cp].pshowdir==LEFT) player[cp].playframe = PMAPLEFTFRAME;
|
|
else if (player[cp].pshowdir==UP) player[cp].playframe = PMAPUPFRAME;
|
|
else if (player[cp].pshowdir==DOWN) player[cp].playframe = PMAPDOWNFRAME;
|
|
|
|
// episode 3 map frames start at 31, ep1&2 at 32
|
|
if (pCKP->Control.levelcontrol.episode==3) player[cp].playframe--;
|
|
|
|
// no walking animation if we're fading
|
|
if (fade.mode != NO_FADE) return;
|
|
|
|
// add in walk frame if walking
|
|
if (player[cp].pwalking) player[cp].playframe += player[cp].pwalkframe;
|
|
}
|
|
|
|
// set blockedl and blockedr...is Keen up against a solid object?
|
|
void gamepdo_wm_setblockedlrud(int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
player[cp].blockedl = player[cp].blockedr = 0;
|
|
player[cp].blockedu = player[cp].blockedd = 0;
|
|
|
|
// cheat: holding down TAB will turn off clipping. or if you are in godmode
|
|
if ((pCKP->Option[OPT_CHEATS].value && g_pInput->getHoldedKey(KTAB)) || player[cp].godmode) return;
|
|
|
|
// R
|
|
if (wm_issolid((player[cp].x>>CSF)+8, (player[cp].y>>CSF)+1, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedr = 1; }
|
|
else if (wm_issolid((player[cp].x>>CSF)+8, (player[cp].y>>CSF)+8, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedr = 1; }
|
|
else if (wm_issolid((player[cp].x>>CSF)+8, (player[cp].y>>CSF)+13, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedr = 1; }
|
|
|
|
// L
|
|
if (wm_issolid((player[cp].x>>CSF)+0, (player[cp].y>>CSF)+1, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedl = 1; }
|
|
else if (wm_issolid((player[cp].x>>CSF)+0, (player[cp].y>>CSF)+8, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedl = 1; }
|
|
else if (wm_issolid((player[cp].x>>CSF)+0, (player[cp].y>>CSF)+13, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedl = 1; }
|
|
|
|
// U
|
|
if (wm_issolid((player[cp].x>>CSF)+1, (player[cp].y>>CSF)-1, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedu = 1; }
|
|
else if (wm_issolid((player[cp].x>>CSF)+4, (player[cp].y>>CSF)-1, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedu = 1; }
|
|
else if (wm_issolid((player[cp].x>>CSF)+7, (player[cp].y>>CSF)-1, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedu = 1; }
|
|
|
|
// D
|
|
if (wm_issolid((player[cp].x>>CSF)+1, (player[cp].y>>CSF)+14, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedd = 1; }
|
|
else if (wm_issolid((player[cp].x>>CSF)+4, (player[cp].y>>CSF)+14, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedd = 1; }
|
|
else if (wm_issolid((player[cp].x>>CSF)+7, (player[cp].y>>CSF)+14, pCKP->Control.levelcontrol.levels_completed))
|
|
{ player[cp].blockedd = 1; }
|
|
|
|
}
|
|
|
|
// select proper player direction
|
|
void gamepdo_wm_setdir(int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
int exception;
|
|
player[cp].dpadcount = 0;
|
|
if (player[cp].playcontrol[PA_X] < 0) { player[cp].pdir = LEFT; player[cp].dpadcount++; }
|
|
if (player[cp].playcontrol[PA_X] > 0) { player[cp].pdir = RIGHT; player[cp].dpadcount++; }
|
|
if (player[cp].playcontrol[PA_Y] < 0) { player[cp].pdir = UP; player[cp].dpadcount++; }
|
|
if (player[cp].playcontrol[PA_Y] > 0) { player[cp].pdir = DOWN; player[cp].dpadcount++; }
|
|
|
|
exception = 0;
|
|
if (player[cp].pshowdir==UP && !player[cp].playcontrol[PA_Y] < 0) exception = 1;
|
|
if (player[cp].pshowdir==DOWN && !player[cp].playcontrol[PA_Y] > 0) exception = 1;
|
|
if (player[cp].pshowdir==LEFT && !player[cp].playcontrol[PA_X] < 0) exception = 1;
|
|
if (player[cp].pshowdir==RIGHT && !player[cp].playcontrol[PA_X] > 0) exception = 1;
|
|
if ((player[cp].playcontrol[PA_Y] < 0) && (player[cp].playcontrol[PA_Y] > 0)) player[cp].pshowdir = DOWN;
|
|
if ((player[cp].playcontrol[PA_X] < 0) && (player[cp].playcontrol[PA_X] > 0)) player[cp].pshowdir = RIGHT;
|
|
|
|
if (player[cp].dpadcount==1 || player[cp].dpadlastcount==0 || exception)
|
|
{
|
|
player[cp].pshowdir = player[cp].pdir;
|
|
}
|
|
player[cp].dpadlastcount = player[cp].dpadcount;
|
|
}
|
|
|
|
// handles inertia and friction for the Y direction
|
|
// (this is where the inertia is actually applied to playx)
|
|
void gamepdo_InertiaAndFriction_Y(int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
if (player[cp].hideplayer)
|
|
{
|
|
player[cp].pinertia_y = 0;
|
|
return;
|
|
}
|
|
|
|
// if we hit a solid object kill all inertia
|
|
if (player[cp].pinertia_y > 0 && player[cp].blockedd)
|
|
{
|
|
player[cp].pinertia_y = 0;
|
|
}
|
|
else if (player[cp].pinertia_y < 0 && player[cp].blockedu)
|
|
{
|
|
player[cp].pinertia_y = 0;
|
|
}
|
|
|
|
// apply pinertia_y
|
|
player[cp].y += player[cp].pinertia_y;
|
|
|
|
// if we stopped walking (i.e. LRUD not held down) apply friction
|
|
//if (!player[cp].keytable[KDOWN] && !player[cp].keytable[KUP])
|
|
if (!player[cp].playcontrol[PA_Y])
|
|
{
|
|
// and apply friction to pinertia_y
|
|
if (player[cp].pfriction_timer_y > PFRICTION_RATE_WM)
|
|
{
|
|
if (player[cp].pinertia_y < 0) player[cp].pinertia_y++;
|
|
else if (player[cp].pinertia_y > 0) player[cp].pinertia_y--;
|
|
player[cp].pfriction_timer_y = 0;
|
|
}
|
|
else player[cp].pfriction_timer_y++;
|
|
}
|
|
}
|
|
|
|
void gamepdo_wm_AllowEnterLevel(int cp, stCloneKeenPlus *pCKP)
|
|
{
|
|
int lvl;
|
|
int x,y,i,o;
|
|
int destx, desty;
|
|
int telfrom, teldest, telsnap;
|
|
|
|
stLevelControl *p_levelcontrol;
|
|
|
|
// 3859 <-> 3889
|
|
// 3911 -> 3955
|
|
// 3877 <-> 3922
|
|
|
|
// 4032 -> 3842
|
|
// 3842 -> 3877
|
|
// 4063 -> 4094
|
|
// 4094 -> 4072
|
|
// 4072 -> 3980
|
|
// 3980 -> 4032 nosnap
|
|
|
|
// table of teleporters (mostly for ep3). player will teleport from
|
|
// a teleporter in a teleport_from entry, to the teleporter
|
|
// in the matching teleport_dest entry. Thus 3859 leads to 3889, etc.
|
|
// if the snap entry is 1 the screen will snap to center on the new
|
|
// position, otherwise it will smoothly scroll over to it
|
|
#define NUM_TPORTS 20
|
|
int teleport_from[NUM_TPORTS+1] = {4014,4032,3842,4063,4094,4072,3980,3859,3889,3911,3877,3922,3947,4025,3988,3955,41,38,0};
|
|
int teleport_dest[NUM_TPORTS+1] = {4072,3842,3877,4094,4072,3980,4032,3889,3859,3955,3922,3877,4025,3988,3911,3889,38,41,0};
|
|
int teleport_snap[NUM_TPORTS+1] = {1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,0 ,0 ,0};
|
|
|
|
p_levelcontrol = &(pCKP->Control.levelcontrol);
|
|
|
|
if (fade.mode != NO_FADE) return;
|
|
if (player[cp].hideplayer) return; // don't execute function while teleporting
|
|
|
|
if ((player[cp].playcontrol[PA_JUMP] || player[cp].playcontrol[PA_POGO]) && !player[cp].wm_lastenterstate)
|
|
{ // trying to enter a level (or use a teleporter, etc)
|
|
|
|
// get level/object marker beneath player
|
|
x = (player[cp].x>>CSF)+4;
|
|
y = (player[cp].y>>CSF)+4;
|
|
lvl = map.objectlayer[x>>4][y>>4];
|
|
if (!lvl)
|
|
{
|
|
y = (player[cp].y>>CSF)+8;
|
|
lvl = map.objectlayer[x>>4][y>>4];
|
|
}
|
|
|
|
x = x >> 4 << 4;
|
|
y = y >> 4 << 4;
|
|
|
|
if (lvl)
|
|
{
|
|
/* handle special map objects, and standard levels too */
|
|
|
|
// look through the teleporter tables and see if this is
|
|
// a teleporter
|
|
telfrom = 0;
|
|
for(i=0;i<NUM_TPORTS;i++)
|
|
{
|
|
if (teleport_from[i]==lvl)
|
|
{ // we found it! it IS a teleporter!
|
|
telfrom = teleport_from[i];
|
|
teldest = teleport_dest[i];
|
|
telsnap = teleport_snap[i];
|
|
break;
|
|
}
|
|
else if (teleport_from[i]==0)
|
|
{ // nope. it's not a teleporter.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// need to teleport somewhere?
|
|
if (telfrom)
|
|
{
|
|
if (map_findobject(teldest, &destx, &desty))
|
|
{
|
|
o = spawn_object(x<<CSF,y<<CSF,OBJ_TELEPORTER);
|
|
objects[o].ai.teleport.direction = TELEPORTING_OUT;
|
|
objects[o].ai.teleport.destx = destx << 4 << CSF;
|
|
objects[o].ai.teleport.desty = desty << 4 << CSF;
|
|
objects[o].ai.teleport.whichplayer = cp;
|
|
objects[o].ai.teleport.NoExitingTeleporter = 0;
|
|
objects[o].ai.teleport.snap = telsnap;
|
|
if (p_levelcontrol->episode==3)
|
|
{
|
|
objects[o].ai.teleport.baseframe = TELEPORT_BASEFRAME_EP3;
|
|
objects[o].ai.teleport.idleframe = TELEPORT_IDLEFRAME_EP3;
|
|
}
|
|
else
|
|
{ // the teleporter in ep1
|
|
objects[o].ai.teleport.baseframe = TELEPORT_GRAY_BASEFRAME_EP1;
|
|
objects[o].ai.teleport.idleframe = TELEPORT_GRAY_IDLEFRAME_EP1;
|
|
}
|
|
g_pSound->playStereofromCoord(SOUND_TELEPORT, PLAY_NOW, objects[player[cp].useObject].scrx);
|
|
|
|
player[cp].hideplayer = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(lvl)
|
|
{
|
|
case NESSIE_PATH: break;
|
|
case NESSIE_PAUSE: break;
|
|
case NESSIE_MOUNTPOINT: break;
|
|
|
|
// ep1 bonus teleporter
|
|
case LVLS_TELEPORTER_BONUS:
|
|
o = spawn_object(x<<CSF,y<<CSF,OBJ_TELEPORTER);
|
|
objects[o].ai.teleport.direction = TELEPORTING_OUT;
|
|
objects[o].ai.teleport.destx = TELEPORT_BONUS_DESTX;
|
|
objects[o].ai.teleport.desty = TELEPORT_BONUS_DESTY;
|
|
objects[o].ai.teleport.whichplayer = cp;
|
|
objects[o].ai.teleport.baseframe = TELEPORT_RED_BASEFRAME_EP1;
|
|
objects[o].ai.teleport.idleframe = TELEPORT_RED_IDLEFRAME_EP1;
|
|
objects[o].ai.teleport.NoExitingTeleporter = 1;
|
|
objects[o].ai.teleport.snap = 1;
|
|
player[cp].hideplayer = 1;
|
|
break;
|
|
|
|
case LVLS_SHIP:
|
|
if (p_levelcontrol->episode==1)
|
|
{
|
|
YourShipNeedsTheseParts(pCKP);
|
|
}
|
|
else
|
|
{
|
|
ShipEp3(pCKP);
|
|
}
|
|
break;
|
|
|
|
default: // a regular level
|
|
p_levelcontrol->chglevelto = (lvl & 0x7fff);
|
|
endlevel(1, pCKP);
|
|
g_pMusicPlayer->stop();
|
|
g_pSound->playStereofromCoord(SOUND_ENTER_LEVEL, PLAY_NOW, objects[player[cp].useObject].scrx);
|
|
|
|
break;
|
|
|
|
} // end switch(level)
|
|
} // end isn't a teleporter
|
|
} // end if(lvl)
|
|
} // end trying to enter a level
|
|
|
|
player[cp].wm_lastenterstate = (player[cp].playcontrol[PA_JUMP] || player[cp].playcontrol[PA_POGO]);
|
|
}
|
|
|
|
char wm_issolid(int xb, int yb, int *levels_completed)
|
|
{
|
|
|
|
// for map tiles solidl and solidr are always gonna be the same...
|
|
// so we can get away with this.
|
|
if ( TileProperty[getmaptileat(xb, yb)][BLEFT] ) return 1;
|
|
//if (tiles[getmaptileat(xb, yb)].solidl) return 1;
|
|
|
|
unsigned int level;
|
|
level = getlevelat(xb, yb);
|
|
|
|
if (level & 0x8000)
|
|
{
|
|
if(levels_completed[map.objectlayer[xb>>4][yb>>4] & 0x7fff] && options[OPT_LVLREPLAYABILITY].value) // check if level is done, but can be replayed
|
|
return 0;
|
|
|
|
if(g_pInput->getHoldedKey(KTAB) && g_pInput->getHoldedKey(KLSHIFT))
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// if nessie is at a mount point near the player, mounts him!
|
|
void MountNessieIfAvailable(int cp)
|
|
{
|
|
char AtSameMountPoint;
|
|
|
|
if (!player[cp].mounted)
|
|
{
|
|
// is nessie paused?
|
|
if (objects[NessieObjectHandle].ai.nessie.pausetimer)
|
|
{
|
|
// is she at the same mount point the player is?
|
|
if (player[cp].y>>CSF>>4 < map.ysize>>2 && objects[NessieObjectHandle].y>>CSF>>4 < map.ysize>>2) AtSameMountPoint = 1; // both at mortimer's castle
|
|
else if (player[cp].y>>CSF>>4 > map.ysize>>1 && objects[NessieObjectHandle].y>>CSF>>4 > map.ysize>>1) AtSameMountPoint = 1; // both at secret island
|
|
else AtSameMountPoint = 0;
|
|
|
|
if (AtSameMountPoint)
|
|
{
|
|
objects[NessieObjectHandle].ai.nessie.mounted[cp] = 1;
|
|
player[cp].mounted = 1;
|
|
player[cp].hideplayer = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AllowMountUnmountNessie(int cp)
|
|
{
|
|
int objmarker;
|
|
if (!player[cp].mounted)
|
|
{ // not mounted. find out if he's trying to mount
|
|
// if the upper quarter of the map (mortimer's castle mount point)
|
|
// he's trying to mount if he's on a NESSIE_MOUNTPOINT object marker
|
|
// and he's going right and is blockedr and/or is going down and is
|
|
// blockedd. in the bottom quarter (secret island mount point)
|
|
// it's up and blockedu.
|
|
|
|
// make sure he's on a mount point
|
|
objmarker = getlevelat((player[cp].x>>CSF)+4, (player[0].y>>CSF)+9);
|
|
if (objmarker != NESSIE_MOUNTPOINT) return;
|
|
|
|
// is he trying to mount?
|
|
if (player[cp].y>>CSF>>4 < map.ysize>>2)
|
|
{ // at mortimer's castle mount point
|
|
if ( (player[cp].playcontrol[PA_X] > 0 && player[cp].blockedr) ||
|
|
(player[cp].playcontrol[PA_Y] > 0 && player[cp].blockedd))
|
|
{
|
|
// YES! if nessie is at that mount point, mount her!!
|
|
MountNessieIfAvailable(cp);
|
|
}
|
|
}
|
|
else
|
|
{ // at secret island mount point
|
|
if (player[cp].playcontrol[PA_Y] < 0 && player[cp].blockedu)
|
|
{
|
|
MountNessieIfAvailable(cp);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // mounted. find out if he's trying to unmount.
|
|
if (objects[NessieObjectHandle].ai.nessie.pausetimer)
|
|
{ // nessie is paused
|
|
if (objects[NessieObjectHandle].y>>CSF>>4 < map.ysize>>2)
|
|
{ // nessie is at mortimer's castle mount point
|
|
if (player[cp].playcontrol[PA_Y] < 0)
|
|
{
|
|
// unmount nessie
|
|
objects[NessieObjectHandle].ai.nessie.mounted[cp] = 0;
|
|
player[cp].mounted = 0;
|
|
player[cp].hideplayer = 0;
|
|
}
|
|
}
|
|
else if (objects[NessieObjectHandle].y>>CSF>>4 > map.ysize>>1)
|
|
{ // nessie is at secret island mount point
|
|
if (player[cp].playcontrol[PA_Y] > 0)
|
|
{
|
|
// unmount nessie
|
|
objects[NessieObjectHandle].ai.nessie.mounted[cp] = 0;
|
|
player[cp].mounted = 0;
|
|
player[cp].hideplayer = 0;
|
|
player[cp].y += (18<<CSF);
|
|
player[cp].x += (8<<CSF);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|