/*========================================================================= player.cpp Author: PKG Created: Project: Spongebob Purpose: Copyright (c) 2001 Climax Development Ltd ===========================================================================*/ /*---------------------------------------------------------------------- Includes -------- */ #include "system\vid.h" #include "player\player.h" #ifndef __PAD_PADS_H__ #include "pad\pads.h" #endif #ifndef __PAD_VIBE_H__ #include "pad\vibe.h" #endif #ifndef __GAME_GAME_H__ #include "game\game.h" #endif #ifndef __GAME_GAMESLOT_H__ #include "game\gameslot.h" #endif #ifndef __LAYER_COLLISION_H__ #include "level\layercollision.h" #endif #ifndef __PLAYER_PMODES_H__ #include "player\pmodes.h" #endif #ifndef __PLAYER_PMBLOON_H__ #include "player\pmbloon.h" #endif #ifndef __PLAYER_PMBUBBLE_H__ #include "player\pmbubble.h" #endif #ifndef __PLAYER_PMNET_H__ #include "player\pmnet.h" #endif #ifndef __PLAYER_PMCHOP_H__ #include "player\pmchop.h" #endif #ifndef __PLAYER_PMCORAL_H__ #include "player\pmcoral.h" #endif #ifndef __PLAYER_PMDEAD_H__ #include "player\pmdead.h" #endif #ifndef __PLAYER_PMFLY_H__ #include "player\pmfly.h" #endif #ifndef __PLAYER_PMJELLY_H__ #include "player\pmjelly.h" #endif #ifndef __PLAYER_PMCART_H__ #include "player\pmcart.h" #endif #ifndef __PLAYER_PMSWAL_H__ #include "player\pmswal.h" #endif #ifndef __GFX_FONT_H__ #include "gfx\font.h" #endif #ifndef __GFX_SPRBANK_H__ #include "gfx\sprbank.h" #endif #ifndef __GFX_OTPOS_H__ #include "gfx\otpos.h" #endif #ifndef __PLATFORM_PLATFORM_H__ #include "platform\platform.h" #endif #ifndef __PICKUPS_PICKUP_H__ #include "pickups\pickup.h" #endif #ifndef __MATHTABLE_HEADER__ #include "utils\mathtab.h" #endif #ifndef __GAME_CONVO_H__ #include "game\convo.h" #endif #ifndef __STRING_ENUMS__ #include #endif /* Std Lib ------- */ /* Data ---- */ #ifndef __SPR_SPRITES_H__ #include #endif #ifndef __ANIM_SPONGEBOB_HEADER__ #include #endif #ifndef __ANIM_SPONGEBOB_NET_HEADER__ #include #endif #ifndef __ANIM_SPONGEBOB_CORALBLOWER_HEADER__ #include #endif #ifndef __ANIM_SPONGEBOB_JELLYLAUNCHER_HEADER__ #include #endif #ifndef __ANIM_SPONGEBOB_WAND_HEADER__ #include #endif #ifndef __ANIM_SPONGEBOB_JELLYFISH_HEADER__ #include #endif #ifndef __ANIM_SPONGEBOB_GLOVE_HEADER__ #include #endif #include "fx\fx.h" /*---------------------------------------------------------------------- Tyepdefs && Defines ------------------- */ #ifdef __VERSION_DEBUG__ //#define _RECORD_DEMO_MODE_ #define _STATE_DEBUG_ #endif /*---------------------------------------------------------------------- Structure defintions -------------------- */ // Two dice. One says 'Re' on every face, the other says 'boot', // 'install', 'try', 'tire', 'sume' and 'number' /*---------------------------------------------------------------------- Function Prototypes ------------------- */ /*---------------------------------------------------------------------- Vars ---- */ #ifdef _RECORD_DEMO_MODE_ #include "player\demoplay.h" #define MAX_DEMO_SIZE 512 // So max size of a demo is 1k #define MAX_DEMO_TIME_IN_FRAMES 30*60 // Recorded demo will last 30 seconds static CDemoPlayer::demoPlayerControl s_demoControls[MAX_DEMO_SIZE]={{PI_NONE,0}}; static int s_demoSize=0; static int s_demoFrameCount=0; static void writeDemoControls() { char filename[32]; int fh; int fc=MAX_DEMO_TIME_IN_FRAMES; sprintf(filename,"demo____.dmo"); fh=PCcreat((char *)filename,0); ASSERT(fh!=-1); PCwrite(fh,(char*)&fc,sizeof(fc)); // frame count PCwrite(fh,(char*)&s_demoSize,sizeof(s_demoSize)); // demo size for(int i=0;iinitialise(&standardFont); m_fontBank->setOt(POWERUPUI_OT); m_scalableFontBank=new ("PlayerFont") ScalableFontBank(); m_scalableFontBank->initialise(&standardFont); m_scalableFontBank->setOt(POWERUPUI_OT); m_actorGfx=CActorPool::GetActor(ACTORS_SPONGEBOB_SBK); for(i=0;iinitialise(this); } CurrentPrompt=-1; PromptRGB=0; PromptFade=0; m_animNo=0; m_animFrame=0; setFacing(FACING_RIGHT); m_currentPlayerModeClass=NULL; m_lastModeBeforeDeath=PLAYER_MODE_BASICUNARMED; // Player will then respawn into this mode respawn(); m_lastPadInput=m_padInput=PI_NONE; setCartCam(false); setReverseCameraMovement(false); resetPlayerCollisionSizeToBase(); m_lockoutPlatform = NULL; m_divingHelmet=false; setIsInWater(true); #ifdef __USER_paul__ /* registerAddon(PLAYER_ADDON_GLOVE); registerAddon(PLAYER_ADDON_NET); registerAddon(PLAYER_ADDON_CORALBLOWER); registerAddon(PLAYER_ADDON_JELLYLAUNCHER); registerAddon(PLAYER_ADDON_BUBBLEWAND); registerAddon(PLAYER_ADDON_JELLYFISHINNET); */ #endif } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::shutdown() { int i; for(i=0;ishutdown(); } for(i=0;idump(); delete m_scalableFontBank; m_fontBank->dump(); delete m_fontBank; CPlayerThing::shutdown(); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ static int oldmode=-1; int newmode=-1; void CPlayer::think(int _frames) { int i; #ifdef __USER_daveo__ if(PadGetDown(0)&PAD_R1) { TestFX(getPos(),this); } #endif #ifdef _STATE_DEBUG_ if(PadGetDown(0)&PAD_L1&&m_currentMode!=PLAYER_MODE_DEAD) { oldmode=m_currentMode; newmode=PLAYER_MODE_FLY; } else if(oldmode!=-1&&!(PadGetHeld(0)&PAD_L1)) { newmode=oldmode; oldmode=-1; } #endif if(newmode!=-1) { setMode((PLAYER_MODE)newmode); newmode=-1; } if(!isDead()) { CThing *platform; platform=isOnPlatform(); if(platform) { if ( ( (CNpcPlatform *) platform )->isCart() ) { if ( m_currentMode != PLAYER_MODE_CART && m_currentMode != PLAYER_MODE_DEAD ) { setMode( PLAYER_MODE_CART ); } } else { DVECTOR posDelta; posDelta=platform->getPosDelta(); posDelta.vy = 0; shove(posDelta); int platformOffset = ( ( CNpcPlatform* ) platform )->getHeightFromPlatformAtPosition( Pos.vx, Pos.vy ); int height = 16; for ( int testX = -1 ; testX < 2 ; testX++ ) { int testHeight = CGameScene::getCollision()->getHeightFromGround(Pos.vx + (checkx * testX),Pos.vy,16); if ( testHeight < height ) { height = testHeight; } } // Hmm.. this *almost* stops him elevating through walls :/ if ( platformOffset < height ) { int goingToHitWall=false; int i; for(i=-1;i<2;i++) { int x=Pos.vx+(checkx*i); int y=Pos.vy-HEIGHT_FOR_HEAD_COLLISION; //if(getHeightFromGroundNoPlatform(x,y,16)>=0&&getHeightFromGroundNoPlatform(x,y+platformOffset,16)<=0&&((CGameScene::getCollision()->getCollisionBlock(x,y+platformOffset)&COLLISION_TYPE_MASK)!=COLLISION_TYPE_FLAG_NORMAL)) if(getHeightFromGroundNoPlatform(x,y+platformOffset,16)<=0&&((CGameScene::getCollision()->getCollisionBlock(x,y+platformOffset)&COLLISION_TYPE_MASK)!=COLLISION_TYPE_FLAG_NORMAL)) { goingToHitWall=true; setLockoutPlatform( m_platform ); break; } } if(!goingToHitWall) { Pos.vy += platformOffset; } } } } } m_allowConversation=false; m_xMove = Pos.vx; m_moveLeftRight = 0; /// #ifdef __USER_paul__ //if(PadGetDown(0)&PAD_TRIANGLE) //{ // addLife(); //} #endif /// // Pending teleport? if(m_teleportPending) { processTeleportToCommand(); } for(i=0;i<_frames;i++) { updatePadInput(); // Only do the weapon change stuff on the first frame. As the buttons pressed do not // change over these frames there is no point in doing it every frame if(m_currentMode!=PLAYER_MODE_DEAD) { if(i==0&&m_currentPlayerModeClass->getState()!=STATE_CELEBRATE) { m_tryingToManuallyPickupWeapon=false; m_tryingToAutomaticallyPickupWeapon=false; // Weapon collect/drop/swap stuff.. if(m_currentMode==PLAYER_MODE_BASICUNARMED) { // Always trying to pick up weapon if unarmed... means that when SB walks // over an item whilst unarmed, he automatically picks it up m_tryingToAutomaticallyPickupWeapon=true; } bool CanChange=true; switch(m_currentPlayerModeClass->getState()) { case STATE_BUTTBOUNCE: case STATE_BUTTFALL: case STATE_BUTTLAND: case STATE_BUTTBOUNCEUP: CanChange=false; break; default: break; } if(CanChange && (m_currentMode!=PLAYER_MODE_CART && getPadInputDown()&PI_WEAPONCHANGE)) { // If already armed then drop current weapon if(m_currentMode!=PLAYER_MODE_BASICUNARMED) { static const int s_pickupsToDrop[NUM_PLAYERMODES]= { -1, // PLAYER_MODE_BASICUNARMED -1, // PLAYER_MODE_FULLUNARMED -1, // PLAYER_MODE_BALLOON PICKUP__BUBBLE_WAND, // PLAYER_MODE_BUBBLE_MIXTURE PICKUP__NET, // PLAYER_MODE_NET PICKUP__CORAL_BLOWER, // PLAYER_MODE_CORALBLOWER PICKUP__JELLY_LAUNCHER, // PLAYER_MODE_JELLY_LAUNCHER -1, // PLAYER_MODE_DEAD -1, // PLAYER_MODE_FLY -1, // PLAYER_MODE_CART -1, // PLAYER_MODE_SWALLOW }; int pickupToDrop; pickupToDrop=s_pickupsToDrop[m_currentMode]; if(pickupToDrop!=-1) { DVECTOR pickupPos; CBasePickup *pickup; pickupPos.vx=Pos.vx; pickupPos.vy=Pos.vy-30; pickup=createPickup((PICKUP_TYPE)pickupToDrop,&pickupPos); pickup->setPos(&pickupPos); ((CBaseWeaponPickup*)pickup)->setHasBeenCollected(); } setMode(PLAYER_MODE_BASICUNARMED); } // Now trying to pick up a weapon.. m_tryingToManuallyPickupWeapon=true; } } } else { // Don't try to pick up weapons when dead.. doh! m_tryingToAutomaticallyPickupWeapon=false; m_tryingToManuallyPickupWeapon=false; } int level=GameScene.getLevelNumber(); // Out of spats? if(m_numSpatulasHeld==0 && level!=5) { int oldTimer=m_spatulaWarningTimer; m_spatulaWarningTimer++; if((m_spatulaWarningTimer&64)!=(oldTimer&64)) { CSoundMediator::playSfx(CSoundMediator::SFX_BEEP10); } } // Trying to converate? if(m_allowConversation==false&& m_currentPlayerModeClass->canConverse()&& getPadInputDown()&PI_UP) { m_allowConversation=true; } // Think for the current player mode int oldBlock; DVECTOR oldPos; oldBlock=CGameScene::getCollision()->getCollisionBlock(Pos.vx,Pos.vy)&COLLISION_TYPE_MASK; oldPos=Pos; m_currentPlayerModeClass->think(); if(oldBlock==COLLISION_TYPE_FLAG_DESTRUCTABLE_FLOOR&& (Pos.vx>>4!=oldPos.vx>>4||Pos.vy>>4!=oldPos.vy>>4)) { GameScene.GetLevel().destroyMapTile(oldPos); } // Is player stood on any special collision? for(int j=0;j<2;j++) { int x=Pos.vx+((j==0)?-checkx:+checkx); if(getHeightFromGroundNoPlatform(x,Pos.vy,5)==0) { int block; block=CGameScene::getCollision()->getCollisionBlock(x,Pos.vy)&COLLISION_TYPE_MASK; // Conveyor belt movement if(block==COLLISION_TYPE_FLAG_MOVE_LEFT) { moveHorizontal(-1); break; } else if(block==COLLISION_TYPE_FLAG_MOVE_RIGHT) { moveHorizontal(+1); break; } } } // Powerups if(m_squeakyBootsTimer) { m_squeakyBootsTimer--; } if(m_invincibilityRingTimer) { m_invincibilityRingTimer--; } // Flashing.. if(m_invincibleFrameCount) { m_invincibleFrameCount--; } // Camera scroll.. if(m_cartCamActive) { // Just force the cam eo think we are running right so that the cam // is always in the right place when in a cart m_cameraXScrollDir=-1; } if(m_cameraXScrollDir==-1) { if(m_cameraXScrollPos>-(CAMERA_SCROLLLIMIT*CAMERA_TILESIZE)<(CAMERA_SCROLLLIMIT*CAMERA_TILESIZE)<returnsafespace) { if(returntimeoutcount0) { m_cameraXScrollPos=0; } } else if(m_cameraXScrollPos>0) { m_cameraXScrollPos-=returnspeed; if(m_cameraXScrollPos<0) { m_cameraXScrollPos=0; } } } } } #endif // Stop the player vanishing off the edge of the telly.. if(m_currentMode!=PLAYER_MODE_DEAD) // Lock camera when player is dead { if(Pos.vxm_playerPosLimitBox.x2) Pos.vx=m_playerPosLimitBox.x2; if(Pos.vym_playerPosLimitBox.y2) Pos.vy=m_playerPosLimitBox.y2; } // Falling camera lag if(m_isFalling) { if(m_fallFrames>CAMERA_FALL_FRAME_THRESHOLD) { if(m_cameraFallYScrollSpeed0) { m_cameraFallYScrollPos=0; } } m_fallFrames=0; m_cameraFallYScrollSpeed=0; } // Look around int pad=getPadInputHeld(); if(pad&PI_UP&&canDoLookAround()) { if(m_padLookAroundTimer>0) { m_padLookAroundTimer=0; } else if(m_padLookAroundTimer>-looktimeout) { m_padLookAroundTimer--; } else if(m_cameraLookOffset>-lookmaxoffsetup) { m_cameraLookOffset-=lookspeed; if(m_cameraLookOffset<-lookmaxoffsetup) { m_cameraLookOffset=-lookmaxoffsetup; } } } else if(pad&PI_DOWN&&canDoLookAround()) { if(m_padLookAroundTimer<0) { m_padLookAroundTimer=0; } else if(m_padLookAroundTimerlookmaxoffsetdown) { m_cameraLookOffset=lookmaxoffsetdown; } } } else { m_padLookAroundTimer=0; } // Return to centre if(m_padLookAroundTimer==0&&m_cameraLookOffset<0) { m_cameraLookOffset+=lookreturnspeed; if(m_cameraLookOffset>0) { m_cameraLookOffset=0; } } if(m_padLookAroundTimer==0&&m_cameraLookOffset>0) { m_cameraLookOffset-=lookreturnspeed; if(m_cameraLookOffset<0) { m_cameraLookOffset=0; } } // Automatic anim sfx playAnimFrameSfx(m_animNo,m_animFrame); } m_xMove = Pos.vx - m_xMove; ASSERT(!(getIsInWater()==false&&isWearingDivingHelmet()==false)); if(isWearingDivingHelmet()) { if(getIsInWater()==false&&m_currentMode!=PLAYER_MODE_DEAD&&m_currentMode!=PLAYER_MODE_FLY) { // Out of water and wearing helmet! // Drain water/health int oldLevel=m_healthWaterLevel/WATER_COUNTER_SECONDTIME; m_healthWaterLevel-=waterDrainSpeed*_frames; if(oldLevel<=6&&oldLevel>m_healthWaterLevel/WATER_COUNTER_SECONDTIME) { CSoundMediator::playSfx(CSoundMediator::SFX_BEEP5); } if(m_healthWaterLevel<=0) { dieYouPorousFreak(DEATHTYPE__DRYUP); CSoundMediator::playSfx(CSoundMediator::SFX_BEEP5); } // Breath sound m_helmetSoundTimer+=_frames; if(m_helmetSoundTimer>150+(m_healthWaterLevel>>WATERLEVELSHIFT)) { CSoundMediator::playSfx(CSoundMediator::SFX_SPONGEBOB_DIVING_HELMET); m_helmetSoundTimer=0; } } else if(getIsInWater()) { // Back in water - fill up helmet! :) m_healthWaterLevel+=waterSoakUpSpeed; if(m_healthWaterLevel>WATERMAXHEALTH) { m_healthWaterLevel=WATERMAXHEALTH; } } } // Ledge look-ahead stuff if(m_ledgeLookAhead) { if(m_ledgeLookTimerlimit) { m_ledgeLookOffset=limit; } } else if(m_ledgeLookOffset>limit) { // Look up m_ledgeLookOffset-=ledgeSpeedIn*_frames; if(m_ledgeLookOffset0) { // Relax from look down m_ledgeLookOffset-=ledgeSpeedOut*_frames; if(m_ledgeLookOffset<=0) { m_ledgeLookOffset=0; m_ledgeLookTimer=0; } } else if(m_ledgeLookOffset<0) { // Relax from look up m_ledgeLookOffset+=ledgeSpeedOut*_frames; if(m_ledgeLookOffset>=0) { m_ledgeLookOffset=0; m_ledgeLookTimer=0; } } } // Camera focus point stuff calcCameraFocusPointTarget(); for(i=0;i<_frames;i++) { m_currentCamFocusPoint.vx+=(m_currentCamFocusPointTarget.vx-m_currentCamFocusPoint.vx)>>cammove; m_currentCamFocusPoint.vy+=(m_currentCamFocusPointTarget.vy-m_currentCamFocusPoint.vy)>>cammove; } // Final camera position int yoff; yoff=m_cameraLookOffset+(m_ledgeLookOffset>>ledgeShift); if(yoff<-camerahardlockup)yoff=-camerahardlockup; else if(yoff>camerahardlockdown)yoff=camerahardlockdown; if(m_currentMode!=PLAYER_MODE_DEAD) // Lock camera when player is dead { m_cameraPos.vx=m_currentCamFocusPoint.vx; m_cameraPos.vy=m_currentCamFocusPoint.vy+yoff; CGameScene::shakeCamera(m_cameraPos); } // Limit camera scroll to the edges of the map if(m_cameraPos.vxm_cameraPosLimitBox.x2) m_cameraPos.vx=m_cameraPosLimitBox.x2; if(m_cameraPos.vym_cameraPosLimitBox.y2) m_cameraPos.vy=m_cameraPosLimitBox.y2; m_ignoreNewlyPressedButtonsOnPadThisThink=false; // Restore flipped camera setReverseCameraMovement(false); // flashing pants.. if(m_pantFlashTimer) { m_pantFlashTimer-=_frames; } CPlayerThing::think(_frames); promptThink(_frames); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::detectHazardousSurface() { // DOn't worry if on platform or already dead if(!isOnPlatform()&& m_currentMode!=PLAYER_MODE_DEAD) { int i; DVECTOR const &pos=getPlayerPos(); for(i=0;i<2;i++) { int x,height; x=pos.vx+(i==0?-checkx:checkx); height=CGameScene::getCollision()->getHeightFromGround(x,pos.vy); if(height<=0) { int block; block=CGameScene::getCollision()->getCollisionBlock(x,pos.vy)&COLLISION_TYPE_MASK; // Death? if(block==COLLISION_TYPE_FLAG_DEATH_LIQUID) { dieYouPorousFreak(DEATHTYPE__LIQUID); break; } else if(block==COLLISION_TYPE_FLAG_DEATH_INSTANT) { dieYouPorousFreak(DEATHTYPE__NORMAL); break; } } } } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int panim=-1; #include "gfx\prim.h" // (pkg) #ifdef _STATE_DEBUG_ int stateDebugX=280; int stateDebugY=180; #endif #ifdef __USER_paul__ #define NUM_LASTPOS 100 typedef struct { int vx,vy; int h; int onp; }POSMEM; static POSMEM lastpos[NUM_LASTPOS]; static int lastposnum=0; int drawlastpos=false; #endif #ifdef __USER_paul__ int mouth=-1,eyes=-1; #endif #include "gui\gui.h" void CPlayer::render() { CPlayerThing::render(); #ifdef _STATE_DEBUG_x char buf[100]; #ifdef __USER_paul__ sprintf(buf,"%04d (%02d) ,%04d (%02d)\ndfg:%+02d\nMode:%s",Pos.vx,Pos.vx&0x0f,Pos.vy,Pos.vy&0x0f,getHeightFromGround(Pos.vx,Pos.vy),s_modeText[m_currentMode]); #else sprintf(buf,"Pos: %04d,%04d",Pos.vx,Pos.vy); #endif m_fontBank->setTrans(0); m_fontBank->setColour(255,255,255); m_fontBank->print(stateDebugX,stateDebugY,buf); #endif #ifdef __USER_paul__ if(PadGetDown(0)&PAD_R1) { POSMEM clear={0,0,0,false}; for(int i=0;ivx=Pos.vx; p->vy=Pos.vy; p->onp=isOnPlatform()!=NULL; if(p->onp) { p->h=getHeightFromPlatformNoGround(Pos.vx,Pos.vy,150); } else { p->h=getHeightFromGround(Pos.vx,Pos.vy,150); } } if(drawlastpos) { POSMEM *p=lastpos; for(int i=0;ivx-m_cameraPos.vx; y=p->vy-m_cameraPos.vy; DrawLine(x-4,y-4,x+4,y+4,0,0,255,0); DrawLine(x-4,y+4,x+4,y-4,0,0,255,0); y=y+p->h; if(p->onp) { DrawLine(x-6,y,x+6,y,255,0,255,0); } else { DrawLine(x-6,y,x+6,y,128,0,255,0); } p++; } } #endif SpriteBank *sb=CGameScene::getSpriteBank(); // Render player if(m_animNo!=ANIM_SPONGEBOB_DONOTDRAW) { DVECTOR sbPos= { Pos.vx-m_cameraPos.vx, Pos.vy-m_cameraPos.vy+1, // Odd.. source sprites were moved up by one pixel at some stage }; renderSb(&sbPos,m_animNo,m_animFrame>>sbanimspeed); m_currentPlayerModeClass->render(&sbPos); } // UI // Don't draw it when the conversations are active - it's messy :/ if(!CConversation::isActive()) { int count,warn; sFrameHdr *fh; char countBuf[5]; int x,y; // Spat/token count warn=false; if(GameScene.getLevelNumber()!=5) { // Spat count count=m_numSpatulasHeld; fh=sb->getFrameHeader(FRM__SPATULA); if(m_numSpatulasHeld==0) { warn=true; } } else { // Token count count=CGameSlotManager::getSlotData()->getKelpTokenCollectedCount(GameScene.getChapterNumber()-1,GameScene.getLevelNumber()-1); fh=sb->getFrameHeader(FRM__TOKEN); } sprintf(countBuf,"x%d",count); x=SB_UI_XBASE; y=SB_UI_YBASE; sb->printFT4(fh,x,y,0,0,POWERUPUI_OT); x+=fh->W; if(warn&&m_spatulaWarningTimer&32) { m_fontBank->setColour(255,0,0); } m_fontBank->print(x,y,countBuf); m_fontBank->setColour(128,128,128); x+=SB_UI_GAP_FROM_SPAT_COUNT_TO_PICKUPS; if(isWearingBoots()) { // Boots int pickupX,pickupY; sFrameHdr *fh=sb->getFrameHeader(FRM__SHOE); if(m_squeakyBootsTimer>SQUEAKY_BOOTS_FLASH_TIME||m_squeakyBootsTimer&2) { sb->printFT4(fh,x,y,0,0,POWERUPUI_OT); sb->printFT4(fh,x+4,y+4,0,0,POWERUPUI_OT); } x+=fh->W+SB_UI_GAP_BETWEEN_ITEMS+4; } // Mode specific ui m_currentPlayerModeClass->renderModeUi(); // Fiving helmet.. now appears in same place as mode specific ui.. if(isWearingDivingHelmet()) { // Helmet POLY_FT4 *ft4; int x,y; int V,W,H,partH; x=POWERUPUI_ICONX; y=POWERUPUI_ICONY; ft4=sb->printFT4(FRM__WATERHILIGHT,x,y,0,0,POWERUPUI_OT); setSemiTrans(ft4,true); fh=sb->getFrameHeader(FRM__WATER); ft4=sb->printFT4(fh,0,0,0,0,POWERUPUI_OT); setSemiTrans(ft4,true); V=fh->V; W=fh->W; H=fh->H; partH=(H*(255-(m_healthWaterLevel>>WATERLEVELSHIFT)))>>8; if(partH>H)partH=H; setXYWH(ft4,x,y+(partH),W,H-partH); ft4->v0=V+(partH); ft4->v1=V+(partH); sb->printFT4(FRM__WATERMETER,x,y,0,0,POWERUPUI_OT); if(!getIsInWater()&&m_healthWaterLevel<(WATER_COUNTER_SECONDTIME*6)&&m_currentPlayerModeClass->getState()!=STATE_SOAKUP) { int digit; DVECTOR src={x+(W/2),y+(H/2)}; DVECTOR dst={INGAME_SCREENW/2,(INGAME_SCREENH/3)*1}; int frame; int digitX,digitY,scale; char buf[4]; int r,g,b; digit=m_healthWaterLevel/WATER_COUNTER_SECONDTIME; if(digit<0)digit=0; frame=WATER_COUNTER_SECONDTIME-(m_healthWaterLevel%WATER_COUNTER_SECONDTIME); if(frame>WATER_COUNTER_MOVINGTIME)frame=WATER_COUNTER_MOVINGTIME; digitX=src.vx+(((dst.vx-src.vx)*frame)/WATER_COUNTER_MOVINGTIME); digitY=src.vy+(((dst.vy-src.vy)*frame)/WATER_COUNTER_MOVINGTIME); scale=WATER_COUNTER_STARTSCALE+(((WATER_COUNTER_ENDSCALE-WATER_COUNTER_STARTSCALE)*frame)/WATER_COUNTER_MOVINGTIME); sprintf(buf,"%d",digit); m_scalableFontBank->setJustification(FontBank::JUST_CENTRE); m_scalableFontBank->setScale(scale); r=WATER_COUNTER_R2-(((WATER_COUNTER_R2-WATER_COUNTER_R1)*(m_healthWaterLevel))/(WATER_COUNTER_SECONDTIME*6)); g=WATER_COUNTER_G2-(((WATER_COUNTER_G2-WATER_COUNTER_G1)*(m_healthWaterLevel))/(WATER_COUNTER_SECONDTIME*6)); b=WATER_COUNTER_B2-(((WATER_COUNTER_B2-WATER_COUNTER_B1)*(m_healthWaterLevel))/(WATER_COUNTER_SECONDTIME*6)); m_scalableFontBank->setColour(r,g,b); m_scalableFontBank->print(digitX,digitY,buf); } x+=fh->W+SB_UI_GAP_BETWEEN_ITEMS; } } promptRender(); } /*---------------------------------------------------------------------- Function: Purpose: Pre-calcs the visible edges of the map ( ie: the hard limits for the camera pos ) Params: camera box ( in tiles ) Returns: ---------------------------------------------------------------------- */ void CPlayer::setCameraBox(CameraBox _cameraBox) { m_cameraPosLimitBox.x1=_cameraBox.x1; m_cameraPosLimitBox.y1=_cameraBox.y1; m_cameraPosLimitBox.x2=_cameraBox.x2-(32*MAP2D_BLOCKSTEPSIZE); // Made up numbers! :) (pkg); m_cameraPosLimitBox.y2=_cameraBox.y2-(16*MAP2D_BLOCKSTEPSIZE); m_playerPosLimitBox.x1=_cameraBox.x1+64; m_playerPosLimitBox.y1=_cameraBox.y1+64; m_playerPosLimitBox.x2=_cameraBox.x2-64; m_playerPosLimitBox.y2=_cameraBox.y2-64; } /*---------------------------------------------------------------------- Function: Purpose: Like the normal setRespawnPos() but plays the telephone ring sound if this is not the current respawn point. To be used from the respawn triggers so that they make the ring sound Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::setRespawnPosAndRingTelephone(DVECTOR const &_respawn) { if(m_respawnPos.vx!=_respawn.vx|| m_respawnPos.vy!=_respawn.vy) { CSoundMediator::playSfx(CSoundMediator::SFX_TELEPHONE_BOX); setRespawnPos(_respawn); } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::getHeightFromGround(int _x,int _y,int _maxHeight) { int height; height=CGameScene::getCollision()->getHeightFromGround(_x,_y,_maxHeight); if(height>=_maxHeight) { CThing *platform; platform=isOnPlatform(); if(platform) { int platformHeight=((CNpcPlatform*)platform)->getHeightFromPlatformAtPosition(_x,_y); if ( platformHeight < height ) { height = platformHeight; if(height>_maxHeight)height=_maxHeight; else if(height<-_maxHeight)height=-_maxHeight; } } } return height; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::getHeightFromPlatformNoGround(int _x,int _y,int _maxHeight) { CThing *platform; int height; platform=isOnPlatform(); ASSERT(platform); height=((CNpcPlatform*)platform)->getHeightFromPlatformAtPosition(_x,_y); if(height>_maxHeight)height=_maxHeight; else if(height<-_maxHeight)height=-_maxHeight; return height; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::getHeightFromGroundNoPlatform(int _x,int _y,int _maxHeight=32) { return( CGameScene::getCollision()->getHeightFromGround(_x,_y,_maxHeight) ); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::setLedgeLookAhead(int _lookAhead) { if(m_ledgeLookAhead!=_lookAhead) { m_ledgeLookAhead=_lookAhead; m_ledgeLookTimer=0; } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::addLife() { CGameSlotManager::GameSlot *gameSlot; gameSlot=CGameSlotManager::getSlotData(); if(gameSlot->m_livesm_lives++; } m_pantFlashTimer=PANT_FLASH_TIME; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::addSpatula(int Count=1) { m_numSpatulasHeld+=Count; if(!m_hasReceivedExtraLifeFor100Spats&&m_numSpatulasHeld==100) { addLife(); m_hasReceivedExtraLifeFor100Spats=true; } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ ATTACK_STATE CPlayer::getAttackState() { return m_currentPlayerModeClass->getAttackState(); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::registerAddon(PLAYER_ADDONS _addon) { if(!s_addonActorGfx[_addon]) { FileEquate sbk; sbk=s_addonActorPoolNames[_addon]; s_addonActorGfx[_addon]=CActorPool::GetActor(sbk); } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::setMode(PLAYER_MODE _mode) { int state; if(_mode==PLAYER_MODE_DEAD) { ASSERT(m_currentMode!=PLAYER_MODE_DEAD); m_lastModeBeforeDeath=m_currentMode; } resetPlayerCollisionSizeToBase(); if(m_currentPlayerModeClass) { state=m_currentPlayerModeClass->getState(); } else { state=STATE_IDLE; } m_currentMode=_mode; m_currentPlayerModeClass=s_playerModes[_mode]; m_currentPlayerModeClass->setInitialState(state); if(!m_currentPlayerModeClass->setState(state)) { m_currentPlayerModeClass->setState(STATE_IDLE); m_moveVelocity.vx=m_moveVelocity.vy=0; } m_currentPlayerModeClass->enter(); m_tryingToManuallyPickupWeapon=false; m_tryingToAutomaticallyPickupWeapon=false; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::getFacing() { return m_facing; } void CPlayer::setFacing(int _facing) { m_facing=_facing; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::getAnimFrame() { return m_animFrame; } void CPlayer::setAnimFrame(int _animFrame) { m_animFrame=_animFrame; } int CPlayer::getAnimFrameCount() { return m_actorGfx->getFrameCount(m_animNo)<springPlayerUp(_springHeight); CSoundMediator::playSfx(CSoundMediator::SFX_BOUNCY_PLATFORM,false,true); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::setFloating() { m_currentPlayerModeClass->setFloating(); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int haha=25; void CPlayer::floatPlayerUp() { m_moveVelocity.vy-=haha; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::teleportTo(int _x,int _y) { DVECTOR pos={_x,_y}; m_teleportPendingPos=pos; m_teleportPending=true; } void CPlayer::processTeleportToCommand() { ASSERT(m_teleportPending); CameraBox releaseCamBox={0,0,29999,29999}; setCameraBox(releaseCamBox); setPos(m_teleportPendingPos); setRespawnPos(m_teleportPendingPos); calcCameraFocusPointTarget(); m_currentCamFocusPoint=m_currentCamFocusPointTarget; m_teleportPending=false; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::playAnimFrameSfx(int _animNo,int _animFrame) { static int lastAnimNo=-1; static const AnimSfx *sfx; if(_animNo!=lastAnimNo) { // Lookup the new anim number and cache it for next time :) int i; sfx=s_animSfx; for(i=0;im_animNumber==_animNo) { break; } sfx++; } if(i==s_numAnimSfx) { // No sfx for this anim sfx=NULL; } lastAnimNo=_animNo; } // Are there any sounds for this anim at this frame? if(sfx) { const AnimFrameSfx *frameSfx; int i; ASSERT(sfx->m_numAnimFrameSfx); frameSfx=sfx->m_animFrameSfx; for(i=0;im_numAnimFrameSfx;i++) { if(m_animFrame==frameSfx->m_frame) { CSoundMediator::SFXID sfxId=frameSfx->m_sfxId; if(isWearingBoots()) { // Ugh.. horrible way to change the sfx when wearing squeaky boots (pkg) if(sfxId==CSoundMediator::SFX_SPONGEBOB_WALK_1)sfxId=CSoundMediator::SFX_SPONGEBOB_SQUEAKY_SHOES_1; else if(sfxId==CSoundMediator::SFX_SPONGEBOB_WALK_2)sfxId=CSoundMediator::SFX_SPONGEBOB_SQUEAKY_SHOES_2; } CSoundMediator::playSfx(sfxId); break; } if(m_animFramem_frame) { break; } frameSfx++; } } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::calcCameraFocusPointTarget() { m_currentCamFocusPointTarget.vx=Pos.vx+MAP2D_CENTRE_X-(m_cameraXScrollPos>>CAMERA_ACCURACYSHIFT); m_currentCamFocusPointTarget.vy=Pos.vy+MAP2D_CENTRE_Y-m_cameraFallYScrollPos; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::respawn() { // Set previous mode setMode(m_lastModeBeforeDeath); m_allowConversation=false; m_numSpatulasHeld=0; m_spatulaWarningTimer=0; m_healthWaterLevel=WATERMAXHEALTH; m_invincibleFrameCount=INVINCIBLE_FRAMES__START; m_recoveringFromElectrocution=false; m_helmetSoundTimer=0; Pos=m_respawnPos; m_cameraLookOffset=0; m_cameraXScrollDir=0; m_cameraXScrollPos=0; m_cameraFallYScrollPos=0; m_isFalling=false; m_fallFrames=false; m_cameraFallYScrollSpeed=0; calcCameraFocusPointTarget(); m_currentCamFocusPoint=m_currentCamFocusPointTarget; m_cameraPos.vx=m_currentCamFocusPoint.vx; m_cameraPos.vy=m_currentCamFocusPoint.vy; m_padLookAroundTimer=0; m_ledgeLookAhead=0; m_ledgeLookOffset=0; m_ledgeLookTimer=0; m_tryingToManuallyPickupWeapon=false; m_tryingToAutomaticallyPickupWeapon=false; m_squeakyBootsTimer=0; m_invincibilityRingTimer=0; m_bubbleAmmo=0; m_jellyAmmo=0; m_jellyfishAmmoCount=0; m_pantFlashTimer=0; m_hasReceivedExtraLifeFor100Spats=false; // Re-stock ammo if need be if(m_currentMode==PLAYER_MODE_BUBBLE_MIXTURE) { giveBubbleAmmoFromWeapon(); } else if(m_currentMode==PLAYER_MODE_JELLY_LAUNCHER) { giveJellyAmmoFromWeapon(); } m_moveVelocity.vx=m_moveVelocity.vy=0; clearPlatform(); updateCollisionArea(); m_ignoreNewlyPressedButtonsOnPadThisThink=true; m_canExitLevel=false; m_teleportPending=false; setSBOTPos(OTPOS__ACTOR_POS); } /*---------------------------------------------------------------------- Function: Purpose: Yes - This function is fat! It can be tidied up when all of the anims are finalised. Etracting the repeated code to a function will probly not improve things cos of the amount of data that would need to be passed about. Params: Returns: ---------------------------------------------------------------------- */ const int ExtraListYPos=50-32; void CPlayer::renderSb(DVECTOR *_pos,int _animNo,int _animFrame) { int playerMode; int trans; int addon; POLY_FT4 *ft4; // Y clipping ( damn useful when player is dead.. ;) if(_pos->vy<0-256||_pos->vy>256+256) { return; } if(m_currentMode==PLAYER_MODE_DEAD) { playerMode=m_lastModeBeforeDeath; } else { playerMode=m_currentMode; } trans=m_invincibleFrameCount||m_invincibilityRingTimer; // Render an addon? addon=s_addonNumbers[playerMode]; if(addon!=NO_ADDON) { s8 addonAnimNo=s_animMapNet[addon][_animNo]; if(addonAnimNo!=-1) { CActorGfx *addonGfx=s_addonActorGfx[addon]; if(addonGfx) { if(_animFrame>=addonGfx->getFrameCount(addonAnimNo)) { PAUL_DBGMSG("FRAME OVERRUN ON SPONGEBOB ADDON! ( %d vs %d )",m_actorGfx->getFrameCount(_animNo),addonGfx->getFrameCount(addonAnimNo)); } else { ft4=addonGfx->Render(*_pos,addonAnimNo,_animFrame,m_facing==FACING_RIGHT?0:1); setSemiTrans(ft4,trans); } } } } // Render JFish in a net? if(playerMode==PLAYER_MODE_NET&&getJellyFishAmmo()) { s8 addonAnimNo=s_animMapNet[PLAYER_ADDON_JELLYFISHINNET][_animNo]; if(addonAnimNo!=-1) { CActorGfx *addonGfx=s_addonActorGfx[PLAYER_ADDON_JELLYFISHINNET]; if(addonGfx) { if(_animFrame>=addonGfx->getFrameCount(addonAnimNo)) { PAUL_DBGMSG("FRAME OVERRUN ON SPONGEBOB JELLYFISH ADDON! ( %d vs %d )",m_actorGfx->getFrameCount(_animNo),addonGfx->getFrameCount(addonAnimNo)); } else { ft4=addonGfx->Render(*_pos,addonAnimNo,_animFrame,m_facing==FACING_RIGHT?0:1); setShadeTex(ft4,0); setRGB0(ft4,255,128,255); setSemiTrans(ft4,trans); } } } } // Render glove addon? if(m_currentMode==PLAYER_MODE_FULLUNARMED) { s8 addonAnimNo=s_animMapNet[PLAYER_ADDON_GLOVE][_animNo]; if(addonAnimNo!=-1) { CActorGfx *addonGfx=s_addonActorGfx[PLAYER_ADDON_GLOVE]; if(addonGfx) { if(_animFrame>=addonGfx->getFrameCount(addonAnimNo)) { PAUL_DBGMSG("FRAME OVERRUN ON SPONGEBOB GLOVE ADDON! ( %d vs %d )",m_actorGfx->getFrameCount(_animNo),addonGfx->getFrameCount(addonAnimNo)); } else { ft4=addonGfx->Render(*_pos,addonAnimNo,_animFrame,m_facing==FACING_RIGHT?0:1); setSemiTrans(ft4,trans); } } } } // Render SB ft4=m_actorGfx->Render(*_pos,_animNo,_animFrame,m_facing==FACING_RIGHT?0:1); CThing *platform; platform=isOnPlatform(); if(!isDead()) { if(platform) { if ( ( (CNpcPlatform *) platform )->isCart() ) { m_actorGfx->RotateScale( ft4, *_pos, ( (CNpcPlatform *) platform )->getCollisionAngle() , ONE, ONE ); } } } setSemiTrans(ft4,trans); // Pants? if(m_pantFlashTimer>0) { /* SpriteBank *sb=CGameScene::getSpriteBank(); sFrameHdr *fh; DVECTOR drawPos; int size; fh=sb->getFrameHeader(FRM__PANTS); drawPos=*_pos; drawPos.vy+=PANT_FLASH_Y_OFFSET; size=m_pantFlashTimer&8?4096:8192; ft4=sb->printRotatedScaledSprite(fh,drawPos.vx,drawPos.vy,size,size,0,0); */ if (FrameFlipFlag&1 && !GameScene.getIsPaused()) { m_scalableFontBank->setJustification(FontBank::JUST_CENTRE); m_scalableFontBank->setScale(511); m_scalableFontBank->setColour(255,255,255); m_scalableFontBank->print( 256, ExtraListYPos, "Extra Life!"); } } } /*---------------------------------------------------------------------- Function: Purpose: Says whether SB can do the look up/down thing Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::canDoLookAround() { return m_currentPlayerModeClass->canDoLookAround(); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::ignoreNewlyPressedButtonsOnPadThisThink() { m_ignoreNewlyPressedButtonsOnPadThisThink=true; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::inSoakUpState() { if(isWearingDivingHelmet()&& (CGameScene::getCollision()->getCollisionBlock(Pos.vx,Pos.vy)&COLLISION_TYPE_MASK)==COLLISION_TYPE_FLAG_SOAKUP) { m_healthWaterLevel+=waterSoakUpSpeed; if(m_healthWaterLevel>WATERMAXHEALTH) { m_healthWaterLevel=WATERMAXHEALTH; } } } /*---------------------------------------------------------------------- Function: Purpose: Params: When _reactDirection is REACT__GET_DIRECTION_FROM_THING then _thing must point to the thing that caused the damage Returns: ---------------------------------------------------------------------- */ #if defined(__USER_daveo__) || defined(__USER_paul__) int invincibleSponge=true; // NB: This is for debugging purposes only so don't try and use it for a permenant cheat mode.. #else int invincibleSponge=false; // NB: This is for debugging purposes only so don't try and use it for a permenant cheat mode.. #endif void CPlayer::takeDamage(DAMAGE_TYPE _damage,REACT_DIRECTION _reactDirection,CThing *_thing) { if((m_invincibleFrameCount==0||_damage==DAMAGE__KILL_OUTRIGHT)&& // Don't take damage if still recovering from the last hit m_invincibilityRingTimer==0&& // Or if we have the invincibility ring on m_currentPlayerModeClass->getState()!=STATE_SOAKUP&& // Or soaking up m_currentPlayerModeClass->getState()!=STATE_CELEBRATE&& // Or celebrating m_currentMode!=PLAYER_MODE_DEAD && // Or already dead! :) m_currentMode!=PLAYER_MODE_SWALLOW) { int ouchThatHurt=true; int ouchThatHurtSoMuchThatImJustGoingToDieNow=false; CSoundMediator::SFXID painSound=CSoundMediator::SFX_SPONGEBOB_OUCH; // Check if we are currently immune to this damage type m_recoveringFromElectrocution=false; switch(_damage) { case DAMAGE__NONE: ouchThatHurt=false; break; case DAMAGE__ELECTROCUTION: case DAMAGE__SHOCK_ENEMY: if(isWearingBoots()) { ouchThatHurt=false; } else { painSound=CSoundMediator::SFX_BOSS_JELLYFISH___SB_GETS_ELECTROCUTED; m_recoveringFromElectrocution=true; } break; case DAMAGE__FALL: case DAMAGE__LAVA: case DAMAGE__HIT_ENEMY: case DAMAGE__GAS_ENEMY: case DAMAGE__POISON_ENEMY: case DAMAGE__SWALLOW_ENEMY: case DAMAGE__PINCH_ENEMY: case DAMAGE__SQUASH_ENEMY: case DAMAGE__BURN_ENEMY: case DAMAGE__BITE_ENEMY: case DAMAGE__COLLISION_DAMAGE: break; case DAMAGE__KILL_OUTRIGHT: ouchThatHurt=ouchThatHurtSoMuchThatImJustGoingToDieNow=true; break; } if(ouchThatHurt) { int died=false; if(invincibleSponge) { m_invincibleFrameCount=INVINCIBLE_FRAMES__HIT; } else { if(ouchThatHurtSoMuchThatImJustGoingToDieNow||getSpatulasHeld()==0/*&&m_currentMode!=PLAYER_MODE_NET)*/) { died=true; } else { // NET REMOVED FROM HIT SYSTEM (PKG) /* if(m_currentMode==PLAYER_MODE_NET) { // Launch net pickup setMode(PLAYER_MODE_BASICUNARMED); DVECTOR pickupPos; CBasePickup *pickup; pickupPos.vx=Pos.vx; pickupPos.vy=Pos.vy-30; pickup=createPickup(PICKUP__NET,&pickupPos); pickup->setPos(&pickupPos); } else */ { // Launch all spatulas! GameScene.dropHealth(Pos,m_numSpatulasHeld,1); m_numSpatulasHeld=0; m_spatulaWarningTimer=0; } } } if(died) { DEATH_TYPE deathType; switch(_damage) { case DAMAGE__SQUASH_ENEMY: deathType=DEATHTYPE__SQUASH; break; default: deathType=DEATHTYPE__NORMAL; break; } dieYouPorousFreak(deathType); CPadVibrationManager::setVibration(0,CPadVibrationManager::VIBE_LONG_STROBEY); } else { CThing *platform; platform=isOnPlatform(); if(platform) { if ( ( (CNpcPlatform *) platform )->isCart() ) { m_invincibleFrameCount=INVINCIBLE_FRAMES__HIT; return; } } if(_reactDirection!=REACT__NO_REACTION) { if(_reactDirection==REACT__GET_DIRECTION_FROM_THING) { ASSERT(_thing); if(Pos.vx<_thing->getPos().vx) { _reactDirection=REACT__LEFT; } else { _reactDirection=REACT__RIGHT; } } m_moveVelocity.vx=((int)_reactDirection); switch(_reactDirection) { case REACT__LEFT: setFacing(FACING_RIGHT); break; case REACT__RIGHT: setFacing(FACING_LEFT); break; case REACT__UP: case REACT__GET_DIRECTION_FROM_THING: case REACT__NO_REACTION: break; } CSoundMediator::playSfx(painSound); m_currentPlayerModeClass->setState(STATE_JUMPBACK); } m_invincibleFrameCount=INVINCIBLE_FRAMES__HIT; CPadVibrationManager::setVibration(0,CPadVibrationManager::VIBE_SHORT); } } } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::collectedQuestItem() { m_currentPlayerModeClass->setState(STATE_CELEBRATE); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::getLivesLeft() { return CGameSlotManager::getSlotData()->m_lives; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::dieYouPorousFreak(DEATH_TYPE _deathType) { ASSERT(m_currentMode!=PLAYER_MODE_DEAD); m_deathType=_deathType; CSoundMediator::playSfx(CSoundMediator::SFX_SPONGEBOB_DEFEATED_JINGLE); setMode(PLAYER_MODE_DEAD); } /*---------------------------------------------------------------------- Function: Purpose: When SB dies and falls off the screen, he needs to be drawn in front of the level - hence this Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::setSBOTPosToFront() { setSBOTPos(0); } void CPlayer::setSBOTPos(int _ot) { int i; for(i=0;iSetOtPos(_ot); } } m_actorGfx->SetOtPos(_ot); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::updatePadInput() { m_lastPadInput=m_padInput; m_padInput=readPadInput(); if(!m_ignoreNewlyPressedButtonsOnPadThisThink) { m_padInputDown=(PLAYERINPUT)(m_padInput&(m_lastPadInput^-1)); } else { m_padInputDown=PI_NONE; } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ typedef struct { CPadConfig::PAD_CFG m_input; PLAYERINPUT m_output; } PAD_CONVERSION; static const PAD_CONVERSION s_padConversionTable[]= { { CPadConfig::PAD_CFG_UP, PI_UP }, { CPadConfig::PAD_CFG_DOWN, PI_DOWN }, { CPadConfig::PAD_CFG_LEFT, PI_LEFT }, { CPadConfig::PAD_CFG_RIGHT, PI_RIGHT }, { CPadConfig::PAD_CFG_JUMP, PI_JUMP }, { CPadConfig::PAD_CFG_FIRE, PI_FIRE }, { CPadConfig::PAD_CFG_CATCH, PI_CATCH }, { CPadConfig::PAD_CFG_WEAPONCHANGE, PI_WEAPONCHANGE }, }; static const int s_padConversionTableSize=sizeof(s_padConversionTable)/sizeof(PAD_CONVERSION); PLAYERINPUT CPlayer::readPadInput() { PLAYERINPUT input; int pad; const PAD_CONVERSION *table; int i; input=PI_NONE; pad=PadGetHeld(0); table=s_padConversionTable; for(i=0;im_input)) { input=(PLAYERINPUT)(input|table->m_output); } table++; } #ifdef _RECORD_DEMO_MODE_ CDemoPlayer::demoPlayerControl *crnt; PLAYERINPUT lastInput; crnt=&s_demoControls[s_demoSize]; if(s_demoFrameCount==0) { crnt->m_inputValue=input; } lastInput=(PLAYERINPUT)crnt->m_inputValue; if(crnt->m_length==255) { lastInput=(PLAYERINPUT)(input-1); } if(lastInput==input) { crnt->m_length++; } else { s_demoSize++; ASSERT(s_demoSizem_inputValue=input; crnt->m_length=1; } s_demoFrameCount++; if(s_demoFrameCount==MAX_DEMO_TIME_IN_FRAMES) { writeDemoControls(); ASSERT(!"DEMO ENDED"); } #endif return input; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::justButtBouncedABadGuy() { CSoundMediator::playSfx(CSoundMediator::SFX_SPONGEBOB_BUTTBOUNCE); m_currentPlayerModeClass->setState(STATE_BUTTBOUNCEUP); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::shove( DVECTOR const &move ) { int colHeight; // X movement int xDist = move.vx; colHeight=0; while( xDist ) { colHeight=CGameScene::getCollision()->getHeightFromGround(Pos.vx+xDist,Pos.vy-HEIGHT_FOR_HEAD_COLLISION,HEIGHT_FOR_HEAD_COLLISION+1); if ( colHeight < HEIGHT_FOR_HEAD_COLLISION ) { break; } if ( xDist > 0 ) { if ( xDist >= 8 ) { xDist -= 8; } else { xDist = 0; } } else if ( xDist < 0 ) { if ( xDist <= -8 ) { xDist += 8; } else { xDist = 0; } } } if(colHeightgetHeightFromGround(cx,Pos.vy-HEIGHT_FOR_HEAD_COLLISION,HEIGHT_FOR_HEAD_COLLISION+1)getHeightFromGround(Pos.vx,Pos.vy+move.vy,5); if(colHeight<0) { // Stop at the edge of the obstruction int dir,vy,cy,i; if(move.vy<0) { dir=-1; vy=move.vy; } else { dir=+1; vy=move.vy; } cy=Pos.vy; for(i=0;igetHeightFromGround(Pos.vx,cy)<0) { break; } cy+=dir; } if(i) Pos.vy=cy-dir; } else { // No obstruction Pos.vy+=move.vy; } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::moveLeft() { if(m_moveVelocity.vx<-CAMERA_STARTMOVETHRESHOLD||m_cameraXScrollPos<-(CAMERA_SCROLLTHRESHOLD*CAMERA_TILESIZE)<-CAMERA_STOPMOVETHRESHOLD) { m_cameraXScrollDir=0; } } void CPlayer::moveRight() { if(m_moveVelocity.vx>CAMERA_STARTMOVETHRESHOLD||m_cameraXScrollPos>(CAMERA_SCROLLTHRESHOLD*CAMERA_TILESIZE)<getState() != STATE_FALL ) { m_currentPlayerModeClass->setState(STATE_FALL); DVECTOR moveVel; moveVel=*getMoveVelocity(); moveVel.vy=0; m_fallFrames=0; setMoveVelocity(&moveVel); } } } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::moveVertical(int _moveDistance) { DVECTOR pos; int hitGround; int hitThisSuspectBlock; pos=Pos; hitGround=false; hitThisSuspectBlock=0; // Are we falling? if(_moveDistance>0) { if ( isOnPlatform() ) { // must also detect that we have landed on platform return( true ); } int colHeightBefore[2],colHeightAfter[2],blockAfter[2],moveRequired[2]; int i; int move; // Get heights of the two edges for(i=0;i<+2;i++) { int x=pos.vx+((i==0?-checkx:+checkx)); colHeightBefore[i]=getHeightFromGround(x,pos.vy,16); colHeightAfter[i]=getHeightFromGround(x,pos.vy+_moveDistance,16); blockAfter[i]=CGameScene::getCollision()->getCollisionBlock(x,pos.vy+_moveDistance); } // See if either side is about to go through the ground for(i=0;i<2;i++) { if(colHeightBefore[i]>=0&&colHeightAfter[i]<=0) { int goingIntoThisBlock=blockAfter[i]&COLLISION_TYPE_MASK; if(goingIntoThisBlock!=COLLISION_TYPE_FLAG_DEATH_FALL) { moveRequired[i]=colHeightAfter[i]; hitGround=true; } else { moveRequired[i]=0; } if(!hitThisSuspectBlock)hitThisSuspectBlock=goingIntoThisBlock; } else { moveRequired[i]=0; } } // Find the smallest move required to hit ground move=0; for(i=0;i<2;i++) { if(moveRequired[i]getCollisionBlock(x,pos.vy+_moveDistance); } // See if either side is about to go through the ground for(i=0;i<3;i++) { if(colHeightBefore[i]>=0&&colHeightAfter[i]<=0&&((blockAfter[i]&COLLISION_TYPE_MASK)!=COLLISION_TYPE_FLAG_NORMAL)) { moveRequired[i]=colHeightAfter[i]; hitGround=true; if(!hitThisSuspectBlock)hitThisSuspectBlock=blockAfter[i]; } else { moveRequired[i]=0; } } // Find the smallest move required to hit ground move=0; for(i=0;i<3;i++) { if(moveRequired[i]getCollisionBlock(x,y+_moveDistance); } bool isGoingToFall = false; // See if either side is about to go through the ground for(i=0;i<3;i++) { if(colHeightBefore[i]>=0&&colHeightAfter[i]<=0&&((blockAfter[i]&COLLISION_TYPE_MASK)!=COLLISION_TYPE_FLAG_NORMAL)) { moveRequired[i]=16+colHeightAfter[i]; //moveRequired[i]=colHeightAfter[i]; // hitGround=true; // do not call hitground code, because this will set it to STATE_IDLE for a frame // instead, do the appropriate stuff for a fall if(!hitThisSuspectBlock)hitThisSuspectBlock=blockAfter[i]; isGoingToFall = true; } else { moveRequired[i]=0; } } if ( isGoingToFall ) { // check where feet are if ( getHeightFromGround(pos.vx, pos.vy, 16 ) == 0 ) { // standing on ground, hence do not fall hitGround = true; } else { m_currentPlayerModeClass->setState(STATE_FALL); DVECTOR moveVel; moveVel=*getMoveVelocity(); moveVel.vy=0; m_fallFrames=0; setMoveVelocity(&moveVel); } } // Find the smallest move required to hit ground move=0; for(i=0;i<3;i++) { if(moveRequired[i]getCollisionBlock(pos.vx,pos.vy+_moveDistance); } pos.vy+=_moveDistance; setPlayerPos(&pos); if(m_currentMode!=PLAYER_MODE_DEAD) { switch(hitThisSuspectBlock&COLLISION_TYPE_MASK) { case COLLISION_TYPE_FLAG_DAMAGE: takeDamage(DAMAGE__COLLISION_DAMAGE); break; case COLLISION_TYPE_FLAG_ELECTRIC: if(!isWearingBoots()) { takeDamage(DAMAGE__COLLISION_DAMAGE); } break; /* case COLLISION_TYPE_FLAG_DEATH_LIQUID: dieYouPorousFreak(DEATHTYPE__LIQUID); break; case COLLISION_TYPE_FLAG_DEATH_INSTANT: dieYouPorousFreak(DEATHTYPE__NORMAL); break; */ case COLLISION_TYPE_FLAG_DEATH_FALL: dieYouPorousFreak(DEATHTYPE__FALL_TO_DEATH); break; default: break; } } return hitGround; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ int CPlayer::moveHorizontal(int _moveDistance) { int hitWall; hitWall=false; if(_moveDistance) { DVECTOR pos; int dirToMove; int i; int colHeightEdges[2]; int colHeight; pos=getPlayerPos(); dirToMove=_moveDistance<0?-1:+1; for(i=abs(_moveDistance);i&&!hitWall;i--) { int touchingGround,x,x2; colHeightEdges[0]=getHeightFromGround(pos.vx-checkx,pos.vy,16); colHeightEdges[1]=getHeightFromGround(pos.vx+checkx,pos.vy,16); touchingGround=false; x=x2=pos.vx+dirToMove; if(dirToMove>0) { // Moving right if(colHeightEdges[1]==0) { // Right side of SB touching ground x+=checkx; x2-=checkx; touchingGround=true; } else if(colHeightEdges[0]==0) { // Left side of SB touching ground x-=checkx; x2+=checkx; touchingGround=true; } } else { // Moving left if(colHeightEdges[0]==0) { // Left side of SB touching ground x-=checkx; x2+=checkx; touchingGround=true; } else if(colHeightEdges[1]==0) { // Right side of SB touching ground x+=checkx; x2-=checkx; touchingGround=true; } } if(touchingGround) { // SB touching ground on at least one edge colHeight=getHeightFromGround(x,pos.vy,16); // Head collision ( a bit of a hack ) int oktomove=false; switch ( CGameScene::getCollision()->getCollisionBlock( x, pos.vy-HEIGHT_FOR_HEAD_COLLISION ) & COLLISION_TYPE_MASK ) { case COLLISION_TYPE_NORMAL: oktomove=true; break; default: break; } if(oktomove) { if(colHeight>=-2&&colHeight<=2) { // Move along the ground pos.vx+=dirToMove; pos.vy+=colHeight; // Heh - these 4 lines stop SB going down a slope on the 'wrong edge' :) colHeight=getHeightFromGround(x2,pos.vy,16); if(colHeight<0) { pos.vy+=colHeight; } } else if(colHeight<0) { // Hit wall hitWall=true; } else { // Moved off edge of ledge pos.vx+=dirToMove; } } } else { // SB not touching ground if(dirToMove<0) { x-=checkx; } else { x+=checkx; } // Head collision ( cheers Charles! :) colHeight = getHeightFromGround( x, pos.vy-HEIGHT_FOR_HEAD_COLLISION, 16 ); if ( colHeight < 0 ) { switch ( CGameScene::getCollision()->getCollisionBlock( x, pos.vy-HEIGHT_FOR_HEAD_COLLISION ) & COLLISION_TYPE_MASK ) { case COLLISION_TYPE_NORMAL: break; default: hitWall=true; break; } } if ( !hitWall ) { colHeight=getHeightFromGround(x,pos.vy,16); if(colHeight>=0) { // Move in air pos.vx+=dirToMove; } else { // Hit wall hitWall=true; } } } } setPlayerPos(&pos); } return hitWall; } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::resetPlayerCollisionSizeToBase() { setPlayerCollisionSize(0,-COLSIZE_BASE_HEIGHT/2,COLSIZE_BASE_WIDTH,COLSIZE_BASE_HEIGHT); } /*---------------------------------------------------------------------- Function: Purpose: Params: Returns: ---------------------------------------------------------------------- */ void CPlayer::setPlayerCollisionSize(int _x,int _y,int _w,int _h) { setCollisionSize(_w,_h); setCollisionCentreOffset(_x,_y); } void CPlayer::getPlayerCollisionSize(int *_x,int *_y,int *_w,int *_h) { DVECTOR const &offset=getCollisionCentreOffset(); DVECTOR const &size=getCollisionSize(); *_x=offset.vx; *_y=offset.vy; *_w=size.vx; *_h=size.vy; } void CPlayer::getPlayerNormalCollisionSize(int *_x,int *_y,int *_w,int *_h) { *_x=0; *_y=-COLSIZE_BASE_HEIGHT/2; *_w=COLSIZE_BASE_WIDTH; *_h=COLSIZE_BASE_HEIGHT; } /*****************************************************************************/ /*****************************************************************************/ /*** On Screen Prompts *******************************************************/ /*****************************************************************************/ /*****************************************************************************/ // Prompt Data is stored reverse order const int PromptIconX=32; const int PromptTextXOfs=20; const int PromptTextYOfs=-4; const int PromptY=(INGAME_SCREENH-32); const int PromptXGap=20; const int PromptYGap=12; const int PromptTMode=1; const int PromptOnScreenTime=50*5; const int PromptFadeSpeed=8; static const CPlayer::sPromptData KaratePromptData[]= { {CPadConfig::PAD_CFG_WEAPONCHANGE, STR_PROMPT_KARATE_UNEQUIP}, {CPadConfig::PAD_CFG_FIRE, STR_PROMPT_KARATE_CHOP}, {CPadConfig::PAD_CFG_NONE, STR_PROMPT_KARATE_TITLE} }; static const CPlayer::sPromptData BubblePromptData[]= { {CPadConfig::PAD_CFG_WEAPONCHANGE, STR_PROMPT_BUBBLEWAND_UNEQUIP}, {CPadConfig::PAD_CFG_FIRE, STR_PROMPT_BUBBLEWAND_BLOW}, {CPadConfig::PAD_CFG_NONE, STR_PROMPT_BUBBLEWAND_TITLE} }; static const CPlayer::sPromptData NetPromptData[]= { {CPadConfig::PAD_CFG_WEAPONCHANGE, STR_PROMPT_NET_UNEQUIP}, {CPadConfig::PAD_CFG_CATCH, STR_PROMPT_NET_CATCH}, {CPadConfig::PAD_CFG_FIRE, STR_PROMPT_NET_THROW}, {CPadConfig::PAD_CFG_NONE, STR_PROMPT_NET_TITLE} }; static const CPlayer::sPromptData CoralBlowerPromptData[]= { {CPadConfig::PAD_CFG_WEAPONCHANGE, STR_PROMPT_CORALBLOWER_UNEQUIP}, {CPadConfig::PAD_CFG_UP, STR_PROMPT_CORALBLOWER_AIM}, {CPadConfig::PAD_CFG_CATCH, STR_PROMPT_CORALBLOWER_SUCK}, {CPadConfig::PAD_CFG_FIRE, STR_PROMPT_CORALBLOWER_FIRE}, {CPadConfig::PAD_CFG_NONE, STR_PROMPT_CORALBLOWER_TITLE} }; static const CPlayer::sPromptData JellyLauncherPromptData[]= { {CPadConfig::PAD_CFG_WEAPONCHANGE, STR_PROMPT_JELLYLAUNCHER_UNEQUIP}, {CPadConfig::PAD_CFG_FIRE, STR_PROMPT_JELLYLAUNCHER_FIRE2}, {CPadConfig::PAD_CFG_FIRE, STR_PROMPT_JELLYLAUNCHER_FIRE}, {CPadConfig::PAD_CFG_NONE, STR_PROMPT_JELLYLAUNCHER_TITLE} }; CPlayer::sPromptTable CPlayer::PromptTable[NUM_PLAYERMODES]= { {0,0}, // PLAYER_MODE_BASICUNARMED {KaratePromptData,0}, // PLAYER_MODE_FULLUNARMED {0,0}, // PLAYER_MODE_BALLOON {BubblePromptData,0}, // PLAYER_MODE_BUBBLE_MIXTURE {NetPromptData,0}, // PLAYER_MODE_NET {CoralBlowerPromptData,0}, // PLAYER_MODE_CORALBLOWER {JellyLauncherPromptData,0}, // PLAYER_MODE_JELLY_LAUNCHER {0,0}, // PLAYER_MODE_DEAD {0,0}, // PLAYER_MODE_FLY {0,0}, // PLAYER_MODE_CART {0,0}, // PLAYER_MODE_SWALLOW }; /*****************************************************************************/ void CPlayer::promptThink(int _frames) { int NewPrompt=m_currentMode; if (NewPrompt==0) { // No pickup- so kill all prompts PromptFade=-PromptFadeSpeed*4; } if (!PromptTable[NewPrompt].Data || PromptTable[NewPrompt].Shown) { NewPrompt=-1; } // Check for Prompt change if (CurrentPrompt!=NewPrompt && NewPrompt!=-1) { if (CurrentPrompt==-1) { // no prompt, so just display it CurrentPrompt=NewPrompt; PromptTimer=PromptOnScreenTime; PromptRGB=0; PromptFade=+PromptFadeSpeed; PromptTable[CurrentPrompt].Shown=1; // Set as shown } else { PromptFade=-PromptFadeSpeed; if (NewPrompt!=-1) { PromptFade*=4; // Fast fade, cos new prompt waiting } } } // Control Current Prompt if (CurrentPrompt!=-1) { PromptRGB+=PromptFade; if (PromptRGB>127) { // Fade in/on screen PromptRGB=127; PromptTimer-=_frames; if (PromptTimer<0) { PromptFade=-PromptFadeSpeed; } } else if (PromptRGB<0) { PromptRGB=0; CurrentPrompt=-1; } } } /*****************************************************************************/ void CPlayer::promptRender() { if (CurrentPrompt==-1) return; sPromptTable &ThisTable=PromptTable[CurrentPrompt]; sPromptData *Ptr=(sPromptData*)ThisTable.Data; int X=0; int Y=PromptY-PromptYGap; SpriteBank *sb=CGameScene::getSpriteBank(); POLY_FT4 *Ft4; int MaxTLen=0; if (!Ptr) return; // no prompt, so go away m_fontBank->setOt(0); m_fontBank->setTrans(1); m_fontBank->setColour(PromptRGB,PromptRGB,PromptRGB); m_fontBank->setSMode(PromptTMode); while ((CPadConfig::PAD_CFG)Ptr->m_input!=CPadConfig::PAD_CFG_NONE) { X=PromptIconX; int Icon[2]={0,0}; switch(CPadConfig::getButton((CPadConfig::PAD_CFG)Ptr->m_input)) { case PAD_CROSS: Icon[0]=FRM__BUTX; break; case PAD_TRIANGLE: Icon[0]=FRM__BUTT; break; case PAD_CIRCLE: Icon[0]=FRM__BUTC; break; case PAD_SQUARE: Icon[0]=FRM__BUTS; break; case PAD_UP: Icon[0]=FRM__BUTU; Icon[1]=FRM__BUTD; break; default: ASSERT(!"Unknown Pad Button"); break; } for (int i=0; i<2; i++) { if (Icon[i]) { //Icon Ft4=sb->printFT4(Icon[i],X,Y,0,0,0); setSemiTrans(Ft4,1); Ft4->tpage|=PromptTMode<<5; setRGB0(Ft4,PromptRGB,PromptRGB,PromptRGB); //Icon Mask - to aid alpha fade int Col=(PromptRGB*3)/2; Ft4=sb->printFT4(Icon[i],X,Y,0,0,0); setSemiTrans(Ft4,1); Ft4->tpage|=2<<5; setRGB0(Ft4,Col,Col,Col); X+=PromptXGap; } } // text int TLen=m_fontBank->getStringWidth(Ptr->Text); if (MaxTLenprint(X,Y+PromptTextYOfs,Ptr->Text); Y-=PromptYGap; Ptr++; } // Title m_fontBank->print(PromptIconX,Y+PromptTextYOfs,Ptr->Text); // Background int BackRGB=PromptRGB/2; TPOLY_F4 *F4=GetPrimTF4(); setTSemiTrans(F4,1); setTABRMode(F4,0); setXYWH(F4,PromptIconX-2,Y-2,MaxTLen+(X-PromptIconX)+4,(PromptY-Y)+4); setRGB0(F4,BackRGB,BackRGB,BackRGB); AddPrimToList(F4,0); // revert Fontbank settings - shouldnt have to do this, grrrr m_fontBank->setTrans(0); m_fontBank->setColour(128,128,128); m_fontBank->setSMode(0); } /*=========================================================================== end */