#include "Maelstrom_Globals.h" #include "netplay.h" #include "object.h" #include "player.h" #include "globals.h" #include "objects.h" /* ----------------------------------------------------------------- */ /* -- The thrust sound callback */ static void ThrustCallback(Uint8 theChannel) { #ifdef DEBUG error("Thrust called back on channel %hu\n", theChannel); #endif /* -- Check the control key */ if ( gPlayers[gOurPlayer]->IsThrusting() ) sound->PlaySound(gThrusterSound,1,theChannel,ThrustCallback); } /* -- ThrustCallback */ /* ----------------------------------------------------------------- */ /* -- The Player class */ Player:: Player(int index) : Object(0, 0, 0, 0, gPlayerShip, NO_PHASE_CHANGE) { int i; Index = index; Score = 0; for ( i=0; idamage = 1; } numshots = 0; /* Create a colored dot for this player */ ship_color = screen->MapRGB(gPlayerColors[index][0], gPlayerColors[index][1], gPlayerColors[index][2]); --gNumSprites; // We aren't really a sprite } Player::~Player() { for ( int i=0; i= gDeathMatch) ) { /* Game over, we got a stud. :) */ int i; OBJ_LOOP(i, gNumPlayers) { gPlayers[i]->IncrLives(-1); gPlayers[i]->Explode(); #ifdef DEBUG error("Killing player %d\n", i+1); #endif } } } void Player::IncrLives(int lives) { if ( gDeathMatch && (lives > 0) ) return; Lives += lives; } /* We've been shot! (returns 1 if we are dead) */ int Player::BeenShot(Object *ship, Shot *shot) { if ( Exploding || !Alive() ) return(0); if ( AutoShield || (ShieldOn && (ShieldLevel > 0)) ) return(0); if ( (special & LUCKY_IRISH) && (FastRandom(LUCK_ODDS) == 0) ) { sound->PlaySound(gLuckySound, 4); return(0); } return(Object::BeenShot(ship, shot)); } /* We've been run over! (returns 1 if we are dead) */ int Player::BeenRunOver(Object *ship) { if ( Exploding || !Alive() ) return(0); if ( AutoShield || (ShieldOn && (ShieldLevel > 0)) ) return(0); if ( ship->IsPlayer() ) /* Players phase through eachother */ return(0); if ( (special & LUCKY_IRISH) && (FastRandom(LUCK_ODDS) == 0) ) { sound->PlaySound(gLuckySound, 4); return(0); } return(Object::BeenRunOver(ship)); } /* We've been run over by a rock or something */ int Player::BeenDamaged(int damage) { if ( Exploding || !Alive() ) return(0); if ( AutoShield || (ShieldOn && (ShieldLevel > 0)) ) return(0); if ( (special & LUCKY_IRISH) && (FastRandom(LUCK_ODDS) == 0) ) { sound->PlaySound(gLuckySound, 4); return(0); } return(Object::BeenDamaged(damage)); } /* We expired (returns -1 if our sprite should be deleted) */ int Player::BeenTimedOut(void) { Exploding = 0; SetPos( ((SCREEN_WIDTH/2-((gNumPlayers/2-Index)*(2*SPRITES_WIDTH))) *SCALE_FACTOR), ((SCREEN_HEIGHT/2)*SCALE_FACTOR) ); if ( gDeathMatch ) Dead = (DEAD_DELAY/2); else Dead = DEAD_DELAY; return(0); } int Player::Explode(void) { /* Create some shrapnel */ int newsprite, xVel, yVel, rx; /* Don't explode while already exploding. :) (DeathMatch) */ if ( Exploding || !Alive() ) return(0); /* Type 1 shrapnel */ rx = (SCALE_FACTOR); xVel = yVel = 0; while (xVel == 0) xVel = FastRandom(rx / 2) + SCALE_FACTOR; while (yVel == 0) yVel = FastRandom(rx) - (rx / 2); if (yVel > 0) yVel += SCALE_FACTOR; else yVel -= SCALE_FACTOR; newsprite = gNumSprites; gSprites[newsprite]=new Shrapnel(x, y, xVel, yVel, gShrapnel1); /* Type 2 shrapnel */ rx = (SCALE_FACTOR); xVel = yVel = 0; while (xVel == 0) xVel = FastRandom(rx / 2) + SCALE_FACTOR; xVel *= -1; while (yVel == 0) yVel = FastRandom(rx) - (rx / 2); if (yVel > 0) yVel += SCALE_FACTOR; else yVel -= SCALE_FACTOR; newsprite = gNumSprites; gSprites[newsprite]=new Shrapnel(x, y, xVel, yVel, gShrapnel2); /* We may lose our special abilities */ if ( (special & LUCKY_IRISH) && (FastRandom(LUCK_ODDS) == 0) ) special &= ~LUCKY_IRISH; else special = 0; /* Finish our explosion */ Exploding = 1; Thrusting = 0; Shooting = 0; ShieldOn = 0; solid = 0; shootable = 0; phase = 0; nextphase = 0; phasetime = 2; xvec = yvec = 0; Set_Blit(gShipExplosion); Set_TTL(myblit->numFrames*phasetime); ExplodeSound(); return(0); } Shot * Player::ShotHit(Rect *hitRect) { int i; OBJ_LOOP(i, numshots) { if ( Overlap(&shots[i]->hitRect, hitRect) ) { /* KillShot() rearranges the shot[] array */ Shot *shotputt = shots[i]; KillShot(i); return(shotputt); } } return(NULL); } int Player::Move(int Freeze) { int i; /* Move and time out old shots */ #ifdef SERIOUS_DEBUG printf("Shots(%d): ", numshots); #endif OBJ_LOOP(i, numshots) { int offset; if ( --shots[i]->ttl == 0 ) { KillShot(i); continue; } /* Set new X position */ shots[i]->x += shots[i]->xvel; if ( shots[i]->x > playground.right ) shots[i]->x = playground.left + (shots[i]->x-playground.right); else if ( shots[i]->x < playground.left ) shots[i]->x = playground.right - (playground.left-shots[i]->x); /* Set new Y position */ shots[i]->y += shots[i]->yvel; if ( shots[i]->y > playground.bottom ) shots[i]->y = playground.top + (shots[i]->y-playground.bottom); else if ( shots[i]->y < playground.top ) shots[i]->y = playground.bottom - (playground.top-shots[i]->y); /* -- Setup the hit rectangle */ offset = (shots[i]->y>>SPRITE_PRECISION); shots[i]->hitRect.top = offset; shots[i]->hitRect.bottom = offset+SHOT_SIZE; offset = (shots[i]->x>>SPRITE_PRECISION); shots[i]->hitRect.left = offset; shots[i]->hitRect.right = offset+SHOT_SIZE; #ifdef SERIOUS_DEBUG printf(" %d = (%d,%d)", i, shots[i]->x, shots[i]->y); #endif } #ifdef SERIOUS_DEBUG printf("\n"); #endif /* Decrement the Bonus and NoThrust time */ if ( Bonus && CutBonus-- == 0 ) { Bonus -= 10; CutBonus = BONUS_DELAY; } if ( NoThrust ) --NoThrust; /* Check to see if we are dead... */ if ( Dead ) { if ( --Dead == 0 ) { // New Chance at Life! if ( NewShip() < 0 ) { /* Game Over */ Dead = DEAD_DELAY; Playing = 0; } } return(0); } /* Update our status... :-) */ if ( Alive() && ! Exploding ) { /* Airbrakes slow us down. :) */ if ( special & AIR_BRAKES ) { if ( yvec > 0 ) --yvec; else if ( yvec < 0 ) ++yvec; if ( xvec > 0 ) --xvec; else if ( xvec < 0 ) ++xvec; } /* Thrust speeds us up! :) */ if ( Thrusting ) { if ( ! WasThrusting ) { sound->PlaySound(gThrusterSound, 1, 3, ThrustCallback); WasThrusting = 1; } /* -- The thrust key is down, increase the thrusters! */ if ( ! NoThrust ) { Accelerate(gVelocityTable[phase].h, gVelocityTable[phase].v); } } else WasThrusting = 0; /* Shoot baby, shoot. */ if ( Shooting ) { if ( ! WasShooting || (special&MACHINE_GUNS) ) { WasShooting = 1; /* Make a single bullet */ MakeShot(0); sound->PlaySound(gShotSound, 2); if ( special & TRIPLE_FIRE ) { /* Followed by two more.. */ MakeShot(1); MakeShot(-1); } } } else WasShooting = 0; /* We be rotating. :-) */ if ( Rotating & 0x01 ) { // Heading right.. if ( ++phase == myblit->numFrames ) phase = 0; } if ( Rotating & 0x10 ) { // Heading left.. if ( --phase < 0 ) phase = (myblit->numFrames-1); } /* Check the shields */ if ( AutoShield ) { WasShielded = 1; --AutoShield; } else if ( ShieldOn ) { if ( ShieldLevel > 0 ) { if ( ! WasShielded ) { sound->PlaySound(gShieldOnSound, 1); WasShielded = 1; } --ShieldLevel; } else { sound->PlaySound(gNoShieldSound, 2); } } else WasShielded = 0; } return(Object::Move(Freeze)); } void Player::HandleKeys(void) { /* Wait for keystrokes and sync() */ unsigned char *inbuf; int len, i; if ( (len=GetSyncBuf(Index, &inbuf)) <= 0 ) return; for ( i=0; i 0 ) { --gPaused; } else { sound->PlaySound(gPauseSound, 5); ++gPaused; } } if ( inbuf[i] == ABORT_KEY ) gGameOn = 0; break; } /* Regular key press handling */ switch(inbuf[++i]) { case THRUST_KEY: Thrusting = 1; break; case RIGHT_KEY: Rotating |= 0x01; break; case LEFT_KEY: Rotating |= 0x10; break; case SHIELD_KEY: ShieldOn = 1; break; case FIRE_KEY: Shooting = 1; break; case PAUSE_KEY: if ( gPaused > 0 ) { --gPaused; } else { sound->PlaySound(gPauseSound, 5); ++gPaused; } break; case ABORT_KEY: gGameOn = 0; break; default: break; } break; case KEY_RELEASE: switch(inbuf[++i]) { case THRUST_KEY: Thrusting = 0; if ( sound->Playing(gThrusterSound) ) sound->HaltSound(3); break; case RIGHT_KEY: Rotating &= ~0x01; break; case LEFT_KEY: Rotating &= ~0x10; break; case SHIELD_KEY: ShieldOn = 0; break; case FIRE_KEY: Shooting = 0; break; case ABORT_KEY: /* Do nothing on release */; break; default: break; } break; default: break; } } } void Player::BlitSprite(void) { int i; if ( ! Alive() ) return; /* Draw the new shots */ OBJ_LOOP(i, numshots) { int X = (shots[i]->x>>SPRITE_PRECISION); int Y = (shots[i]->y>>SPRITE_PRECISION); screen->QueueBlit(X, Y, gPlayerShot); } /* Draw the shield, if necessary */ if ( AutoShield || (ShieldOn && (ShieldLevel > 0)) ) { screen->QueueBlit(x>>SPRITE_PRECISION, y>>SPRITE_PRECISION, gShieldBlit->sprite[Sphase]); } /* Draw the thrust, if necessary */ if ( Thrusting && ! NoThrust ) { int thrust_x, thrust_y; thrust_x = x + gThrustOrigins[phase].h; thrust_y = y + gThrustOrigins[phase].v; screen->QueueBlit(thrust_x>>SPRITE_PRECISION, thrust_y>>SPRITE_PRECISION, ThrustBlit->sprite[phase]); } /* Draw our ship */ Object::BlitSprite(); } void Player::UnBlitSprite(void) { int i; if ( ! Alive() ) return; /* Erase all old shots */ OBJ_LOOP(i, numshots) { int X = (shots[i]->x>>SPRITE_PRECISION); int Y = (shots[i]->y>>SPRITE_PRECISION); screen->Clear(X, Y, SHOT_SIZE, SHOT_SIZE, DOCLIP); } /* Erase the thrust, if necessary */ if ( WasThrusting ) { int thrust_x, thrust_y; thrust_x = (x + gThrustOrigins[phase].h)>>SPRITE_PRECISION; thrust_y = (y + gThrustOrigins[phase].v)>>SPRITE_PRECISION; screen->Clear(thrust_x, thrust_y, 16, 16, DOCLIP); if ( ThrustBlit == gThrust1 ) ThrustBlit = gThrust2; else ThrustBlit = gThrust1; } if ( WasShielded ) { if ( Sphase ) Sphase = 0; else Sphase = 1; } Object::UnBlitSprite(); } void Player::HitSound(void) { sound->PlaySound(gSteelHit, 3); } void Player::ExplodeSound(void) { sound->PlaySound(gShipHitSound, 3); } void Player::AbortGame(void) { QueueKey(KEY_PRESS, ABORT_KEY); } /* Private functions... */ int Player::MakeShot(int offset) { int shotphase; if ( numshots == MAX_SHOTS ) return(-1); /* Handle the velocity */ if ( (shotphase = phase+offset) < 0 ) shotphase = myblit->numFrames-1; else if ( shotphase == myblit->numFrames ) shotphase = 0; shots[numshots]->yvel = (gVelocityTable[shotphase].v<xvel = (gVelocityTable[shotphase].h<x = x; shots[numshots]->y = y; offset = ((SPRITES_WIDTH/2)-2)<x += offset; shots[numshots]->y += offset; shots[numshots]->xvel += xvec; shots[numshots]->x -= xvec; shots[numshots]->yvel += yvec; shots[numshots]->y -= yvec; /* -- Setup the hit rectangle */ offset = (shots[numshots]->y>>SPRITE_PRECISION); shots[numshots]->hitRect.top = offset; shots[numshots]->hitRect.bottom = offset+SHOT_SIZE; offset = (shots[numshots]->x>>SPRITE_PRECISION); shots[numshots]->hitRect.left = offset; shots[numshots]->hitRect.right = offset+SHOT_SIZE; /* How LONG do they live? :) */ if ( special & LONG_RANGE ) shots[numshots]->ttl = (SHOT_DURATION * 2); else shots[numshots]->ttl = SHOT_DURATION; return(++numshots); } void Player::KillShot(int index) { OBJ_KILL(shots, index, numshots, Shot); } /* The Shot sprites for the Shinobi and Player */ Uint8 gPlayerShotColors[] = { 0xF0, 0xCC, 0xCC, 0xF0, 0xCC, 0x96, 0xC6, 0xCC, 0xCC, 0xC6, 0xC6, 0xCC, 0xF0, 0xCC, 0xCC, 0xF0 }; SDL_Surface *gPlayerShot; Uint8 gEnemyShotColors[] = { 0xDC, 0xDA, 0xDA, 0xDC, 0xDA, 0x17, 0x23, 0xDA, 0xDA, 0x23, 0x23, 0xDA, 0xDC, 0xDA, 0xDA, 0xDC }; SDL_Surface *gEnemyShot; Uint8 gPlayerColors[MAX_PLAYERS][3] = { { 0x00, 0x00, 0xFF }, /* Player 1 */ { 0xFF, 0x99, 0xFF }, /* Player 2 */ { 0x33, 0x99, 0x00 }, /* Player 3 */ }; /* The players!! */ Player *gPlayers[MAX_PLAYERS];