From 09b007411c39f8147e9c63a601d6b89e32d02bd7 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Wed, 24 Aug 2011 14:14:44 +0200 Subject: [PATCH] Import of version 1.1 minus music, sound and graphics. --- code/Starfighter.cpp | 148 +++ code/Starfighter.h | 70 ++ code/ai.cpp | 200 ++++ code/ai.h | 37 + code/aliens.cpp | 2038 +++++++++++++++++++++++++++++++++++++++++ code/aliens.h | 58 ++ code/audio.cpp | 136 +++ code/audio.h | 36 + code/bullets.cpp | 797 ++++++++++++++++ code/bullets.h | 54 ++ code/cargo.cpp | 120 +++ code/cargo.h | 40 + code/classes.h | 710 ++++++++++++++ code/collectable.cpp | 430 +++++++++ code/collectable.h | 43 + code/comms.cpp | 156 ++++ code/comms.h | 38 + code/debris.cpp | 94 ++ code/debris.h | 36 + code/defs.h | 315 +++++++ code/events.cpp | 87 ++ code/events.h | 37 + code/explosions.cpp | 111 +++ code/explosions.h | 37 + code/game.cpp | 344 +++++++ code/game.h | 75 ++ code/globals.cpp | 75 ++ code/globals.h | 33 + code/graphics.cpp | 103 +++ code/graphics.h | 37 + code/init.cpp | 271 ++++++ code/init.h | 49 + code/intermission.cpp | 869 ++++++++++++++++++ code/intermission.h | 56 ++ code/loadSave.cpp | 253 +++++ code/loadSave.h | 44 + code/messages.cpp | 232 +++++ code/messages.h | 40 + code/misc.cpp | 473 ++++++++++ code/misc.h | 44 + code/missions.cpp | 1460 +++++++++++++++++++++++++++++ code/missions.h | 52 ++ code/player.cpp | 429 +++++++++ code/player.h | 50 + code/resources.cpp | 221 +++++ code/resources.h | 45 + code/script.cpp | 312 +++++++ code/script.h | 52 ++ code/shop.cpp | 772 ++++++++++++++++ code/shop.h | 43 + code/structs.h | 306 +++++++ code/title.cpp | 738 +++++++++++++++ code/title.h | 54 ++ code/unpack.cpp | 161 ++++ code/unpack.h | 35 + code/version.h | 1 + code/weapons.cpp | 261 ++++++ code/weapons.h | 36 + docs/LICENSE | 339 +++++++ docs/index.html | 368 ++++++++ makefile | 37 + 61 files changed, 14598 insertions(+) create mode 100644 code/Starfighter.cpp create mode 100644 code/Starfighter.h create mode 100644 code/ai.cpp create mode 100644 code/ai.h create mode 100644 code/aliens.cpp create mode 100644 code/aliens.h create mode 100644 code/audio.cpp create mode 100644 code/audio.h create mode 100644 code/bullets.cpp create mode 100644 code/bullets.h create mode 100644 code/cargo.cpp create mode 100644 code/cargo.h create mode 100644 code/classes.h create mode 100644 code/collectable.cpp create mode 100644 code/collectable.h create mode 100644 code/comms.cpp create mode 100644 code/comms.h create mode 100644 code/debris.cpp create mode 100644 code/debris.h create mode 100644 code/defs.h create mode 100644 code/events.cpp create mode 100644 code/events.h create mode 100644 code/explosions.cpp create mode 100644 code/explosions.h create mode 100644 code/game.cpp create mode 100644 code/game.h create mode 100644 code/globals.cpp create mode 100644 code/globals.h create mode 100644 code/graphics.cpp create mode 100644 code/graphics.h create mode 100644 code/init.cpp create mode 100644 code/init.h create mode 100644 code/intermission.cpp create mode 100644 code/intermission.h create mode 100644 code/loadSave.cpp create mode 100644 code/loadSave.h create mode 100644 code/messages.cpp create mode 100644 code/messages.h create mode 100644 code/misc.cpp create mode 100644 code/misc.h create mode 100644 code/missions.cpp create mode 100644 code/missions.h create mode 100644 code/player.cpp create mode 100644 code/player.h create mode 100644 code/resources.cpp create mode 100644 code/resources.h create mode 100644 code/script.cpp create mode 100644 code/script.h create mode 100644 code/shop.cpp create mode 100644 code/shop.h create mode 100644 code/structs.h create mode 100644 code/title.cpp create mode 100644 code/title.h create mode 100644 code/unpack.cpp create mode 100644 code/unpack.h create mode 100644 code/version.h create mode 100644 code/weapons.cpp create mode 100644 code/weapons.h create mode 100644 docs/LICENSE create mode 100755 docs/index.html create mode 100755 makefile diff --git a/code/Starfighter.cpp b/code/Starfighter.cpp new file mode 100644 index 0000000..dd2d480 --- /dev/null +++ b/code/Starfighter.cpp @@ -0,0 +1,148 @@ +/* +Project: Starfighter +Copyright (C) 2003 Parallel Realities +All Rights Reserved + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "version.h" +#include "Starfighter.h" + +int main(int argc, char *argv[]) +{ + defineGlobals(); // Must do this first! + + signed char cheatAttempt = 0; + + if (argc > 1) + { + if (strcmp("--help", argv[1]) == 0) + { + printf("\nProject: Starfighter %s\n", VERSION); + printf("Copyright Parallel Realities 2003\n\n"); + printf("Additional Commands\n"); + printf("\t-noaudio Disables sound and music\n"); + printf("\t-mono Mono sound output (best for headphones)\n\n"); + printf("www.parallelrealities.co.uk\n"); + printf("\n"); + exit(0); + } + } + + for (int i = 1 ; i < argc ; i++) + { + #if USEPACK + #else + if (strcmp(argv[i], "-nomove") == 0) + {printf("Enemy movement disabled\n"); dev.moveAliens = 0;} + if (strcmp(argv[i], "-nofire") == 0) + {printf("Enemy firing disabled\n"); dev.fireAliens = 0;} + #endif + if (strcmp(argv[i], "-cheat") == 0) + cheatAttempt = 1; + if (strcmp(argv[i], "-noaudio") == 0) + {printf("No Audio\n"); engine.useAudio = 0;} + if (strcmp(argv[i], "-mono") == 0) + {printf("Mono sound output\n"); engine.useAudio = 1;} + } + + atexit(cleanUp); + + initSystem(); // Opens video mode and sound + loadFont(); + + if (cheatAttempt) + { + graphics.clearScreen(graphics.black); + graphics.drawString("That doesn't work anymore", -1, 285, FONT_WHITE); + graphics.drawString("Try harder...", -1, 315, FONT_WHITE); + graphics.updateScreen(); + SDL_Delay(2000); + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + SDL_Delay(500); + } + + graphics.freeGraphics(); + loadSound(); + + initWeapons(); + + initVars(); + defineAliens(); + + graphics.setColorIndexes(); + + setAllyMessages(); + + showStory(); + + // Determine which part of the game we will go to... + int section = 0; + + newGame(); + + /* + currentGame.system = 3; + currentGame.area = 24; + currentGame.shieldUnits = 4; + currentGame.hasWingMate1 = 1; + currentGame.hasWingMate2 = 1; + + player.shield = (currentGame.shieldUnits * 25); + + player.weaponType[1] = W_LASER; + + player.ammo[0] = 1; + weapon[1].ammo[0] = 3; + weapon[1].damage = 5; + weapon[1].reload[0] = 7; + + engine.cheatAmmo = 1; + engine.cheatShield = 1; + engine.cheatCash = 1; + updateSystemStatus(); + */ + + while (true) + { + switch(section) + { + case 0: + section = doTitle(); + break; + + case 1: + section = galaxyMap(); + break; + + case 2: + if (currentGame.stationedPlanet == -1) {doCutscene(0);} + section = mainGameLoop(); + break; + } + } + + //doTitle(); + //galaxyMap(); + //mainGameLoop(); + //doCutscene(2); + //doCredits(); + + return(0); +} diff --git a/code/Starfighter.h b/code/Starfighter.h new file mode 100644 index 0000000..4c1df37 --- /dev/null +++ b/code/Starfighter.h @@ -0,0 +1,70 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void initSystem(); +extern void loadGameGraphics(); +extern void loadSound(); +extern void initWeapons(); +extern void initMissions(); +extern void initVars(); +extern void showStory(); +extern int doTitle(); +extern void defineAliens(); +extern void loadFont(); +extern int galaxyMap(); +extern void defineGlobals(); +extern void cleanUp(); +extern int mainGameLoop(); +extern void setAllyMessages(); +extern void newGame(); +extern void doCredits(); +extern void getPlayerInput(); +extern void initPlanetMissions(signed char system); +extern void updateSystemStatus(); +extern void doCutscene(int scene); + +globalEngineVariables engine; +devVariables dev; +Game currentGame; +object player; +mission currentMission; +Graphics graphics; + +object enemy[MAX_ALIENS]; +Star star[200]; +Planet systemPlanet[10]; +mission missions[MAX_MISSIONS]; +ShopItem shopItems[MAX_SHOPITEMS]; +Mix_Chunk *sound[MAX_SOUNDS]; +object weapon[MAX_WEAPONS]; +object cargo[MAX_CARGO]; +event gameEvent[10]; +cutMsg cutMessage[10]; diff --git a/code/ai.cpp b/code/ai.cpp new file mode 100644 index 0000000..e339b6d --- /dev/null +++ b/code/ai.cpp @@ -0,0 +1,200 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "ai.h" + +/* +Some very simple artificial intelligence routines for the aliens. +Shouldn't really be called AI since they just do things at random. +Aliens are assigned various AI types and this routine makes use of them. +Levels of aggression, defence and evasion are all here. +*/ + +void setEnemyAI(object *theEnemy) +{ + // Make friendly craft generally concentrate on smaller fighters + if ((theEnemy->flags & FL_FRIEND) && (theEnemy->target == &enemy[WC_BOSS])) + { + if ((rand() % 5) == 0) + { + theEnemy->target = theEnemy; + theEnemy->thinktime = 0; + return; + } + } + + int i = rand() % 10; + float tx = theEnemy->target->x; + float ty = theEnemy->target->y; + + int chase = 0; // Chance in 10 of chasing player + int area = 0; // Chance in 10 of moving to an area around the player + int stop = 0; // Chance in 10 of hanging back + int point = 0; // Size of area alien will move into + + switch (theEnemy->AIType) + { + case AI_NORMAL: + chase = 3; + point = 6; + stop = 9; + area = 250; + break; + case AI_OFFENSIVE: + chase = 7; + point = 8; + stop = 9; + area = 50; + break; + case AI_DEFENSIVE: + chase = 2; + point = 6; + stop = 8; + area = 300; + break; + case AI_EVASIVE: + chase = 1; + point = 8; + stop = 9; + area = 600; + break; + case AI_WANDER: + chase = -1; + point = 0; + stop = 10; + area = 1200; + break; + } + + if (i <= chase) + { + // Chase the target + theEnemy->dx = ((theEnemy->x - tx) / ((300 / theEnemy->speed) + rand() % 100)); + theEnemy->dy = ((theEnemy->y - ty) / ((300 / theEnemy->speed) + rand() % 100)); + return; + } + else if ((i >= point) && (i <= stop)) + { + // Fly to a random point around the target + tx += (rand() % area - (rand() % area * 2)); + ty += (rand() % area - (rand() % area * 2)); + theEnemy->dx = ((theEnemy->x - tx) / ((300 / theEnemy->speed) + rand() % 100)); + theEnemy->dy = ((theEnemy->y - ty) / ((300 / theEnemy->speed) + rand() % 100)); + return; + } + else + { + // Hang back + theEnemy->dx = 0; + theEnemy->dy = 0; + return; + } +} + +void setKlineAttackMethod(object *theEnemy) +{ + theEnemy->maxShield -= 500; + if (theEnemy->maxShield == 0) + theEnemy->flags -= FL_CANNOTDIE; + + if (theEnemy->maxShield == 1000) + { + setRadioMessage(FACE_KLINE, "Very good, Bainfield. Now let's get a little more serious...", 1); + theEnemy->weaponType[0] = W_SPREADSHOT; + theEnemy->chance[1] = 40; + } + else if (theEnemy->maxShield == 500) + { + setRadioMessage(FACE_KLINE, "Your ability to stay alive irritates me!! Try dodging some of these!!", 1); + theEnemy->weaponType[0] = W_DIRSHOCKMISSILE; + theEnemy->weaponType[1] = W_DIRSHOCKMISSILE; + theEnemy->chance[0] = 2; + theEnemy->chance[1] = 2; + theEnemy->flags += FL_AIMS; + } + else if (theEnemy->maxShield == 0) + { + setRadioMessage(FACE_KLINE, "ENOUGH!! THIS ENDS NOW!!!", 1); + theEnemy->weaponType[0] = W_AIMED_SHOT; + theEnemy->weaponType[1] = W_MICRO_HOMING_MISSILES; + theEnemy->flags += FL_CANCLOAK; + theEnemy->chance[0] = 100; + theEnemy->chance[1] = 2; + } + + theEnemy->shield = 500; +} + +/* +This AI is exclusively for Kline. +*/ +void setKlineAI(object *theEnemy) +{ + // Weapon type change + if ((rand() % 3) == 0) + { + if (currentGame.area != 26) + { + if (theEnemy->flags & FL_AIMS) + theEnemy->flags -= FL_AIMS; + + switch(rand() % 2) + { + case 0: + theEnemy->weaponType[0] = W_TRIPLE_SHOT; + break; + case 1: + theEnemy->weaponType[0] = W_AIMED_SHOT; + theEnemy->flags += FL_AIMS; + break; + } + } + } + + if (theEnemy->flags & FL_CIRCLES) + theEnemy->flags -= FL_CIRCLES; + if (theEnemy->flags & FL_CONTINUOUS_FIRE) + theEnemy->flags -= FL_CONTINUOUS_FIRE; + if (theEnemy->flags & FL_DROPMINES) + theEnemy->flags -= FL_DROPMINES; + + switch(rand() % 10) + { + case 0: + if ((theEnemy->weaponType[0] != W_DIRSHOCKMISSILE) && (theEnemy->weaponType[1] != W_MICRO_HOMING_MISSILES)) + theEnemy->flags += FL_CONTINUOUS_FIRE; + theEnemy->dx = ((theEnemy->x - theEnemy->target->x) / ((300 / theEnemy->speed) + rand() % 100)); + theEnemy->dy = ((theEnemy->y - theEnemy->target->y) / ((300 / theEnemy->speed) + rand() % 100)); + break; + case 1: + case 2: + // Kline only attacks then he is ready! + if ((!(theEnemy->flags & FL_NOFIRE)) && (currentGame.area == 11)) + theEnemy->flags += FL_DROPMINES; + break; + case 3: + case 4: + theEnemy->flags += FL_CIRCLES; + break; + default: + setEnemyAI(theEnemy); + break; + } +} diff --git a/code/ai.h b/code/ai.h new file mode 100644 index 0000000..26631c8 --- /dev/null +++ b/code/ai.h @@ -0,0 +1,37 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void setRadioMessage(signed char face, char *in, int priority); + +extern object enemy[MAX_ALIENS]; +extern object player; +extern Game currentGame; diff --git a/code/aliens.cpp b/code/aliens.cpp new file mode 100644 index 0000000..0475cd2 --- /dev/null +++ b/code/aliens.cpp @@ -0,0 +1,2038 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "aliens.h" + +signed char placeAlien(object *theEnemy) +{ + if (rand() % 2 == 0) + theEnemy->x = Math::rrand(800, 1600); + else + theEnemy->x = Math::rrand(-800, 0); + + if (rand() % 2 == 0) + theEnemy->y = Math::rrand(600, 1200); + else + theEnemy->y = Math::rrand(-600, 0); + + if (currentGame.area == 24) + { + theEnemy->x = 800; + theEnemy->y = Math::rrand(200, 400); + } + + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + if ((enemy[i].owner != theEnemy) && (enemy[i].shield > 0)) + { + if (Collision::collision(theEnemy->x, theEnemy->y, theEnemy->image[0]->w, theEnemy->image[0]->h, enemy[i].x, enemy[i].y, enemy[i].image[0]->w, enemy[i].image[0]->h)) + return 0; + } + } + + return 1; +} + +/* +This simply pulls back an alien from the array that is +"dead" (no shield) and returns the index number so we can have +a new one. +*/ +int getAlien() +{ + for (int i = 0 ; i < engine.maxAliens ; i++) + { + if (!enemy[i].active) + { + return i; + } + } + + return -1; +} + +void addDrone(object *host) +{ + int index = getAlien(); + + if (index == -1) + return; + + enemy[index] = defEnemy[CD_DRONE]; + enemy[index].active = 1; + enemy[index].face = rand() % 2; + enemy[index].owner = &enemy[index]; // Most enemies will own themselves + enemy[index].target = &enemy[index]; + enemy[index].thinktime = (50 + rand() % 50); + enemy[index].systemPower = enemy[index].maxShield; + enemy[index].deathCounter = 0 - (enemy[index].maxShield * 3); + enemy[index].hit = 0; + + enemy[index].x = host->x + rand() % 50; + enemy[index].y = host->y + rand() % 50; +} + +void addSmallAsteroid(object *host) +{ + if (engine.missionCompleteTimer != 0) + return; + + int index = -1; + int debris = 1 + rand() % 10; + + for (int i = 0 ; i < debris ; i++) + addBullet(&weapon[W_ROCKETS], host, 0, 0); + + for (int i = 10 ; i < 20 ; i++) + if (enemy[i].active == 0) + index = i; + + if (index == -1) + return; + + if ((rand() % 10) > 3) + { + enemy[index] = defEnemy[CD_ASTEROID2]; + enemy[index].imageIndex[0] = enemy[index].imageIndex[1] = 39 + rand() % 2; + enemy[index].image[0] = graphics.shipShape[enemy[index].imageIndex[0]]; + enemy[index].image[1] = graphics.shipShape[enemy[index].imageIndex[1]]; + } + else + { + enemy[index] = defEnemy[CD_DRONE]; + } + + enemy[index].owner = &enemy[index]; // Most enemies will own themselves + enemy[index].target = &enemy[index]; + enemy[index].thinktime = 1; + enemy[index].systemPower = enemy[index].maxShield; + enemy[index].deathCounter = 0 - (enemy[index].maxShield * 3); + enemy[index].hit = 0; + + enemy[index].x = host->x; + enemy[index].y = host->y; + enemy[index].active = 1; +} + +signed char addAlien() +{ + int index = getAlien(); + + if ((index == -1) || (currentGame.area == 23) || (currentGame.area == 26)) + return 0; + + signed char *alienArray; + signed char numberOfAliens = 1; + + alienArray = new signed char[5]; + + switch(currentGame.area) + { + case 0: + case 3: + case 11: + numberOfAliens = 1; + alienArray[0] = CD_DUALFIGHTER; + break; + case 1: + case 2: + case 4: + case 5: + numberOfAliens = 2; + alienArray[0] = CD_DUALFIGHTER; + alienArray[1] = CD_PROTOFIGHTER; + break; + case 9: + case 13: + case 14: + case 16: + numberOfAliens = 4; + alienArray[0] = CD_DUALFIGHTER; + alienArray[1] = CD_PROTOFIGHTER; + alienArray[2] = CD_MISSILEBOAT; + alienArray[3] = CD_AIMFIGHTER; + break; + case 25: + numberOfAliens = 6; + alienArray[0] = CD_DUALFIGHTER; + alienArray[1] = CD_PROTOFIGHTER; + alienArray[2] = CD_MISSILEBOAT; + alienArray[3] = CD_AIMFIGHTER; + alienArray[4] = CD_ESCORT; + alienArray[5] = CD_MOBILE_RAY; + break; + case 22: + numberOfAliens = 2; + alienArray[0] = CD_AIMFIGHTER; + alienArray[1] = CD_DUALFIGHTER; + break; + case 7: + case 8: + numberOfAliens = 3; + alienArray[0] = CD_DUALFIGHTER; + alienArray[1] = CD_PROTOFIGHTER; + alienArray[2] = CD_AIMFIGHTER; + break; + case 18: + numberOfAliens = 2; + alienArray[0] = CD_DUALFIGHTER; + alienArray[1] = CD_MINER; + break; + case 10: + case 15: + numberOfAliens = 1; + alienArray[0] = CD_ASTEROID; + break; + case 24: + numberOfAliens = 2; + alienArray[0] = CD_ASTEROID; + alienArray[1] = CD_ASTEROID2; + break; + case MAX_MISSIONS - 1: + numberOfAliens = 3; + alienArray[0] = CD_DUALFIGHTER; + alienArray[1] = CD_MISSILEBOAT; + alienArray[2] = CD_AIMFIGHTER; + if (currentGame.system == 2) + { + numberOfAliens = 4; + alienArray[3] = CD_PROTOFIGHTER; + } + break; + default: + numberOfAliens = 1; + alienArray[0] = CD_DUALFIGHTER; + break; + } + + signed char randEnemy = alienArray[rand() % numberOfAliens]; + + if ((currentGame.area != 10) && (currentGame.area != 15) && (currentGame.area != 24)) + { + if ((currentGame.system == 1) && (currentGame.area == MAX_MISSIONS - 1)) + { + if ((rand() % 5) == 0) + randEnemy = CD_SLAVETRANSPORT; + } + + if ((rand() % 6) == 0) + randEnemy = CD_TRANSPORTSHIP; + } + + delete(alienArray); + + enemy[index] = defEnemy[randEnemy]; + enemy[index].active = 1; + enemy[index].face = rand() % 2; + enemy[index].owner = &enemy[index]; // Most enemies will own themselves + enemy[index].target = &enemy[index]; + enemy[index].thinktime = (50 + rand() % 50); + enemy[index].systemPower = enemy[index].maxShield; + enemy[index].deathCounter = 0 - (enemy[index].maxShield * 3); + enemy[index].hit = 0; + + Math::limitInt(&enemy[index].deathCounter, -250, 0); + + // Attempts to place an alien. If it fails, the alien is deactivated. + for (int i = 0 ; i < 100 ; i++) + { + if (placeAlien(&enemy[index])) + break; + enemy[index].active = 0; + + return 0; + } + + if (enemy[index].classDef == CD_CARGOSHIP) + addCargo(&enemy[index], P_CARGO); + + if (enemy[index].classDef == CD_MOBILE_RAY) + enemy[index].shield = 25; + + if (enemy[index].classDef == CD_ESCORT) + enemy[index].shield = 50; + + enemy[index].dx = Math::rrand(-2, 2); + enemy[index].dy = Math::rrand(-2, 2); + + enemy[index].ammo[0] = 0; + + if (currentGame.area == 18) + enemy[index].flags += FL_HASMINIMUMSPEED; + + return 1; +} + +void getPreDefinedAliens() +{ + FILE *fp; + char string[255]; + strcpy(string, ""); + + int index, alienType, placeAttempt; + int barrierSpeed = 1; + + sprintf(string, "data/aliens%d.dat", currentGame.area); + #if USEPACK + int dataLocation = locateDataInPak(string, 0); + if (dataLocation == -1) + return; + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen(string, "rb"); + if (fp == NULL) + return; + #endif + + fscanf(fp, "%d ", &index); + + while (index != -1) + { + placeAttempt = 0; + + fscanf(fp, "%d ", &alienType); + + enemy[index] = defEnemy[alienType]; + enemy[index].owner = &enemy[index]; + enemy[index].target = &enemy[index]; + enemy[index].face = rand() % 2; + enemy[index].active = 1; + + /* + we make 1000 attempts to place this enemy since it is required. If after 1000 attempts + we still have managed to place the alien, then it simple isn't going to happen and we + will just exit the same. The chances of this happening are very very low! + */ + while (true) + { + placeAttempt++; + + if (placeAlien(&enemy[index])) + break; + + if (placeAttempt > 1000) + showErrorAndExit(2, ""); + } + + if (currentGame.area == 2) + addCargo(&enemy[index], P_CARGO); + else if (currentGame.area == 7) + addCargo(&enemy[index], P_PHOEBE); + + if (index == WC_KLINE) + { + enemy[WC_KLINE].target = &player; + if (currentGame.area == 25) + enemy[WC_KLINE].shield = 500; + } + + if (enemy[index].classDef == CD_CLOAKFIGHTER) + { + enemy[index].active = 0; + enemy[index].maxShield = enemy[index].shield = 400; + enemy[index].flags -= FL_RUNSAWAY; + enemy[index].speed = 3; + } + + if ((enemy[index].classDef == CD_MOBILE_RAY) && (index >= 11)) + { + enemy[index].active = 0; + } + + if (enemy[index].classDef == CD_FIREFLY) + { + enemy[index].active = 0; + } + + if (enemy[index].classDef == CD_BARRIER) + { + enemy[index].owner = &enemy[WC_BOSS]; + enemy[index].speed = barrierSpeed; + barrierSpeed++; + } + + if ((currentGame.area == 17) && (enemy[index].classDef == CD_BOSS)) + { + enemy[index].imageIndex[1] = 29; + enemy[index].flags += FL_IMMORTAL; + } + + if (currentGame.area == 18) + enemy[index].flags += FL_HASMINIMUMSPEED; + + if (currentGame.area == 23) + { + enemy[index].flags = FL_WEAPCO; + if (index == WC_BOSS) + enemy[index].chance[1] = 5; + } + + fscanf(fp, "%d ", &index); + } + + fclose(fp); + + if (currentGame.area == 5) + { + enemy[WC_BOSS].target = &player; + enemy[WC_BOSS].x = -400; + enemy[WC_BOSS].y = 300; + + enemy[13].owner = &enemy[WC_BOSS]; + enemy[13].target = &player; + enemy[13].dx = -25; + enemy[13].dy = -21; + + enemy[12].owner = &enemy[WC_BOSS]; + enemy[12].target = &player; + enemy[12].dx = -20; + enemy[12].dy = 37; + } + else if ((currentGame.area == 11) || (currentGame.area == 14)) + { + enemy[WC_BOSS].target = &player; + enemy[WC_BOSS].x = -400; + enemy[WC_BOSS].y = 300; + + enemy[13].owner = &enemy[WC_BOSS]; + enemy[13].target = &player; + enemy[13].dx = 15; + enemy[13].dy = -22; + + enemy[12].owner = &enemy[WC_BOSS]; + enemy[12].target = &player; + enemy[12].dx = 15; + enemy[12].dy = 22; + + enemy[11].owner = &enemy[13]; + enemy[11].target = &player; + enemy[11].dx = -35; + enemy[11].dy = -12; + + enemy[10].owner = &enemy[12]; + enemy[10].target = &player; + enemy[10].dx = -35; + enemy[10].dy = 20; + + if (currentGame.area == 14) + { + enemy[WC_BOSS].AIType = AI_EVASIVE; + + for (int i = 10 ; i < 15 ; i++) + { + enemy[i].imageIndex[0] += 15; + enemy[i].imageIndex[1] += 15; + + enemy[i].image[0] = graphics.shipShape[enemy[i].imageIndex[0]]; + enemy[i].image[1] = graphics.shipShape[enemy[i].imageIndex[1]]; + } + } + } + else if (currentGame.area == 21) + { + enemy[WC_BOSS].target = &player; + enemy[WC_BOSS].x = 400; + enemy[WC_BOSS].y = 300; + + enemy[13].owner = &enemy[WC_BOSS]; + enemy[13].dy = 20; + + enemy[12].owner = &enemy[WC_BOSS]; + enemy[12].dy = -16; + } +} + +void addFriendly(int type) +{ + if (type != FR_SID) + enemy[type] = defEnemy[CD_FRIEND]; + else + enemy[type] = defEnemy[CD_SID]; + + enemy[type].owner = &enemy[type]; + enemy[type].target = &enemy[type]; + enemy[type].active = 1; + + if (rand() % 2 == 0) + enemy[type].x = Math::rrand(400, 550); + else + enemy[type].x = Math::rrand(250, 400); + + if (rand() % 2 == 0) + enemy[type].y = Math::rrand(300, 450); + else + enemy[type].y = Math::rrand(150, 300); + + if (type == FR_PHOEBE) + enemy[type].classDef = CD_PHOEBE; + + if (type == FR_URSULA) + enemy[type].classDef = CD_URSULA; + + // For the sake of it being the final battle :) + if (currentGame.area == 25) + enemy[type].flags += FL_IMMORTAL; +} + +void setTarget(int index) +{ + engine.targetIndex = index; + engine.targetShield = 85; + engine.targetShield /= enemy[index].shield; + + engine.targetArrowTimer = -1; + if (currentGame.area == 10) + engine.targetArrowTimer = 0; +} + +void initAliens() +{ + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + enemy[i].active = 0; + enemy[i].shield = -1; + enemy[i].flags = 0; + } + + engine.targetIndex = -1; + + getPreDefinedAliens(); + + // specific for Phoebe being captured! + if (currentGame.area == 7) + currentGame.hasWingMate1 = 1; + + if (currentGame.area == 11) + enemy[WC_KLINE].active = 0; + + for (int i = 0 ; i < engine.maxAliens ; i++) + addAlien(); + + if (currentGame.hasWingMate1) + addFriendly(FR_PHOEBE); + + if (currentGame.hasWingMate2) + addFriendly(FR_URSULA); + + if ((currentGame.area == 9) || (currentGame.area == 17) || (currentGame.area == 25)) + addFriendly(FR_SID); + + // Disable Wingmates for certain missions + switch (currentGame.area) + { + case 7: + case 9: + case 10: + case 15: + case 16: + case 18: + case 24: + case 26: + enemy[FR_PHOEBE].active = 0; + enemy[FR_URSULA].active = 0; + break; + } + + if (currentGame.area == 10) + { + enemy[0].collectChance = 100; + enemy[0].collectType = P_ESCAPEPOD; + } + + // Some specifics for interception missions + if (currentGame.area == MAX_MISSIONS - 1) + { + if ((currentGame.system > 1) && ((rand() % 5) == 0)) + { + enemy[WC_KLINE] = defEnemy[CD_KLINE]; + enemy[WC_KLINE].owner = &enemy[WC_KLINE]; + enemy[WC_KLINE].target = &player; + enemy[WC_KLINE].shield = 100; + enemy[WC_KLINE].active = 1; + enemy[WC_KLINE].x = player.x + 1000; + enemy[WC_KLINE].y = player.y; + setTarget(WC_KLINE); + } + + if ((currentGame.system == 2) && (currentGame.experimentalShield > 0)) + { + if ((rand() % 2) == 0) + { + enemy[10] = defEnemy[CD_CLOAKFIGHTER]; + enemy[10].owner = &enemy[10]; + enemy[10].target = &enemy[10]; + enemy[10].shield = 1000; + enemy[10].active = 1; + enemy[10].x = player.x - 1000; + enemy[10].y = player.y; + setTarget(10); + enemy[10].shield = currentGame.experimentalShield; + } + } + } + + if (currentGame.area == 26) + { + enemy[WC_KLINE].flags += FL_IMMORTAL; + enemy[WC_KLINE].flags += FL_NOFIRE; + enemy[WC_KLINE].flags += FL_NOMOVE; + + enemy[WC_KLINE].x = 600; + enemy[WC_KLINE].y = 300; + + enemy[WC_KLINE].deathCounter = -250; + enemy[WC_KLINE].maxShield = 1500; + enemy[WC_KLINE].shield = 500; + } + + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + enemy[i].systemPower = enemy[i].maxShield; + enemy[i].deathCounter = 0 - (enemy[i].maxShield * 3); + Math::limitInt(&enemy[i].deathCounter, -350, 0); + } + + // Set target energy meter + switch (currentGame.area) + { + case 5: + case 11: + case 13: + case 17: + case 18: + case 19: + case 20: + case 21: + case 23: + setTarget(WC_BOSS); + break; + case 7: + setTarget(FR_PHOEBE); + break; + case 8: + setTarget(19); + break; + case 9: + setTarget(FR_SID); + break; + case 10: + setTarget(0); + break; + case 25: + case 26: + setTarget(WC_KLINE); + break; + default: + break; + } +} + +/* +"Looks" for an enemy by picking a randomly active enemy and using them +as a target. If the target is too far away, it will be ignored. +*/ +void searchForTarget(object *theEnemy) +{ + int i; + + if (theEnemy->flags & FL_WEAPCO) + { + i = (rand() % 10); + + if (i == 0) + { + theEnemy->target = &player; + return; + } + } + + i = rand() % MAX_ALIENS; + + object *targetEnemy = &enemy[i]; + + // Tell Sid not to attack craft that are already disabled or can + // return fire. This will save him from messing about (unless we're on the last mission) + if ((theEnemy->classDef == CD_SID) && (currentGame.area != 25)) + { + if ((targetEnemy->flags & FL_DISABLED) || (!(targetEnemy->flags & FL_NOFIRE))) + return; + } + + // Tell Phoebe and Ursula not to attack ships that cannot fire or are disabled (unless we're on the last mission) + if (currentGame.area != 25) + { + if ((theEnemy->classDef == CD_PHOEBE) || (theEnemy->classDef == CD_URSULA)) + { + // Don't attack the boss or we could be here all day(!) + if (targetEnemy->classDef == CD_BOSS) + return; + + if ((targetEnemy->flags & FL_DISABLED) || (targetEnemy->flags & FL_NOFIRE)) + return; + } + } + + if ((targetEnemy->shield < 1) || (!targetEnemy->active)) + return; + + if ((targetEnemy->flags & FL_WEAPCO) && (theEnemy->flags & FL_WEAPCO)) + return; + + if ((targetEnemy->flags & FL_FRIEND) && (theEnemy->flags & FL_FRIEND)) + return; + + if (abs((int)theEnemy->x - (int)theEnemy->target->x) > 550) + return; + + if (abs((int)theEnemy->y - (int)theEnemy->target->y) > 400) + return; + + theEnemy->target = targetEnemy; +} + +int traceTarget(object *theEnemy) +{ + // Do various checks to see if the alien can fire at + // the target. Start with the most obvious checks. + + // No target + if (theEnemy->target == theEnemy) + return 0; + + // Whilst firing a Ray, no other weapons can be fired! + if (theEnemy->flags & FL_FIRERAY) + return 0; + + // The target is on the same side as you! + if ((theEnemy->flags & FL_WEAPCO) && (theEnemy->target->flags & FL_WEAPCO)) + return 0; + if ((theEnemy->flags & FL_FRIEND) && (theEnemy->target->flags & FL_FRIEND)) + return 0; + + // You're facing the wrong way + if ((theEnemy->face == 0) && (theEnemy->target->x < theEnemy->x)) + return 0; + if ((theEnemy->face == 1) && (theEnemy->target->x > theEnemy->x)) + return 0; + + // Slightly more than half a screen away from you + if (abs((int)theEnemy->x - (int)theEnemy->target->x) > 550) + return 0; + + if ((theEnemy->flags & FL_AIMS) || (theEnemy->flags & FL_CONTINUOUS_FIRE)) + return 1; + + // Not at the correct vertical height + if ((theEnemy->y < theEnemy->target->y - 15) || (theEnemy->y > theEnemy->target->y + theEnemy->target->image[0]->h + 15)) + return 0; + + return 1; +} + +/* +Currently only used for the allies. Whilst flying around, the allies will fire on +any enemy craft that enter their line of sight. +*/ +int traceView(object *theEnemy) +{ + object *anEnemy = enemy; + + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + if ((theEnemy != anEnemy) && (anEnemy->flags & FL_WEAPCO) && (anEnemy->shield > 0)) + { + if ((theEnemy->y > anEnemy->y - 15) && (theEnemy->y < anEnemy->y + anEnemy->image[0]->h + 15)) + { + if ((theEnemy->face == 1) && (anEnemy->x < theEnemy->x)) + return 1; + if ((theEnemy->face == 0) && (anEnemy->x > theEnemy->x)) + return 1; + } + } + + *anEnemy++; + } + + return 0; +} + +void moveAndSeparate(object *theEnemy) +{ + signed char checkCollisions = 1; + signed char hasCollided = 0; + + // don't worry about bumping into other craft + if ((theEnemy->flags & FL_LEAVESECTOR) || (theEnemy->classDef == CD_DRONE) || (currentGame.area == 18)) + checkCollisions = 0; + + if (theEnemy->shield < 1) + checkCollisions = 0; + + if (theEnemy->owner == theEnemy) + { + if (theEnemy->flags & FL_CIRCLES) + { + if (theEnemy->face == 0) + { + theEnemy->dx += 0.02; + theEnemy->dy += 0.02; + } + else + { + theEnemy->dx -= 0.02; + theEnemy->dy -= 0.02; + } + + theEnemy->x -= (sin(theEnemy->dx) * 4); + theEnemy->y -= (cos(theEnemy->dy) * 4); + } + else + { + theEnemy->x -= theEnemy->dx; + theEnemy->y -= theEnemy->dy; + } + } + + object *anEnemy = enemy; + + if (checkCollisions) + { + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + if ((theEnemy->flags & FL_LEAVESECTOR) || (theEnemy->classDef == CD_DRONE) || (theEnemy->classDef == CD_ASTEROID2) || (theEnemy->owner == anEnemy->owner) || (theEnemy->owner->owner == anEnemy->owner) || (anEnemy->shield < 1)) + { + *anEnemy++; + continue; + } + + if (Collision::collision(theEnemy, anEnemy)) + { + if ((anEnemy->classDef == CD_BARRIER) && (anEnemy->owner != theEnemy)) + { + theEnemy->shield--; + theEnemy->hit = 3; + theEnemy->dx *= -1; + theEnemy->dy *= -1; + playSound(SFX_HIT); + } + + if (anEnemy->owner == anEnemy) + hasCollided = 1; + } + + *anEnemy++; + } + } + + // Handle a collision with the player + if ((player.shield > 0) && (theEnemy->shield > 0) && (checkCollisions)) + { + if (Collision::collision(theEnemy, &player)) + { + hasCollided = 1; + + if (theEnemy->classDef == CD_ASTEROID) + { + if (!engine.cheatShield) + player.shield -= theEnemy->shield; + theEnemy->shield = 0; + playSound(SFX_EXPLOSION); + setInfoLine("Warning: Asteroid Collision Damage!!", FONT_RED); + player.hit = 5; + playSound(SFX_HIT); + } + + if (theEnemy->classDef == CD_ASTEROID2) + { + if (!engine.cheatShield) + player.shield -= theEnemy->shield; + theEnemy->shield = 0; + playSound(SFX_EXPLOSION); + setInfoLine("Warning: Asteroid Collision Damage!!", FONT_RED); + player.hit = 5; + playSound(SFX_HIT); + } + + if (theEnemy->classDef == CD_BARRIER) + { + if (!engine.cheatShield) + player.shield--; + player.hit = 5; + playSound(SFX_HIT); + } + } + } + + // Got back to where you originally were... + if (theEnemy->owner == theEnemy) + { + if (hasCollided) + { + if (theEnemy->flags & FL_CIRCLES) + { + if (theEnemy->face == 0) + { + theEnemy->dx -= 0.02; + theEnemy->dy -= 0.02; + } + else + { + theEnemy->dx += 0.02; + theEnemy->dy += 0.02; + } + + theEnemy->x += (sin(theEnemy->dx) * 4); + theEnemy->y += (cos(theEnemy->dy) * 4); + + theEnemy->thinktime = 0; + } + else + { + theEnemy->x += theEnemy->dx; + theEnemy->y += theEnemy->dy; + + theEnemy->dx *= -1; + theEnemy->dy *= -1; + + theEnemy->dx *= 0.2; + theEnemy->dy *= 0.2; + + Math::limitInt(&theEnemy->thinktime, 0, 15); + } + } + } +} + +/* +Call this whenever a mission requires all the remaining aliens to +automatically die +*/ +void killAllAliens() +{ + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + if ((enemy[i].flags & FL_WEAPCO) && (enemy[i].active) && (enemy[i].shield > 0)) + enemy[i].shield = 0; + } +} + +void doAliens() +{ + static float barrierLoop = 0; + + barrierLoop += 0.2; + + // A global variable for checking if all the aliens are dead + engine.allAliensDead = 1; + + signed char canFire; + int shapeToUse; + + object *theEnemy = enemy; + + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + if (theEnemy->active) + { + if (theEnemy->shield > 0) + { + if ((theEnemy->flags & FL_WEAPCO) && (!(theEnemy->flags & FL_DISABLED))) + engine.allAliensDead = 0; + + // Set part attributes + if (theEnemy->owner != theEnemy) + { + theEnemy->face = theEnemy->owner->face; + + if (theEnemy->face == 0) + theEnemy->x = theEnemy->owner->x - theEnemy->dx; + else + theEnemy->x = theEnemy->owner->x + theEnemy->owner->image[0]->w + theEnemy->dx - theEnemy->image[0]->w; + + theEnemy->y = (theEnemy->owner->y + theEnemy->dy); + + if (theEnemy->owner->shield < 1) + { + if ((theEnemy->classDef != CD_URANUSBOSSWING1) && (theEnemy->classDef != CD_URANUSBOSSWING2)) + { + theEnemy->shield = 0; + } + else + { + theEnemy->flags -= FL_IMMORTAL; + theEnemy->owner = theEnemy; + theEnemy->chance[0] = 25; + } + } + } + + canFire = 1; // The alien is allowed to fire + + Math::limitInt(&--theEnemy->thinktime, 0, 250); + + if (theEnemy->target->shield < 1) + theEnemy->target = theEnemy; + + // Specific to Sid to stop him pissing about(!) + if ((theEnemy->classDef == CD_SID) && (theEnemy->target->flags & FL_DISABLED)) + theEnemy->target = theEnemy; + + if (theEnemy->target == theEnemy) + { + if (engine.missionCompleteTimer == 0) + { + searchForTarget(theEnemy); + } + else + { + if (theEnemy->flags & FL_FRIEND) + { + theEnemy->target = &player; + theEnemy->thinktime = 1; + } + } + } + + if ((!(theEnemy->flags & FL_DISABLED)) && (theEnemy->thinktime == 0) && (theEnemy->target != theEnemy) && (theEnemy->owner == theEnemy)) + { + if (theEnemy->classDef != CD_KLINE) + setEnemyAI(theEnemy); + else + setKlineAI(theEnemy); + + theEnemy->thinktime = (rand() % 25) * 10; + + // Face direction of movement unless you always face your target(!) + + if (!(theEnemy->flags & FL_ALWAYSFACE)) + { + theEnemy->face = 0; + if (theEnemy->dx > 0) theEnemy->face = 1; + } + + Math::limitFloat(&theEnemy->dx, 0 - theEnemy->speed, theEnemy->speed); + Math::limitFloat(&theEnemy->dy, 0 - theEnemy->speed, theEnemy->speed); + + } + + if (theEnemy->flags & FL_ALWAYSFACE) + { + theEnemy->face = 0; + if (theEnemy->x > theEnemy->target->x) theEnemy->face = 1; + } + + if ((currentGame.area == 18) && (theEnemy->classDef == CD_BOSS)) + theEnemy->face = 0; + + if ((theEnemy->flags & FL_DEPLOYDRONES) && ((rand() % 300) == 0)) + addDrone(theEnemy); + + if (theEnemy->flags & FL_LEAVESECTOR) + { + Math::limitFloat(&(theEnemy->dx -= 0.5), 0, -15); + theEnemy->dy = 0; + theEnemy->thinktime = 999; + theEnemy->face = 0; + + if (theEnemy->x >= 5000) + { + theEnemy->flags -= FL_LEAVESECTOR; + theEnemy->flags += FL_ESCAPED; + theEnemy->active = 0; + + if (theEnemy->classDef == CD_CLOAKFIGHTER) + { + currentGame.experimentalShield = theEnemy->shield; + setInfoLine("Experimental Fighter has fled", FONT_CYAN); + } + + theEnemy->shield = 0; + updateMissionRequirements(M_ESCAPE_TARGET, theEnemy->classDef, 1); + + if (theEnemy->classDef != CD_CLOAKFIGHTER) + updateMissionRequirements(M_DESTROY_TARGET_TYPE, theEnemy->classDef, 1); + } + } + + /* + This deals with the Experimental Fighter in Mordor (and Kline on the final mission) + It can cloak and uncloak at random. When cloaked, it's sprite is + not displayed. However the engine trail is still visible! + */ + if ((theEnemy->flags & FL_CANCLOAK) && ((rand() % 500) == 0)) + { + if (theEnemy->flags & FL_ISCLOAKED) + theEnemy->flags -= FL_ISCLOAKED; + else + theEnemy->flags += FL_ISCLOAKED; + playSound(SFX_CLOAK); + } + + // ------------ Barriers ------------------ + + if (theEnemy->classDef == CD_BARRIER) + { + theEnemy->dx = -10 + (sin(barrierLoop + theEnemy->speed) * 60); + theEnemy->dy = 20 + (cos(barrierLoop + theEnemy->speed) * 40); + } + + // ---------------------------------------- + + // ------------ Mobile Shields ------------ + + if (theEnemy->classDef == CD_MOBILESHIELD) + { + Math::limitInt(&(++enemy[WC_BOSS].shield), 0, enemy[WC_BOSS].maxShield); + } + + // ---------------------------------------- + + Math::limitChar(&--theEnemy->reload[0], 0, 999); + Math::limitChar(&--theEnemy->reload[1], 0, 999); + + if ((!(theEnemy->flags & FL_DISABLED)) && (!(theEnemy->flags & FL_NOFIRE))) + { + if ((theEnemy->target->shield > 0)) + canFire = traceTarget(theEnemy); + + if (((theEnemy->thinktime % 2) == 0) && (theEnemy->flags & FL_FRIEND)) + canFire = traceView(theEnemy); + } + else + { + canFire = 0; + } + + if ((canFire == 1) && (dev.fireAliens)) + { + if ((theEnemy->reload[0] == 0) && ((rand() % 1000 < theEnemy->chance[0]) || (theEnemy->flags & FL_CONTINUOUS_FIRE))) + { + fireBullet(theEnemy, 0); + } + if ((theEnemy->reload[1] == 0) && ((rand() % 1000 < theEnemy->chance[1]) || (theEnemy->flags & FL_CONTINUOUS_FIRE))) + { + if ((theEnemy->weaponType[1] != W_ENERGYRAY) && (theEnemy->weaponType[1] != W_LASER)) + { + if (theEnemy->weaponType[1] == W_CHARGER) + theEnemy->ammo[1] = 50 + rand() % 150; + fireBullet(theEnemy, 1); + } + else if (theEnemy->weaponType[1] == W_LASER) + { + theEnemy->flags += FL_FIRELASER; + } + else if ((theEnemy->weaponType[1] == W_ENERGYRAY) && (theEnemy->ammo[0] == 250)) + { + theEnemy->flags += FL_FIRERAY; + playSound(SFX_ENERGYRAY); + } + } + } + + // --------------- Ray specific stuff ------------------ + if (theEnemy->flags & FL_FIRERAY) + { + fireRay(theEnemy); + } + else + { + Math::limitChar(&++theEnemy->ammo[0], 0, 250); + } + // ------------------------------------------------------- + + // --------------- Laser specific stuff ------------------ + if (theEnemy->flags & FL_FIRELASER) + { + fireBullet(theEnemy, 1); + if ((rand() % 25) == 0) + theEnemy->flags -= FL_FIRELASER; + } + // ------------------------------------------------------- + + // ---------------- Mine specific stuff ------------------ + + if (theEnemy->flags & FL_DROPMINES) + if ((rand() % 150) == 0) + addCollectable(theEnemy->x, theEnemy->y, P_MINE, 25, 600 + rand() % 2400); + + // Kline drops mines a lot more often + if ((theEnemy->flags & FL_DROPMINES) && (theEnemy == &enemy[WC_KLINE])) + if ((rand() % 10) == 0) + addCollectable(theEnemy->x, theEnemy->y, P_MINE, 25, 600 + rand() % 2400); + + // ------------------------------------------------------- + + shapeToUse = theEnemy->imageIndex[theEnemy->face]; + + if (theEnemy->hit) + shapeToUse += SHIP_HIT_INDEX; + + Math::limitChar(&--theEnemy->hit, 0, 100); + + if ((theEnemy->x + theEnemy->image[0]->w > 0) && (theEnemy->x < 800) && (theEnemy->y + theEnemy->image[0]->h > 0) && (theEnemy->y < 600)) + { + if ((!(theEnemy->flags & FL_DISABLED)) && (theEnemy->classDef != CD_ASTEROID) && (theEnemy->classDef != CD_ASTEROID2)) + addEngine(theEnemy); + if ((!(theEnemy->flags & FL_ISCLOAKED)) || (theEnemy->hit > 0)) + graphics.blit(graphics.shipShape[shapeToUse], (int)theEnemy->x, (int)theEnemy->y); + if (theEnemy->flags & FL_DISABLED) + { + if ((rand() % 10) == 0) + addExplosion(theEnemy->x + (rand() % theEnemy->image[0]->w), theEnemy->y + (rand() % theEnemy->image[0]->h), E_ELECTRICAL); + } + } + + if ((currentGame.area == 24) && (theEnemy->x < -300)) + theEnemy->active = 0; + } + else + { + theEnemy->shield--; + if ((theEnemy->x > 0) && (theEnemy->x < 800) && (theEnemy->y > 0) && (theEnemy->y < 600)) + { + graphics.blit(theEnemy->image[theEnemy->face], (int)theEnemy->x, (int)theEnemy->y); + addExplosion(theEnemy->x + (rand() % theEnemy->image[0]->w), theEnemy->y + (rand() % theEnemy->image[0]->h), E_BIG_EXPLOSION); + } + if (theEnemy->shield < theEnemy->deathCounter) + { + theEnemy->active = 0; + if ((theEnemy->classDef == CD_BOSS) || (theEnemy->owner == &enemy[WC_BOSS]) || (theEnemy->flags & FL_FRIEND) || (theEnemy->classDef == CD_ASTEROID) || (theEnemy->classDef == CD_KLINE)) + addDebris((int)theEnemy->x, (int)theEnemy->y, theEnemy->maxShield); + + if (theEnemy->classDef == CD_ASTEROID) + { + int i = 1 + (rand() % 3); + for (int j = 0 ; j < i ; j++) + addSmallAsteroid(theEnemy); + } + } + } + + // Adjust the movement even whilst exploding + if ((dev.moveAliens) && (!(theEnemy->flags & FL_NOMOVE)) && (!(theEnemy->flags & FL_DISABLED))) + { + moveAndSeparate(theEnemy); + } + + if ((currentGame.area != 18) || (theEnemy->shield < 0)) + theEnemy->x += engine.ssx; + + theEnemy->y += engine.ssy; + } + + *theEnemy++; + } +} + +void setAlienShapes() +{ + for (int i = 0 ; i < MAX_DEFALIENS ; i++) + { + if (graphics.shipShape[defEnemy[i].imageIndex[0]] != NULL) + { + defEnemy[i].image[0] = graphics.shipShape[defEnemy[i].imageIndex[0]]; + defEnemy[i].image[1] = graphics.shipShape[defEnemy[i].imageIndex[1]]; + defEnemy[i].engineX = defEnemy[i].image[0]->w; + defEnemy[i].engineY = (defEnemy[i].image[0]->h / 2); + } + } +} + +#if USEPACK + +void loadAliens() +{ + int dataLocation = locateDataInPak("data/aliens.dat", 1); + int def, ai, speed, shield, max, image1, image2, weapon1, weapon2, chance1, chance2, score; + int collectChance, collectType, collectVal, flags; + + FILE *fp; + + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + + for (int i = 0 ; i < MAX_DEFALIENS ; i++) + { + fscanf(fp, "%d", &def); + fscanf(fp, "%d", &ai); + fscanf(fp, "%d", &speed); + fscanf(fp, "%d", &shield); + fscanf(fp, "%d", &max); + fscanf(fp, "%d", &image1); + fscanf(fp, "%d", &image2); + fscanf(fp, "%d", &weapon1); + fscanf(fp, "%d", &weapon2); + fscanf(fp, "%d", &chance1); + fscanf(fp, "%d", &chance2); + fscanf(fp, "%d", &score); + fscanf(fp, "%d", &collectChance); + fscanf(fp, "%d", &collectType); + fscanf(fp, "%d", &collectVal); + fscanf(fp, "%d", &flags); + + defEnemy[i].classDef = def; + defEnemy[i].AIType = ai; + defEnemy[i].speed = speed; + defEnemy[i].shield = shield; + defEnemy[i].maxShield = max; + defEnemy[i].imageIndex[0] = image1; + defEnemy[i].imageIndex[1] = image2; + defEnemy[i].weaponType[0] = weapon1; + defEnemy[i].weaponType[1] = weapon2; + defEnemy[i].chance[0] = chance1; + defEnemy[i].chance[1] = chance2; + defEnemy[i].score = score; + defEnemy[i].collectChance = collectChance; + defEnemy[i].collectType = collectType; + defEnemy[i].collectValue = collectVal; + defEnemy[i].flags = flags; + } + + fclose(fp); +} + +void defineAliens(){loadAliens();} + +#else + +void saveAliens() +{ + FILE *fp; + + fp = fopen("data/aliens.dat", "wb"); + if (fp == NULL) + { + printf("Unable to write Alien Data File\n"); + exit(1); + } + + for (int i = 0 ; i < MAX_DEFALIENS ; i++) + { + fprintf(fp, "%d ", defEnemy[i].classDef); + fprintf(fp, "%d ", defEnemy[i].AIType); + fprintf(fp, "%d ", defEnemy[i].speed); + fprintf(fp, "%d ", defEnemy[i].shield); + fprintf(fp, "%d ", defEnemy[i].maxShield); + fprintf(fp, "%d ", defEnemy[i].imageIndex[0]); + fprintf(fp, "%d ", defEnemy[i].imageIndex[1]); + fprintf(fp, "%d ", defEnemy[i].weaponType[0]); + fprintf(fp, "%d ", defEnemy[i].weaponType[1]); + fprintf(fp, "%d ", defEnemy[i].chance[0]); + fprintf(fp, "%d ", defEnemy[i].chance[1]); + fprintf(fp, "%d ", defEnemy[i].score); + fprintf(fp, "%d ", defEnemy[i].collectChance); + fprintf(fp, "%d ", defEnemy[i].collectType); + fprintf(fp, "%d ", defEnemy[i].collectValue); + fprintf(fp, "%d\n", defEnemy[i].flags); + } + + // Put an extra line for the PAK file "just in case" + fprintf(fp, "\n"); + + fclose(fp); +} + +/* +Will be dumped into a data file at the end of the project +*/ +void defineAliens() +{ + // Dual Plasma Fighter. + defEnemy[CD_DUALFIGHTER].classDef = CD_DUALFIGHTER; + defEnemy[CD_DUALFIGHTER].AIType = AI_NORMAL; + defEnemy[CD_DUALFIGHTER].speed = 4; + defEnemy[CD_DUALFIGHTER].maxShield = 5; + defEnemy[CD_DUALFIGHTER].shield = 5; + defEnemy[CD_DUALFIGHTER].imageIndex[0] = 2; + defEnemy[CD_DUALFIGHTER].imageIndex[1] = 3; + defEnemy[CD_DUALFIGHTER].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_DUALFIGHTER].weaponType[1] = W_ROCKETS; + defEnemy[CD_DUALFIGHTER].chance[0] = 100; + defEnemy[CD_DUALFIGHTER].chance[1] = 1; + defEnemy[CD_DUALFIGHTER].score = 25; + defEnemy[CD_DUALFIGHTER].collectChance = 50; + defEnemy[CD_DUALFIGHTER].collectType = P_ANYTHING; + defEnemy[CD_DUALFIGHTER].collectValue = 50; + defEnemy[CD_DUALFIGHTER].flags = FL_WEAPCO; + + // Missile Boat + defEnemy[CD_MISSILEBOAT].classDef = CD_MISSILEBOAT; + defEnemy[CD_MISSILEBOAT].AIType = AI_DEFENSIVE; + defEnemy[CD_MISSILEBOAT].speed = 2; + defEnemy[CD_MISSILEBOAT].maxShield = 50; + defEnemy[CD_MISSILEBOAT].shield = 50; + defEnemy[CD_MISSILEBOAT].imageIndex[0] = 4; + defEnemy[CD_MISSILEBOAT].imageIndex[1] = 5; + defEnemy[CD_MISSILEBOAT].weaponType[0] = W_ROCKETS; + defEnemy[CD_MISSILEBOAT].weaponType[1] = W_DOUBLE_ROCKETS; + defEnemy[CD_MISSILEBOAT].chance[0] = 25; + defEnemy[CD_MISSILEBOAT].chance[1] = 4; + defEnemy[CD_MISSILEBOAT].score = 250; + defEnemy[CD_MISSILEBOAT].collectChance = 25; + defEnemy[CD_MISSILEBOAT].collectType = P_ANYTHING; + defEnemy[CD_MISSILEBOAT].collectValue = 75; + defEnemy[CD_MISSILEBOAT].flags = FL_WEAPCO; + + //Prototype fighter + defEnemy[CD_PROTOFIGHTER].classDef = CD_PROTOFIGHTER; + defEnemy[CD_PROTOFIGHTER].AIType = AI_DEFENSIVE; + defEnemy[CD_PROTOFIGHTER].speed = 5; + defEnemy[CD_PROTOFIGHTER].maxShield = 15; + defEnemy[CD_PROTOFIGHTER].shield = 15; + defEnemy[CD_PROTOFIGHTER].imageIndex[0] = 6; + defEnemy[CD_PROTOFIGHTER].imageIndex[1] = 7; + defEnemy[CD_PROTOFIGHTER].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_PROTOFIGHTER].weaponType[1] = P_ANYTHING; + defEnemy[CD_PROTOFIGHTER].chance[0] = 100; + defEnemy[CD_PROTOFIGHTER].chance[1] = 1; + defEnemy[CD_PROTOFIGHTER].score = 50; + defEnemy[CD_PROTOFIGHTER].collectChance = 50; + defEnemy[CD_PROTOFIGHTER].collectType = P_ANYTHING; + defEnemy[CD_PROTOFIGHTER].collectValue = 50; + defEnemy[CD_PROTOFIGHTER].flags = FL_WEAPCO; + + // Phoebe and Ursula + defEnemy[CD_FRIEND].classDef = CD_FRIEND; + defEnemy[CD_FRIEND].AIType = AI_OFFENSIVE; + defEnemy[CD_FRIEND].speed = 3; + defEnemy[CD_FRIEND].maxShield = 50; + defEnemy[CD_FRIEND].shield = 50; + defEnemy[CD_FRIEND].imageIndex[0] = 20; + defEnemy[CD_FRIEND].imageIndex[1] = 21; + defEnemy[CD_FRIEND].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_FRIEND].weaponType[1] = W_HOMING_MISSILE; + defEnemy[CD_FRIEND].chance[0] = 100; + defEnemy[CD_FRIEND].chance[1] = 5; + defEnemy[CD_FRIEND].score = 0; + defEnemy[CD_FRIEND].collectChance = 0; + defEnemy[CD_FRIEND].collectType = P_CASH; + defEnemy[CD_FRIEND].collectValue = 0; + defEnemy[CD_FRIEND].flags = FL_FRIEND; + + // Boss 1 + defEnemy[CD_FRIGATE].classDef = CD_BOSS; + defEnemy[CD_FRIGATE].AIType = AI_NORMAL; + defEnemy[CD_FRIGATE].speed = 2; + defEnemy[CD_FRIGATE].maxShield = 550; + defEnemy[CD_FRIGATE].shield = 550; + defEnemy[CD_FRIGATE].imageIndex[0] = 8; + defEnemy[CD_FRIGATE].imageIndex[1] = 9; + defEnemy[CD_FRIGATE].weaponType[0] = W_MICRO_ROCKETS; + defEnemy[CD_FRIGATE].weaponType[1] = W_ENERGYRAY; + defEnemy[CD_FRIGATE].chance[0] = 100; + defEnemy[CD_FRIGATE].chance[1] = 85; + defEnemy[CD_FRIGATE].score = 500; + defEnemy[CD_FRIGATE].collectChance = 100; + defEnemy[CD_FRIGATE].collectType = P_CASH; + defEnemy[CD_FRIGATE].collectValue = 250; + defEnemy[CD_FRIGATE].flags = FL_WEAPCO; + + defEnemy[CD_FRIGATE_WING1].classDef = CD_FRIGATE_WING1; + defEnemy[CD_FRIGATE_WING1].AIType = AI_NORMAL; + defEnemy[CD_FRIGATE_WING1].speed = 2; + defEnemy[CD_FRIGATE_WING1].maxShield = 100; + defEnemy[CD_FRIGATE_WING1].shield = 100; + defEnemy[CD_FRIGATE_WING1].imageIndex[0] = 10; + defEnemy[CD_FRIGATE_WING1].imageIndex[1] = 11; + defEnemy[CD_FRIGATE_WING1].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_FRIGATE_WING1].weaponType[1] = W_ROCKETS; + defEnemy[CD_FRIGATE_WING1].chance[0] = 100; + defEnemy[CD_FRIGATE_WING1].chance[1] = 10; + defEnemy[CD_FRIGATE_WING1].score = 500; + defEnemy[CD_FRIGATE_WING1].collectChance = 100; + defEnemy[CD_FRIGATE_WING1].collectType = P_ANYTHING; + defEnemy[CD_FRIGATE_WING1].collectValue = 250; + defEnemy[CD_FRIGATE_WING1].flags = FL_WEAPCO + FL_DAMAGEOWNER; + + defEnemy[CD_FRIGATE_WING2].classDef = CD_FRIGATE_WING2; + defEnemy[CD_FRIGATE_WING2].AIType = AI_NORMAL; + defEnemy[CD_FRIGATE_WING2].speed = 2; + defEnemy[CD_FRIGATE_WING2].maxShield = 100; + defEnemy[CD_FRIGATE_WING2].shield = 100; + defEnemy[CD_FRIGATE_WING2].imageIndex[0] = 12; + defEnemy[CD_FRIGATE_WING2].imageIndex[1] = 13; + defEnemy[CD_FRIGATE_WING2].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_FRIGATE_WING2].weaponType[1] = W_ROCKETS; + defEnemy[CD_FRIGATE_WING2].chance[0] = 100; + defEnemy[CD_FRIGATE_WING2].chance[1] = 10; + defEnemy[CD_FRIGATE_WING2].score = 500; + defEnemy[CD_FRIGATE_WING2].collectChance = 100; + defEnemy[CD_FRIGATE_WING2].collectType = P_ANYTHING; + defEnemy[CD_FRIGATE_WING2].collectValue = 250; + defEnemy[CD_FRIGATE_WING2].flags = FL_WEAPCO + FL_DAMAGEOWNER; + + // Transport ship + defEnemy[CD_TRANSPORTSHIP].classDef = CD_TRANSPORTSHIP; + defEnemy[CD_TRANSPORTSHIP].AIType = AI_EVASIVE; + defEnemy[CD_TRANSPORTSHIP].speed = 4; + defEnemy[CD_TRANSPORTSHIP].maxShield = 10; + defEnemy[CD_TRANSPORTSHIP].shield = 10; + defEnemy[CD_TRANSPORTSHIP].imageIndex[0] = 14; + defEnemy[CD_TRANSPORTSHIP].imageIndex[1] = 15; + defEnemy[CD_TRANSPORTSHIP].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_TRANSPORTSHIP].weaponType[1] = W_DOUBLE_SHOT; + defEnemy[CD_TRANSPORTSHIP].chance[0] = 0; + defEnemy[CD_TRANSPORTSHIP].chance[1] = 0; + defEnemy[CD_TRANSPORTSHIP].score = 25; + defEnemy[CD_TRANSPORTSHIP].collectChance = 100; + defEnemy[CD_TRANSPORTSHIP].collectType = P_WEAPONS; + defEnemy[CD_TRANSPORTSHIP].collectValue = 30; + defEnemy[CD_TRANSPORTSHIP].flags = FL_WEAPCO + FL_NOFIRE; + + // Cargo ship + defEnemy[CD_CARGOSHIP].classDef = CD_CARGOSHIP; + defEnemy[CD_CARGOSHIP].AIType = AI_EVASIVE; + defEnemy[CD_CARGOSHIP].speed = 4; + defEnemy[CD_CARGOSHIP].maxShield = 10; + defEnemy[CD_CARGOSHIP].shield = 10; + defEnemy[CD_CARGOSHIP].imageIndex[0] = 22; + defEnemy[CD_CARGOSHIP].imageIndex[1] = 23; + defEnemy[CD_CARGOSHIP].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_CARGOSHIP].weaponType[1] = W_DOUBLE_SHOT; + defEnemy[CD_CARGOSHIP].chance[0] = 0; + defEnemy[CD_CARGOSHIP].chance[1] = 0; + defEnemy[CD_CARGOSHIP].score = 25; + defEnemy[CD_CARGOSHIP].collectChance = 50; + defEnemy[CD_CARGOSHIP].collectType = P_ANYTHING; + defEnemy[CD_CARGOSHIP].collectValue = 100; + defEnemy[CD_CARGOSHIP].flags = FL_WEAPCO + FL_NOFIRE; + + // Weapco Miner + defEnemy[CD_MINER].classDef = CD_MINER; + defEnemy[CD_MINER].AIType = AI_EVASIVE; + defEnemy[CD_MINER].speed = 4; + defEnemy[CD_MINER].maxShield = 25; + defEnemy[CD_MINER].shield = 25; + defEnemy[CD_MINER].imageIndex[0] = 16; + defEnemy[CD_MINER].imageIndex[1] = 17; + defEnemy[CD_MINER].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_MINER].weaponType[1] = W_DOUBLE_SHOT; + defEnemy[CD_MINER].chance[0] = 0; + defEnemy[CD_MINER].chance[1] = 0; + defEnemy[CD_MINER].score = 100; + defEnemy[CD_MINER].collectChance = 100; + defEnemy[CD_MINER].collectType = P_ANYTHING; + defEnemy[CD_MINER].collectValue = 30; + defEnemy[CD_MINER].flags = FL_WEAPCO + FL_NOFIRE + FL_DROPMINES; + + // Kline + defEnemy[CD_KLINE].classDef = CD_KLINE; + defEnemy[CD_KLINE].AIType = AI_OFFENSIVE; + defEnemy[CD_KLINE].speed = 5; + defEnemy[CD_KLINE].maxShield = 750; + defEnemy[CD_KLINE].shield = 750; + defEnemy[CD_KLINE].imageIndex[0] = 18; + defEnemy[CD_KLINE].imageIndex[1] = 19; + defEnemy[CD_KLINE].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_KLINE].weaponType[1] = W_MICRO_ROCKETS; + defEnemy[CD_KLINE].chance[0] = 100; + defEnemy[CD_KLINE].chance[1] = 2; + defEnemy[CD_KLINE].score = 0; + defEnemy[CD_KLINE].collectChance = 0; + defEnemy[CD_KLINE].collectType = P_ANYTHING; + defEnemy[CD_KLINE].collectValue = 0; + defEnemy[CD_KLINE].flags = FL_WEAPCO + FL_CANNOTDIE + FL_ALWAYSFACE + FL_CIRCLES; + + // Aim Fighter + defEnemy[CD_AIMFIGHTER].classDef = CD_AIMFIGHTER; + defEnemy[CD_AIMFIGHTER].AIType = AI_NORMAL; + defEnemy[CD_AIMFIGHTER].speed = 3; + defEnemy[CD_AIMFIGHTER].maxShield = 15; + defEnemy[CD_AIMFIGHTER].shield = 15; + defEnemy[CD_AIMFIGHTER].imageIndex[0] = 8; + defEnemy[CD_AIMFIGHTER].imageIndex[1] = 9; + defEnemy[CD_AIMFIGHTER].weaponType[0] = W_AIMED_SHOT; + defEnemy[CD_AIMFIGHTER].weaponType[1] = W_AIMED_SHOT; + defEnemy[CD_AIMFIGHTER].chance[0] = 7; + defEnemy[CD_AIMFIGHTER].chance[1] = 1; + defEnemy[CD_AIMFIGHTER].score = 50; + defEnemy[CD_AIMFIGHTER].collectChance = 75; + defEnemy[CD_AIMFIGHTER].collectType = P_ANYTHING; + defEnemy[CD_AIMFIGHTER].collectValue = 100; + defEnemy[CD_AIMFIGHTER].flags = FL_WEAPCO + FL_AIMS; + + // Slave ship + defEnemy[CD_SLAVETRANSPORT].classDef = CD_SLAVETRANSPORT; + defEnemy[CD_SLAVETRANSPORT].AIType = AI_EVASIVE; + defEnemy[CD_SLAVETRANSPORT].speed = 2; + defEnemy[CD_SLAVETRANSPORT].maxShield = 10; + defEnemy[CD_SLAVETRANSPORT].shield = 20; + defEnemy[CD_SLAVETRANSPORT].imageIndex[0] = 10; + defEnemy[CD_SLAVETRANSPORT].imageIndex[1] = 11; + defEnemy[CD_SLAVETRANSPORT].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_SLAVETRANSPORT].weaponType[1] = W_DOUBLE_SHOT; + defEnemy[CD_SLAVETRANSPORT].chance[0] = 0; + defEnemy[CD_SLAVETRANSPORT].chance[1] = 0; + defEnemy[CD_SLAVETRANSPORT].score = 25; + defEnemy[CD_SLAVETRANSPORT].collectChance = 100; + defEnemy[CD_SLAVETRANSPORT].collectType = P_SLAVES; + defEnemy[CD_SLAVETRANSPORT].collectValue = 25; + defEnemy[CD_SLAVETRANSPORT].flags = FL_WEAPCO + FL_NOFIRE; + + // Good Transport + defEnemy[CD_GOODTRANSPORT].classDef = CD_GOODTRANSPORT; + defEnemy[CD_GOODTRANSPORT].AIType = AI_EVASIVE; + defEnemy[CD_GOODTRANSPORT].speed = 3; + defEnemy[CD_GOODTRANSPORT].maxShield = 75; + defEnemy[CD_GOODTRANSPORT].shield = 75; + defEnemy[CD_GOODTRANSPORT].imageIndex[0] = 12; + defEnemy[CD_GOODTRANSPORT].imageIndex[1] = 13; + defEnemy[CD_GOODTRANSPORT].weaponType[0] = W_AIMED_SHOT; + defEnemy[CD_GOODTRANSPORT].weaponType[1] = W_AIMED_SHOT; + defEnemy[CD_GOODTRANSPORT].chance[0] = 100; + defEnemy[CD_GOODTRANSPORT].chance[1] = 100; + defEnemy[CD_GOODTRANSPORT].score = 0; + defEnemy[CD_GOODTRANSPORT].collectChance = 0; + defEnemy[CD_GOODTRANSPORT].collectType = P_ANYTHING; + defEnemy[CD_GOODTRANSPORT].collectValue = 0; + defEnemy[CD_GOODTRANSPORT].flags = FL_FRIEND + FL_NOFIRE + FL_AIMS; + + // Sid Wilson + defEnemy[CD_SID].classDef = CD_SID; + defEnemy[CD_SID].AIType = AI_NORMAL; + defEnemy[CD_SID].speed = 3; + defEnemy[CD_SID].maxShield = 50; + defEnemy[CD_SID].shield = 50; + defEnemy[CD_SID].imageIndex[0] = 24; + defEnemy[CD_SID].imageIndex[1] = 25; + defEnemy[CD_SID].weaponType[0] = W_IONCANNON; + defEnemy[CD_SID].weaponType[1] = W_IONCANNON; + defEnemy[CD_SID].chance[0] = 100; + defEnemy[CD_SID].chance[1] = 0; + defEnemy[CD_SID].score = 0; + defEnemy[CD_SID].collectChance = 0; + defEnemy[CD_SID].collectType = P_ANYTHING; + defEnemy[CD_SID].collectValue = 0; + defEnemy[CD_SID].flags = FL_FRIEND + FL_AIMS; + + // Mining Vessel Boss + defEnemy[CD_MINEBOSS].classDef = CD_BOSS; + defEnemy[CD_MINEBOSS].AIType = AI_NORMAL; + defEnemy[CD_MINEBOSS].speed = 3; + defEnemy[CD_MINEBOSS].maxShield = 1000; + defEnemy[CD_MINEBOSS].shield = 1000; + defEnemy[CD_MINEBOSS].imageIndex[0] = 26; + defEnemy[CD_MINEBOSS].imageIndex[1] = 27; + defEnemy[CD_MINEBOSS].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_MINEBOSS].weaponType[1] = W_SPREADSHOT; + defEnemy[CD_MINEBOSS].chance[0] = 0; + defEnemy[CD_MINEBOSS].chance[1] = 0; + defEnemy[CD_MINEBOSS].score = 1000; + defEnemy[CD_MINEBOSS].collectChance = 100; + defEnemy[CD_MINEBOSS].collectType = P_ANYTHING; + defEnemy[CD_MINEBOSS].collectValue = 255; + defEnemy[CD_MINEBOSS].flags = FL_WEAPCO + FL_IMMORTAL; + + defEnemy[CD_BOSS2_WING1].classDef = CD_BOSS2_WING1; + defEnemy[CD_BOSS2_WING1].AIType = AI_NORMAL; + defEnemy[CD_BOSS2_WING1].speed = 1; + defEnemy[CD_BOSS2_WING1].maxShield = 250; + defEnemy[CD_BOSS2_WING1].shield = 250; + defEnemy[CD_BOSS2_WING1].imageIndex[0] = 28; + defEnemy[CD_BOSS2_WING1].imageIndex[1] = 29; + defEnemy[CD_BOSS2_WING1].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_BOSS2_WING1].weaponType[1] = W_SPREADSHOT; + defEnemy[CD_BOSS2_WING1].chance[0] = 0; + defEnemy[CD_BOSS2_WING1].chance[1] = 0; + defEnemy[CD_BOSS2_WING1].score = 1000; + defEnemy[CD_BOSS2_WING1].collectChance = 100; + defEnemy[CD_BOSS2_WING1].collectType = P_ANYTHING; + defEnemy[CD_BOSS2_WING1].collectValue = 255; + defEnemy[CD_BOSS2_WING1].flags = FL_WEAPCO + FL_DAMAGEOWNER; + + defEnemy[CD_BOSS2_WING2].classDef = CD_BOSS2_WING2; + defEnemy[CD_BOSS2_WING2].AIType = AI_NORMAL; + defEnemy[CD_BOSS2_WING2].speed = 1; + defEnemy[CD_BOSS2_WING2].maxShield = 500; + defEnemy[CD_BOSS2_WING2].shield = 500; + defEnemy[CD_BOSS2_WING2].imageIndex[0] = 30; + defEnemy[CD_BOSS2_WING2].imageIndex[1] = 31; + defEnemy[CD_BOSS2_WING2].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_BOSS2_WING2].weaponType[1] = W_SPREADSHOT; + defEnemy[CD_BOSS2_WING2].chance[0] = 0; + defEnemy[CD_BOSS2_WING2].chance[1] = 0; + defEnemy[CD_BOSS2_WING2].score = 1000; + defEnemy[CD_BOSS2_WING2].collectChance = 100; + defEnemy[CD_BOSS2_WING2].collectType = P_ANYTHING; + defEnemy[CD_BOSS2_WING2].collectValue = 255; + defEnemy[CD_BOSS2_WING2].flags = FL_WEAPCO + FL_DEPLOYDRONES + FL_DAMAGEOWNER; + + defEnemy[CD_BOSS2_WING3].classDef = CD_BOSS2_WING3; + defEnemy[CD_BOSS2_WING3].AIType = AI_NORMAL; + defEnemy[CD_BOSS2_WING3].speed = 1; + defEnemy[CD_BOSS2_WING3].maxShield = 500; + defEnemy[CD_BOSS2_WING3].shield = 500; + defEnemy[CD_BOSS2_WING3].imageIndex[0] = 32; + defEnemy[CD_BOSS2_WING3].imageIndex[1] = 33; + defEnemy[CD_BOSS2_WING3].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_BOSS2_WING3].weaponType[1] = W_SPREADSHOT; + defEnemy[CD_BOSS2_WING3].chance[0] = 0; + defEnemy[CD_BOSS2_WING3].chance[1] = 0; + defEnemy[CD_BOSS2_WING3].score = 1000; + defEnemy[CD_BOSS2_WING3].collectChance = 100; + defEnemy[CD_BOSS2_WING3].collectType = P_ANYTHING; + defEnemy[CD_BOSS2_WING3].collectValue = 255; + defEnemy[CD_BOSS2_WING3].flags = FL_WEAPCO + FL_DEPLOYDRONES + FL_DAMAGEOWNER; + + defEnemy[CD_BOSS2_WING4].classDef = CD_BOSS2_WING4; + defEnemy[CD_BOSS2_WING4].AIType = AI_NORMAL; + defEnemy[CD_BOSS2_WING4].speed = 1; + defEnemy[CD_BOSS2_WING4].maxShield = 250; + defEnemy[CD_BOSS2_WING4].shield = 250; + defEnemy[CD_BOSS2_WING4].imageIndex[0] = 34; + defEnemy[CD_BOSS2_WING4].imageIndex[1] = 35; + defEnemy[CD_BOSS2_WING4].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_BOSS2_WING4].weaponType[1] = W_SPREADSHOT; + defEnemy[CD_BOSS2_WING4].chance[0] = 0; + defEnemy[CD_BOSS2_WING4].chance[1] = 0; + defEnemy[CD_BOSS2_WING4].score = 1000; + defEnemy[CD_BOSS2_WING4].collectChance = 100; + defEnemy[CD_BOSS2_WING4].collectType = P_ANYTHING; + defEnemy[CD_BOSS2_WING4].collectValue = 255; + defEnemy[CD_BOSS2_WING4].flags = FL_WEAPCO + FL_DAMAGEOWNER; + + // Drone + defEnemy[CD_DRONE].classDef = CD_DRONE; + defEnemy[CD_DRONE].AIType = AI_OFFENSIVE; + defEnemy[CD_DRONE].speed = 8; + defEnemy[CD_DRONE].maxShield = 5; + defEnemy[CD_DRONE].shield = 5; + defEnemy[CD_DRONE].imageIndex[0] = 36; + defEnemy[CD_DRONE].imageIndex[1] = 37; + defEnemy[CD_DRONE].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_DRONE].weaponType[1] = W_LASER; + defEnemy[CD_DRONE].chance[0] = 100; + defEnemy[CD_DRONE].chance[1] = 0; + defEnemy[CD_DRONE].score = 0; + defEnemy[CD_DRONE].collectChance = 10; + defEnemy[CD_DRONE].collectType = P_SHIELD; + defEnemy[CD_DRONE].collectValue = 1; + defEnemy[CD_DRONE].flags = FL_WEAPCO; + + // Experimental Fighter + defEnemy[CD_CLOAKFIGHTER].classDef = CD_CLOAKFIGHTER; + defEnemy[CD_CLOAKFIGHTER].AIType = AI_OFFENSIVE; + defEnemy[CD_CLOAKFIGHTER].speed = 6; + defEnemy[CD_CLOAKFIGHTER].maxShield = 1000; + defEnemy[CD_CLOAKFIGHTER].shield = 1000; + defEnemy[CD_CLOAKFIGHTER].imageIndex[0] = 10; + defEnemy[CD_CLOAKFIGHTER].imageIndex[1] = 11; + defEnemy[CD_CLOAKFIGHTER].weaponType[0] = W_SPREADSHOT; + defEnemy[CD_CLOAKFIGHTER].weaponType[1] = W_DOUBLE_ROCKETS; + defEnemy[CD_CLOAKFIGHTER].chance[0] = 100; + defEnemy[CD_CLOAKFIGHTER].chance[1] = 5; + defEnemy[CD_CLOAKFIGHTER].score = 550; + defEnemy[CD_CLOAKFIGHTER].collectChance = 100; + defEnemy[CD_CLOAKFIGHTER].collectType = P_CASH; + defEnemy[CD_CLOAKFIGHTER].collectValue = 255; + defEnemy[CD_CLOAKFIGHTER].flags = FL_WEAPCO + FL_CANCLOAK + FL_RUNSAWAY; + + // Evil Ursula + defEnemy[CD_EVILURSULA].classDef = CD_EVILURSULA; + defEnemy[CD_EVILURSULA].AIType = AI_OFFENSIVE; + defEnemy[CD_EVILURSULA].speed = 5; + defEnemy[CD_EVILURSULA].maxShield = 500; + defEnemy[CD_EVILURSULA].shield = 500; + defEnemy[CD_EVILURSULA].imageIndex[0] = 12; + defEnemy[CD_EVILURSULA].imageIndex[1] = 13; + defEnemy[CD_EVILURSULA].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_EVILURSULA].weaponType[1] = W_HOMING_MISSILE; + defEnemy[CD_EVILURSULA].chance[0] = 100; + defEnemy[CD_EVILURSULA].chance[1] = 100; + defEnemy[CD_EVILURSULA].score = 500; + defEnemy[CD_EVILURSULA].collectChance = 100; + defEnemy[CD_EVILURSULA].collectType = P_ESCAPEPOD; + defEnemy[CD_EVILURSULA].collectValue = 1; + defEnemy[CD_EVILURSULA].flags = FL_WEAPCO; + + // Mercenary + defEnemy[CD_KRASS].classDef = CD_KRASS; + defEnemy[CD_KRASS].AIType = AI_OFFENSIVE; + defEnemy[CD_KRASS].speed = 5; + defEnemy[CD_KRASS].maxShield = 1000; + defEnemy[CD_KRASS].shield = 1000; + defEnemy[CD_KRASS].imageIndex[0] = 26; + defEnemy[CD_KRASS].imageIndex[1] = 27; + defEnemy[CD_KRASS].weaponType[0] = W_SPREADSHOT; + defEnemy[CD_KRASS].weaponType[1] = W_CHARGER; + defEnemy[CD_KRASS].chance[0] = 100; + defEnemy[CD_KRASS].chance[1] = 0; + defEnemy[CD_KRASS].score = 2000; + defEnemy[CD_KRASS].collectChance = 100; + defEnemy[CD_KRASS].collectType = P_ANYTHING; + defEnemy[CD_KRASS].collectValue = 255; + defEnemy[CD_KRASS].flags = FL_FRIEND + FL_IMMORTAL; + + // Executive Transport + defEnemy[CD_EXEC].classDef = CD_BOSS; + defEnemy[CD_EXEC].AIType = AI_NORMAL; + defEnemy[CD_EXEC].speed = 5; + defEnemy[CD_EXEC].maxShield = 1000; + defEnemy[CD_EXEC].shield = 1000; + defEnemy[CD_EXEC].imageIndex[0] = 28; + defEnemy[CD_EXEC].imageIndex[1] = 28; + defEnemy[CD_EXEC].weaponType[0] = W_SPREADSHOT; + defEnemy[CD_EXEC].weaponType[1] = W_HOMING_MISSILE; + defEnemy[CD_EXEC].chance[0] = 0; + defEnemy[CD_EXEC].chance[1] = 0; + defEnemy[CD_EXEC].score = 2000; + defEnemy[CD_EXEC].collectChance = 0; + defEnemy[CD_EXEC].collectType = P_ANYTHING; + defEnemy[CD_EXEC].collectValue = 0; + defEnemy[CD_EXEC].flags = FL_WEAPCO + FL_NOFIRE; + + // Asteroid + defEnemy[CD_ASTEROID].classDef = CD_ASTEROID; + defEnemy[CD_ASTEROID].AIType = AI_WANDER; + defEnemy[CD_ASTEROID].speed = 1; + defEnemy[CD_ASTEROID].maxShield = 50; + defEnemy[CD_ASTEROID].shield = 50; + defEnemy[CD_ASTEROID].imageIndex[0] = 38; + defEnemy[CD_ASTEROID].imageIndex[1] = 38; + defEnemy[CD_ASTEROID].weaponType[0] = W_SPREADSHOT; + defEnemy[CD_ASTEROID].weaponType[1] = W_HOMING_MISSILE; + defEnemy[CD_ASTEROID].chance[0] = 0; + defEnemy[CD_ASTEROID].chance[1] = 0; + defEnemy[CD_ASTEROID].score = 0; + defEnemy[CD_ASTEROID].collectChance = 25; + defEnemy[CD_ASTEROID].collectType = P_ORE; + defEnemy[CD_ASTEROID].collectValue = 1; + defEnemy[CD_ASTEROID].flags = FL_WEAPCO; + + defEnemy[CD_ASTEROID2].classDef = CD_ASTEROID2; + defEnemy[CD_ASTEROID2].AIType = AI_WANDER; + defEnemy[CD_ASTEROID2].speed = 1; + defEnemy[CD_ASTEROID2].maxShield = 10; + defEnemy[CD_ASTEROID2].shield = 10; + defEnemy[CD_ASTEROID2].imageIndex[0] = 39; + defEnemy[CD_ASTEROID2].imageIndex[1] = 40; + defEnemy[CD_ASTEROID2].weaponType[0] = W_SPREADSHOT; + defEnemy[CD_ASTEROID2].weaponType[1] = W_HOMING_MISSILE; + defEnemy[CD_ASTEROID2].chance[0] = 0; + defEnemy[CD_ASTEROID2].chance[1] = 0; + defEnemy[CD_ASTEROID2].score = 0; + defEnemy[CD_ASTEROID2].collectChance = 25; + defEnemy[CD_ASTEROID2].collectType = P_ORE; + defEnemy[CD_ASTEROID2].collectValue = 1; + defEnemy[CD_ASTEROID2].flags = FL_WEAPCO; + + // Escort + defEnemy[CD_ESCORT].classDef = CD_ESCORT; + defEnemy[CD_ESCORT].AIType = AI_NORMAL; + defEnemy[CD_ESCORT].speed = 3; + defEnemy[CD_ESCORT].maxShield = 200; + defEnemy[CD_ESCORT].shield = 200; + defEnemy[CD_ESCORT].imageIndex[0] = 30; + defEnemy[CD_ESCORT].imageIndex[1] = 31; + defEnemy[CD_ESCORT].weaponType[0] = W_LASER; + defEnemy[CD_ESCORT].weaponType[1] = W_LASER; + defEnemy[CD_ESCORT].chance[0] = 25; + defEnemy[CD_ESCORT].chance[1] = 25; + defEnemy[CD_ESCORT].score = 450; + defEnemy[CD_ESCORT].collectChance = 100; + defEnemy[CD_ESCORT].collectType = P_ANYTHING; + defEnemy[CD_ESCORT].collectValue = 100; + defEnemy[CD_ESCORT].flags = FL_WEAPCO; + + // Mobile Ray Cannon + defEnemy[CD_MOBILE_RAY].classDef = CD_MOBILE_RAY; + defEnemy[CD_MOBILE_RAY].AIType = AI_OFFENSIVE; + defEnemy[CD_MOBILE_RAY].speed = 5; + defEnemy[CD_MOBILE_RAY].maxShield = 250; + defEnemy[CD_MOBILE_RAY].shield = 250; + defEnemy[CD_MOBILE_RAY].imageIndex[0] = 10; + defEnemy[CD_MOBILE_RAY].imageIndex[1] = 11; + defEnemy[CD_MOBILE_RAY].weaponType[0] = W_ENERGYRAY; + defEnemy[CD_MOBILE_RAY].weaponType[1] = W_ENERGYRAY; + defEnemy[CD_MOBILE_RAY].chance[0] = 50; + defEnemy[CD_MOBILE_RAY].chance[1] = 50; + defEnemy[CD_MOBILE_RAY].score = 1000; + defEnemy[CD_MOBILE_RAY].collectChance = 75; + defEnemy[CD_MOBILE_RAY].collectType = P_SHIELD; + defEnemy[CD_MOBILE_RAY].collectValue = 2; + defEnemy[CD_MOBILE_RAY].flags = FL_WEAPCO; + + // Rebel Carrier + defEnemy[CD_REBELCARRIER].classDef = CD_REBELCARRIER; + defEnemy[CD_REBELCARRIER].AIType = AI_OFFENSIVE; + defEnemy[CD_REBELCARRIER].speed = 2; + defEnemy[CD_REBELCARRIER].maxShield = 100; + defEnemy[CD_REBELCARRIER].shield = 100; + defEnemy[CD_REBELCARRIER].imageIndex[0] = 32; + defEnemy[CD_REBELCARRIER].imageIndex[1] = 33; + defEnemy[CD_REBELCARRIER].weaponType[0] = W_DOUBLE_ROCKETS; + defEnemy[CD_REBELCARRIER].weaponType[1] = W_MICRO_ROCKETS; + defEnemy[CD_REBELCARRIER].chance[0] = 50; + defEnemy[CD_REBELCARRIER].chance[1] = 2; + defEnemy[CD_REBELCARRIER].score = 0; + defEnemy[CD_REBELCARRIER].collectChance = 0; + defEnemy[CD_REBELCARRIER].collectType = P_SHIELD; + defEnemy[CD_REBELCARRIER].collectValue = 0; + defEnemy[CD_REBELCARRIER].flags = FL_FRIEND; + + // Pluto Boss + defEnemy[CD_PLUTOBOSS].classDef = CD_PLUTOBOSS; + defEnemy[CD_PLUTOBOSS].AIType = AI_OFFENSIVE; + defEnemy[CD_PLUTOBOSS].speed = 4; + defEnemy[CD_PLUTOBOSS].maxShield = 500; + defEnemy[CD_PLUTOBOSS].shield = 500; + defEnemy[CD_PLUTOBOSS].imageIndex[0] = 12; + defEnemy[CD_PLUTOBOSS].imageIndex[1] = 13; + defEnemy[CD_PLUTOBOSS].weaponType[0] = W_DOUBLE_ROCKETS; + defEnemy[CD_PLUTOBOSS].weaponType[1] = W_MICRO_ROCKETS; + defEnemy[CD_PLUTOBOSS].chance[0] = 50; + defEnemy[CD_PLUTOBOSS].chance[1] = 2; + defEnemy[CD_PLUTOBOSS].score = 1000; + defEnemy[CD_PLUTOBOSS].collectChance = 0; + defEnemy[CD_PLUTOBOSS].collectType = P_SHIELD; + defEnemy[CD_PLUTOBOSS].collectValue = 0; + defEnemy[CD_PLUTOBOSS].flags = FL_WEAPCO; + + // Pluto Boss Barrier + defEnemy[CD_BARRIER].classDef = CD_BARRIER; + defEnemy[CD_BARRIER].AIType = AI_OFFENSIVE; + defEnemy[CD_BARRIER].speed = 1; + defEnemy[CD_BARRIER].maxShield = 250; + defEnemy[CD_BARRIER].shield = 250; + defEnemy[CD_BARRIER].imageIndex[0] = 32; + defEnemy[CD_BARRIER].imageIndex[1] = 33; + defEnemy[CD_BARRIER].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_BARRIER].weaponType[1] = W_MICRO_ROCKETS; + defEnemy[CD_BARRIER].chance[0] = 0; + defEnemy[CD_BARRIER].chance[1] = 0; + defEnemy[CD_BARRIER].score = 1000; + defEnemy[CD_BARRIER].collectChance = 100; + defEnemy[CD_BARRIER].collectType = P_ANYTHING; + defEnemy[CD_BARRIER].collectValue = 25; + defEnemy[CD_BARRIER].flags = FL_WEAPCO + FL_NOFIRE; + + // Neptune Boss + defEnemy[CD_NEPTUNEBOSS].classDef = CD_NEPTUNEBOSS; + defEnemy[CD_NEPTUNEBOSS].AIType = AI_OFFENSIVE; + defEnemy[CD_NEPTUNEBOSS].speed = 4; + defEnemy[CD_NEPTUNEBOSS].maxShield = 800; + defEnemy[CD_NEPTUNEBOSS].shield = 800; + defEnemy[CD_NEPTUNEBOSS].imageIndex[0] = 12; + defEnemy[CD_NEPTUNEBOSS].imageIndex[1] = 13; + defEnemy[CD_NEPTUNEBOSS].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_NEPTUNEBOSS].weaponType[1] = W_MICRO_ROCKETS; + defEnemy[CD_NEPTUNEBOSS].chance[0] = 100; + defEnemy[CD_NEPTUNEBOSS].chance[1] = 0; + defEnemy[CD_NEPTUNEBOSS].score = 1000; + defEnemy[CD_NEPTUNEBOSS].collectChance = 100; + defEnemy[CD_NEPTUNEBOSS].collectType = P_ANYTHING; + defEnemy[CD_NEPTUNEBOSS].collectValue = 25; + defEnemy[CD_NEPTUNEBOSS].flags = FL_WEAPCO; + + // Mobile Shield + defEnemy[CD_MOBILESHIELD].classDef = CD_MOBILESHIELD; + defEnemy[CD_MOBILESHIELD].AIType = AI_EVASIVE; + defEnemy[CD_MOBILESHIELD].speed = 6; + defEnemy[CD_MOBILESHIELD].maxShield = 150; + defEnemy[CD_MOBILESHIELD].shield = 150; + defEnemy[CD_MOBILESHIELD].imageIndex[0] = 34; + defEnemy[CD_MOBILESHIELD].imageIndex[1] = 35; + defEnemy[CD_MOBILESHIELD].weaponType[0] = W_DOUBLE_SHOT; + defEnemy[CD_MOBILESHIELD].weaponType[1] = W_MICRO_ROCKETS; + defEnemy[CD_MOBILESHIELD].chance[0] = 0; + defEnemy[CD_MOBILESHIELD].chance[1] = 0; + defEnemy[CD_MOBILESHIELD].score = 250; + defEnemy[CD_MOBILESHIELD].collectChance = 100; + defEnemy[CD_MOBILESHIELD].collectType = P_ANYTHING; + defEnemy[CD_MOBILESHIELD].collectValue = 25; + defEnemy[CD_MOBILESHIELD].flags = FL_WEAPCO + FL_NOFIRE; + + // Firefly + defEnemy[CD_FIREFLY].classDef = CD_FIREFLY; + defEnemy[CD_FIREFLY].AIType = AI_OFFENSIVE; + defEnemy[CD_FIREFLY].speed = 5; + defEnemy[CD_FIREFLY].maxShield = 250; + defEnemy[CD_FIREFLY].shield = 250; + defEnemy[CD_FIREFLY].imageIndex[0] = 0; + defEnemy[CD_FIREFLY].imageIndex[1] = 1; + defEnemy[CD_FIREFLY].weaponType[0] = W_TRIPLE_SHOT; + defEnemy[CD_FIREFLY].weaponType[1] = W_DOUBLE_ROCKETS; + defEnemy[CD_FIREFLY].chance[0] = 100; + defEnemy[CD_FIREFLY].chance[1] = 5; + defEnemy[CD_FIREFLY].score = 500; + defEnemy[CD_FIREFLY].collectChance = 100; + defEnemy[CD_FIREFLY].collectType = P_ANYTHING; + defEnemy[CD_FIREFLY].collectValue = 250; + defEnemy[CD_FIREFLY].flags = FL_WEAPCO; + + // Uranus Boss + defEnemy[CD_URANUSBOSS].classDef = CD_URANUSBOSS; + defEnemy[CD_URANUSBOSS].AIType = AI_OFFENSIVE; + defEnemy[CD_URANUSBOSS].speed = 4; + defEnemy[CD_URANUSBOSS].maxShield = 750; + defEnemy[CD_URANUSBOSS].shield = 750; + defEnemy[CD_URANUSBOSS].imageIndex[0] = 41; + defEnemy[CD_URANUSBOSS].imageIndex[1] = 42; + defEnemy[CD_URANUSBOSS].weaponType[0] = W_SPREADSHOT; + defEnemy[CD_URANUSBOSS].weaponType[1] = W_DOUBLE_ROCKETS; + defEnemy[CD_URANUSBOSS].chance[0] = 100; + defEnemy[CD_URANUSBOSS].chance[1] = 5; + defEnemy[CD_URANUSBOSS].score = 500; + defEnemy[CD_URANUSBOSS].collectChance = 100; + defEnemy[CD_URANUSBOSS].collectType = P_ANYTHING; + defEnemy[CD_URANUSBOSS].collectValue = 250; + defEnemy[CD_URANUSBOSS].flags = FL_WEAPCO; + + // Uranus Boss Wing 1 + defEnemy[CD_URANUSBOSSWING1].classDef = CD_URANUSBOSSWING1; + defEnemy[CD_URANUSBOSSWING1].AIType = AI_OFFENSIVE; + defEnemy[CD_URANUSBOSSWING1].speed = 4; + defEnemy[CD_URANUSBOSSWING1].maxShield = 250; + defEnemy[CD_URANUSBOSSWING1].shield = 250; + defEnemy[CD_URANUSBOSSWING1].imageIndex[0] = 43; + defEnemy[CD_URANUSBOSSWING1].imageIndex[1] = 44; + defEnemy[CD_URANUSBOSSWING1].weaponType[0] = W_DOUBLE_ROCKETS; + defEnemy[CD_URANUSBOSSWING1].weaponType[1] = W_DOUBLE_ROCKETS; + defEnemy[CD_URANUSBOSSWING1].chance[0] = 5; + defEnemy[CD_URANUSBOSSWING1].chance[1] = 0; + defEnemy[CD_URANUSBOSSWING1].score = 500; + defEnemy[CD_URANUSBOSSWING1].collectChance = 100; + defEnemy[CD_URANUSBOSSWING1].collectType = P_ANYTHING; + defEnemy[CD_URANUSBOSSWING1].collectValue = 250; + defEnemy[CD_URANUSBOSSWING1].flags = FL_WEAPCO + FL_IMMORTAL; + + // Uranus Boss Wing 2 + defEnemy[CD_URANUSBOSSWING2].classDef = CD_URANUSBOSSWING2; + defEnemy[CD_URANUSBOSSWING2].AIType = AI_OFFENSIVE; + defEnemy[CD_URANUSBOSSWING2].speed = 4; + defEnemy[CD_URANUSBOSSWING2].maxShield = 250; + defEnemy[CD_URANUSBOSSWING2].shield = 250; + defEnemy[CD_URANUSBOSSWING2].imageIndex[0] = 45; + defEnemy[CD_URANUSBOSSWING2].imageIndex[1] = 46; + defEnemy[CD_URANUSBOSSWING2].weaponType[0] = W_DOUBLE_ROCKETS; + defEnemy[CD_URANUSBOSSWING2].weaponType[1] = W_DOUBLE_ROCKETS; + defEnemy[CD_URANUSBOSSWING2].chance[0] = 5; + defEnemy[CD_URANUSBOSSWING2].chance[1] = 0; + defEnemy[CD_URANUSBOSSWING2].score = 500; + defEnemy[CD_URANUSBOSSWING2].collectChance = 100; + defEnemy[CD_URANUSBOSSWING2].collectType = P_ANYTHING; + defEnemy[CD_URANUSBOSSWING2].collectValue = 250; + defEnemy[CD_URANUSBOSSWING2].flags = FL_WEAPCO + FL_IMMORTAL; + + saveAliens(); +} + +#endif diff --git a/code/aliens.h b/code/aliens.h new file mode 100644 index 0000000..22f101a --- /dev/null +++ b/code/aliens.h @@ -0,0 +1,58 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void setEnemyAI(object *theEnemy); +extern void setKlineAI(object *theEnemy); +extern void fireBullet(object *attacker, int weaponType); +extern void addExplosion(float x, float y, int type); +extern void addEngine(object *craft); +extern void fireRay(object *attacker); +extern void addDebris(int x, int y, int amount); +extern void playSound(int sid); +extern int locateDataInPak(char *file, signed char required); +extern object *addCargo(object *owner, int cargoType); +extern void addCollectable(float x, float y, int type, int value, int life); +extern void updateMissionRequirements(int type, int id, int value); +extern void setInfoLine(char *in, int color); +extern void addBullet(object *theWeapon, object *attacker, int y, int dy); +extern void showErrorAndExit(int errorId, char *name); + +extern globalEngineVariables engine; +extern devVariables dev; +extern Game currentGame; +extern object player; +extern object enemy[MAX_ALIENS]; +extern Graphics graphics; +extern mission currentMission; +extern object weapon[MAX_WEAPONS]; + +object defEnemy[MAX_DEFALIENS]; // A predefined enemy diff --git a/code/audio.cpp b/code/audio.cpp new file mode 100644 index 0000000..46d5b4a --- /dev/null +++ b/code/audio.cpp @@ -0,0 +1,136 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "audio.h" + +void playSound(int sid) +{ + if ((!currentGame.useSound) || (!engine.useAudio)) + return; + + switch(sid) + { + case SFX_DEATH: + case SFX_CLOCK: + case SFX_FLY: + case SFX_SHIELDUP: + case SFX_PICKUP: + case SFX_CLOAK: + case SFX_PLASMA2: + case SFX_PLASMA3: + Mix_PlayChannel(-1, sound[sid], 0); + break; + case SFX_PLASMA: + case SFX_LASER: + Mix_PlayChannel(0, sound[sid], 0); + break; + case SFX_ENERGYRAY: + case SFX_MISSILE: + Mix_PlayChannel(1, sound[sid], 0); + break; + case SFX_HIT: + Mix_PlayChannel(4, sound[sid], 0); + break; + case SFX_EXPLOSION: + case SFX_DEBRIS: + case SFX_DEBRIS2: + Mix_PlayChannel(3, sound[sid], 0); + break; + } +} + +Mix_Chunk *loadSound(char *filename) +{ + Mix_Chunk *chunk; + + #if USEPACK + unpack(filename, PAK_WAV); + chunk = Mix_LoadWAV_RW(engine.sdlrw, 1); + #else + chunk = Mix_LoadWAV(filename); + #endif + + return chunk; +} + +void loadMusic(char *filename) +{ + if (Mix_PlayingMusic()) + Mix_HaltMusic(); + + if (engine.music != NULL) + Mix_FreeMusic(engine.music); + + #if USEPACK + unpack(filename, PAK_MOD); + + char musicFilename[PATH_MAX]; + strcpy(musicFilename, ""); + sprintf(musicFilename, "%smusic.mod", engine.userHomeDirectory); + + engine.music = Mix_LoadMUS(musicFilename); + #else + engine.music = Mix_LoadMUS(filename); + #endif +} + +void playRandomTrack() +{ + if ((!currentGame.useMusic) || (!engine.useAudio)) + return; + + int tracks = 0; + + char track[][30] = { + "music/Frantic.mod", "music/Artificial.mod", "music/Lunatic.mod", "music/ToxicFriend.mod", + "music/DigitalInferno.mod", "music/TempoTrance.mod", "music/IntoTheMachine.mod" + }; + + switch(currentGame.system) + { + case 0: + tracks = 3; + break; + case 1: + tracks = 5; + break; + case 2: + case 3: + tracks = 7; + break; + } + + switch(currentGame.area) + { + case 5: + case 11: + case 18: + case 25: + loadMusic("music/HardTranceDub.mod"); + break; + case 26: + loadMusic("music/LoopsAndTings.mod"); + break; + default: + loadMusic(track[rand() % tracks]); + } + + Mix_PlayMusic(engine.music, -1); +} diff --git a/code/audio.h b/code/audio.h new file mode 100644 index 0000000..32defc0 --- /dev/null +++ b/code/audio.h @@ -0,0 +1,36 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" + +extern void unpack(char *file, signed char fileType); +extern void freePackBuffer(); + +extern globalEngineVariables engine; +extern Game currentGame; +extern Mix_Chunk *sound[MAX_SOUNDS]; diff --git a/code/bullets.cpp b/code/bullets.cpp new file mode 100644 index 0000000..af8f1db --- /dev/null +++ b/code/bullets.cpp @@ -0,0 +1,797 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "bullets.h" + +void addBullet(object *theWeapon, object *attacker, int y, int dy) +{ + object *bullet; + signed char imageIndex; + int tempX, tempY, steps; + + bullet = new object; + + if (attacker == &player) + currentGame.shots++; + + bullet->next = NULL; + bullet->active = 1; + bullet->x = attacker->x - ((attacker->image[0]->w / 2) * attacker->face); + bullet->y = attacker->y + y; + bullet->flags = theWeapon->flags; + bullet->shield = 300; // bullets live for (approximately) 5 seconds + + // Timed explosions live between 1 and 3 seconds + if (bullet->flags & WF_TIMEDEXPLOSION) + bullet->shield = 60 + ((rand() % 3) * 60); + + if (attacker->face == 0) + { + bullet->dx = theWeapon->speed; + if ((currentGame.area == 18) || (currentGame.area == 24)) + bullet->dx += fabs(engine.ssx); + } + else + { + bullet->dx = (0 - theWeapon->speed); + } + + if (bullet->flags & WF_VARIABLE_SPEED) + { + bullet->dx = Math::rrand(100, 200); + bullet->dx /= 10; + if (attacker->face == 1) + bullet->dx = 0 - bullet->dx; + } + + bullet->dy = dy; + + if (bullet->flags & WF_SCATTER) + { + bullet->dy = Math::rrand(-200, 200); + if (bullet->dy != 0) + bullet->dy /= 200; + } + + if (attacker->flags & FL_WEAPCO) + bullet->flags += WF_WEAPCO; + else + bullet->flags += WF_FRIEND; + + bullet->owner = attacker->owner; + + bullet->id = theWeapon->id; + + bullet->damage = theWeapon->damage; + + if (bullet->id == WT_CHARGER) + { + bullet->damage = attacker->ammo[1]; + if (bullet->damage < 50) + { + bullet->damage = 1; + bullet->id = WT_PLASMA; + } + } + + bullet->target = NULL; + + if (attacker->flags & FL_FRIEND) + imageIndex = 0; + else + imageIndex = 1; + + // Use the enemy's images if applicable + if (bullet->id != WT_ROCKET) + bullet->image[0] = theWeapon->image[imageIndex]; + else + bullet->image[0] = theWeapon->image[attacker->face]; + + if (bullet->flags & WF_AIMED) + { + tempX = (int)fabs(attacker->target->x - attacker->x); + tempY = (int)fabs(attacker->target->y - attacker->y); + steps = max(tempX, tempY); + + if (steps == 0) + steps = 12; + + if (!(bullet->flags & WF_TIMEDEXPLOSION)) + steps /= 8; + else + steps /= 6 + (rand() % 6); + + tempX = (int)(attacker->target->x - attacker->x); + tempY = (int)(attacker->target->y - attacker->y); + + bullet->dx = tempX / steps; + bullet->dy = tempY / steps; + } + + if (attacker->classDef == CD_ASTEROID) + { + bullet->dx = Math::rrand(-20, 20); + bullet->dy = Math::rrand(-20, 20); + bullet->image[0] = graphics.shape[4]; + } + + engine.bulletTail->next = bullet; + engine.bulletTail = bullet; +} + +/* +Fill in later... +*/ +void fireBullet(object *attacker, int weaponType) +{ + if (attacker->reload[weaponType] > 0) + return; + + int y = (attacker->image[0]->h) / 5; + + // Remove some ammo from the player + if ((attacker == &player) && (weaponType == 1) && (!engine.cheatAmmo)) + player.ammo[1]--; + + object *theWeapon = &weapon[attacker->weaponType[weaponType]]; + + switch(theWeapon->id) + { + case WT_PLASMA: + case WT_SPREAD: + case WT_DIRECTIONAL: + playSound(SFX_PLASMA); + break; + case WT_ROCKET: + playSound(SFX_MISSILE); + break; + case WT_LASER: + playSound(SFX_LASER); + break; + case WT_CHARGER: + playSound(SFX_PLASMA3); + break; + } + + if (theWeapon->flags & WF_STRAIGHT) + { + switch (theWeapon->ammo[0]) + { + case 1: + addBullet(theWeapon, attacker, y * 3, 0); + break; + case 2: + addBullet(theWeapon, attacker, y * 2, 0); + addBullet(theWeapon, attacker, y * 4, 0); + break; + case 3: + addBullet(theWeapon, attacker, y * 2, 0); + addBullet(theWeapon, attacker, y * 3, 0); + addBullet(theWeapon, attacker, y * 4, 0); + break; + case 4: + addBullet(theWeapon, attacker, y, 0); + addBullet(theWeapon, attacker, y * 2, 0); + addBullet(theWeapon, attacker, y * 4, 0); + addBullet(theWeapon, attacker, y * 5, 0); + break; + case 5: + for (int i = 1 ; i < 6; i++) + addBullet(theWeapon, attacker, y * i, 0); + break; + } + } + else if (theWeapon->flags & WF_THIN_SPREAD) + { + addBullet(theWeapon, attacker, y * 2, -1); + if (theWeapon->ammo[0] == 3) + { + addBullet(theWeapon, attacker, y * 3, 0); + } + else + { + addBullet(theWeapon, attacker, y * 2, 0); + addBullet(theWeapon, attacker, y * 4, 0); + } + addBullet(theWeapon, attacker, y * 4, 1); + } + else if (theWeapon->flags & WF_WIDE_SPREAD) + { + addBullet(theWeapon, attacker, y * 1, -2); + addBullet(theWeapon, attacker, y * 2, -1); + addBullet(theWeapon, attacker, y * 3, 0); + addBullet(theWeapon, attacker, y * 4, 1); + addBullet(theWeapon, attacker, y * 5, 2); + } + + // Reset the weapon reload time. Double it if it is not friendly or a boss or Kline + attacker->reload[weaponType] = theWeapon->reload[0]; + if ((attacker->flags & FL_WEAPCO) && (attacker != &enemy[WC_BOSS]) && (attacker != &enemy[WC_KLINE]) && (theWeapon->id != W_LASER)) + attacker->reload[weaponType] *= 2; + + if ((engine.cheatAmmo) || (theWeapon->id == WT_LASER)) + return; + + if ((attacker == &player) && (weaponType == 0)) + { + if (player.ammo[0] > 0) + { + player.ammo[0]--; + if (player.ammo[0] == 0) + { + player.weaponType[0] = W_PLAYER_WEAPON; + weapon[W_PLAYER_WEAPON2] = weapon[W_PLAYER_WEAPON]; // reset to weapon 1 defaults + } + } + } +} + +/* +Used for homing missiles. When a missile is active and it is told to home in +on an enemy, it will attempt to randomly grab one every frame if it does not +already have a target. If the target it is currently chasing is killed, it will +begin to look for a new one (done in doBullets()). The homing missile will make +one attempt per call (one call per frame) to find a suitable target. If the target +it picks is dead or outside the screen range, then it returns NULL. A suitable +target will be returned as the object address. +*/ +object *getRandomEnemy(object *bullet) +{ + int i; + + if (bullet->owner->flags & FL_WEAPCO) + { + i = (rand() % 10); + + if (i < 1) + return &player; + } + + i = rand() % MAX_ALIENS; + + if ((enemy[i].shield < 1) || (!enemy[i].active)) + return NULL; + + if ((bullet->owner->flags & FL_WEAPCO) && (enemy[i].flags & FL_WEAPCO)) + return NULL; + + if ((bullet->owner->flags & FL_FRIEND) && (enemy[i].flags & FL_FRIEND)) + return NULL; + + if (abs((int)bullet->x - (int)enemy[i].target->x) > 800) + return NULL; + + if (abs((int)bullet->y - (int)enemy[i].target->y) > 200) + return NULL; + + return &enemy[i]; +} + +/* +Fill in later... +*/ +void destroyAlien(object *bullet, object *theEnemy) +{ + playSound(SFX_EXPLOSION); + + // Chain reaction destruction if needed + if (theEnemy->flags & FL_DAMAGEOWNER) + { + theEnemy->owner->shield -= theEnemy->maxShield; + if (theEnemy->owner->shield < 1) + destroyAlien(bullet, theEnemy->owner); + } + + if (theEnemy->flags & FL_FRIEND) + { + if (theEnemy->classDef == CD_PHOEBE) + currentGame.wingMate1Ejects++; + else if (theEnemy->classDef == CD_URSULA) + currentGame.wingMate2Ejects++; + + // Phoebe cannot eject on the rescue mission + if (currentGame.area != 7) + { + if ((theEnemy->classDef == CD_PHOEBE) || (theEnemy->classDef == CD_URSULA)) + setInfoLine(">> Ally has ejected! <<\n", FONT_RED); + else + setInfoLine(">> Friendly craft has been destroy!! <<\n", FONT_RED); + } + } + + if (bullet->owner == &player) + { + // Once again, stop point leeching + if (currentGame.area != MAX_MISSIONS - 1) + currentGame.cash += theEnemy->score; + currentGame.cashEarned += theEnemy->score; + currentGame.totalKills++; + } + else if (bullet->owner->classDef == CD_PHOEBE) + { + currentGame.wingMate1Kills++; + } + else if (bullet->owner->classDef == CD_URSULA) + { + currentGame.wingMate2Kills++; + } + else + { + currentGame.totalOtherKills++; + } + + if ((bullet->owner->classDef == CD_PHOEBE) || (bullet->owner->classDef == CD_URSULA)) + { + if ((rand() % 8) == 0) + { + getKillMessage(bullet->owner); + } + } + + updateMissionRequirements(M_DESTROY_TARGET_TYPE, theEnemy->classDef, 1); + updateMissionRequirements(M_PROTECT_TARGET, theEnemy->classDef, 1); + + if (rand() % 100 <= theEnemy->collectChance) + { + unsigned char value; + + if ((rand() % 10) == 0) + theEnemy->collectValue *= 2; + + while (theEnemy->collectValue > 0) + { + value = (10 + (rand() % theEnemy->collectValue)); + if (value > theEnemy->collectValue) + value =theEnemy->collectValue; + addCollectable(theEnemy->x, theEnemy->y, theEnemy->collectType, value, 600); + theEnemy->collectValue -= value; + } + } + + // Make it explode immediately + if (theEnemy->classDef == CD_ASTEROID) + { + theEnemy->shield = -999; + if ((currentGame.area == 10) && (theEnemy != &enemy[0]) && (currentMission.completed1[0] == 0) && (currentMission.targetValue1[1] == 1)) + engine.targetArrowTimer = 120; + } + + if ((theEnemy->classDef == CD_KRASS) && (bullet->owner == &player)) + setRadioMessage(FACE_CHRIS, "My NAME is CHRIS!!!!!!!!", 1); + + if (theEnemy->classDef == CD_KLINE) + { + setRadioMessage(FACE_KLINE, "It was an honor... to have fought you...", 1); + theEnemy->dx = theEnemy->dy = 0; + theEnemy->maxShield = 1500; + theEnemy->shield = -200; + } +} + +char checkPlayerShockDamage(float x, float y, int radius) +{ + // Don't let the player be hurt by an explosion after they have completed + // all the mission objectives. That would be *really* annoying! + if ((engine.cheatShield) || (engine.missionCompleteTimer != 0)) + return 0; + + float distX = fabs(x - player.x); + float distY = fabs(y - player.y); + + if ((distX <= 50) && (distY <= 50)) + { + if (distX >= 1) + distX /= 5; + + if (distY >= 1) + distY /= 5; + + player.shield -= (int)(10 - distX); + player.shield -= (int)(10 - distY); + Math::limitInt(&player.shield, 0, player.maxShield); + player.hit = 10; + + return 1; + } + + return 0; +} + +/* +Fill in later... +*/ +void fireRay(object *attacker) +{ + SDL_Rect ray; + + if (attacker->face == 0) + { + ray.x = (int)(attacker->x + attacker->image[0]->w); + } + else + { + ray.x = (int)(attacker->x - 800); + } + ray.y = (int)(attacker->y + attacker->engineY - 1); + ray.h = 3; + ray.w = 800; + + int red = SDL_MapRGB(graphics.screen->format, rand() % 256, 0x00, 0x00); + SDL_FillRect(graphics.screen, &ray, red); + graphics.addBuffer(ray.x, ray.y, ray.w, ray.h); + + if (attacker != &player) + { + if (player.shield > 0) + { + if (Collision::collision(player.x, player.y, player.image[0]->w, player.image[0]->h, ray.x, ray.y, ray.w, ray.h) && (!engine.cheatShield)) + { + if (player.shield > engine.lowShield) + { + if (player.shield - 1 <= engine.lowShield) + { + setInfoLine("!!! WARNING: SHIELD LOW !!!", FONT_RED); + } + } + player.shield--; + + addExplosion(player.x, player.y, E_SMALL_EXPLOSION); + playSound(SFX_HIT); + if (player.shield < 1) + { + playSound(SFX_DEATH); + playSound(SFX_EXPLOSION); + } + } + } + } + + object *anEnemy = enemy; + + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + if (anEnemy->flags & FL_IMMORTAL) + continue; + + if ((anEnemy->shield > 0) && (attacker != anEnemy) && (attacker->classDef != anEnemy->classDef)) + { + if (Collision::collision(anEnemy->x, anEnemy->y, anEnemy->image[0]->w, anEnemy->image[0]->h, ray.x, ray.y, ray.w, ray.h)) + { + anEnemy->shield--; + addExplosion(anEnemy->x, anEnemy->y, E_SMALL_EXPLOSION); + playSound(SFX_HIT); + if (anEnemy->shield < 1) + { + destroyAlien(attacker, anEnemy); + } + } + } + + *anEnemy++; + } + + attacker->ammo[0]--; + if (attacker->ammo[0] < 1) + attacker->flags -= FL_FIRERAY; +} + +/* +This handles active bullets in a linked list. The current bullet and +previous bullet pointers are first assigned to the main header bullet +and each successive bullet is pulled out. Bullets are moved in their +delta coordinates, with rockets having fire trails added to them. Seperate +collision checks are done for a bullet that belongs to the enemy and one +that belongs to a player. However rockets can hit anyone. Upon an enemy +being killed, mission requirements are checked and collectables are randomly +spawned. +*/ +void doBullets() +{ + object *bullet = engine.bulletHead; + object *prevBullet = engine.bulletHead; + engine.bulletTail = engine.bulletHead; + + object *theEnemy, *theCargo; + + signed char okayToHit = 0; + float homingMissileSpeed = 0; + + while (bullet->next != NULL) + { + bullet = bullet->next; + + if (bullet->active == 1) + { + if (bullet->flags & WF_HOMING) + { + if (bullet->target == NULL) + bullet->target = getRandomEnemy(bullet); + + if (bullet->owner->flags & FL_FRIEND) + homingMissileSpeed = 0.25; + else + homingMissileSpeed = 0.05; + } + + if (bullet->id == WT_ROCKET) + { + addExplosion(bullet->x, bullet->y, E_SMALL_EXPLOSION); + } + else if (bullet->id == WT_MICROROCKET) + { + addExplosion(bullet->x, bullet->y, E_TINY_EXPLOSION); + } + + if ((bullet->flags & WF_AIMED) || (bullet->flags & WF_THIN_SPREAD)) + { + graphics.blit(bullet->image[0], (int)(bullet->x - bullet->dx), (int)(bullet->y - bullet->dy)); + } + + if (bullet->id == WT_CHARGER) + { + for (int i = 0 ; i < bullet->damage ; i++) + graphics.blit(bullet->image[0], (int)(bullet->x - Math::rrand(-(bullet->damage / 3), 0)), (int)(bullet->y + Math::rrand(-3, 3))); + } + + graphics.blit(bullet->image[0], (int)bullet->x, (int)bullet->y); + bullet->x += bullet->dx; + bullet->y += bullet->dy; + + if (bullet->target != NULL) + { + if (bullet->x < bullet->target->x) + Math::limitFloat(&(bullet->dx += homingMissileSpeed), -15, 15); + if (bullet->x > bullet->target->x) + Math::limitFloat(&(bullet->dx -= homingMissileSpeed), -15, 15); + + //Rocket is (more or less) inline with target. Fly straight + if ((bullet->x > bullet->target->x - 1) && (bullet->x < bullet->target->x + 5)) + bullet->dx = 0; + + if (bullet->y < bullet->target->y) + Math::limitFloat(&(bullet->dy += homingMissileSpeed), -15, 15); + if (bullet->y > bullet->target->y) + Math::limitFloat(&(bullet->dy -= homingMissileSpeed), -15, 15); + + //Rocket is (more or less) inline with target. Fly straight + if ((bullet->y > bullet->target->y - 1) && (bullet->y < bullet->target->y + 5)) + bullet->dy = 0; + + if ((bullet->target->shield < 1) || (bullet->target->flags & FL_ISCLOAKED)) + bullet->target = NULL; + } + + bullet->x += engine.ssx; + bullet->y += engine.ssy; + + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + theEnemy = &enemy[i]; + + if ((theEnemy->shield < 1) || (!theEnemy->active)) + continue; + + okayToHit = 0; + + if ((bullet->flags & WF_FRIEND) && (theEnemy->flags & FL_WEAPCO)) + okayToHit = 1; + if ((bullet->flags & WF_WEAPCO) && (theEnemy->flags & FL_FRIEND)) + okayToHit = 1; + if ((bullet->id == WT_ROCKET) || (bullet->id == WT_LASER) || (bullet->id == WT_CHARGER)) + okayToHit = 1; + + if (bullet->owner == theEnemy->owner) + okayToHit = 0; + + if (okayToHit) + { + if ((bullet->active == 1) && (Collision::collision(bullet, theEnemy))) + { + if (bullet->owner == &player) + { + currentGame.hits++; + if ((theEnemy->classDef == CD_PHOEBE) || (theEnemy->classDef == CD_URSULA)) + getMissFireMessage(theEnemy); + } + + if (!(theEnemy->flags & FL_IMMORTAL)) + { + if (!(bullet->flags & WF_DISABLE)) + theEnemy->shield -= bullet->damage; + else + theEnemy->systemPower -= bullet->damage; + + theEnemy->hit = 5; + } + + if (theEnemy->flags & FL_CANNOTDIE) + { + Math::limitInt(&theEnemy->shield, 1, theEnemy->maxShield); + if (theEnemy->shield == 1) + { + if (currentGame.area != 26) + { + if (!(theEnemy->flags & FL_LEAVESECTOR)) + { + theEnemy->flags += FL_LEAVESECTOR; + if (theEnemy->flags & FL_CIRCLES) + theEnemy->flags -= FL_CIRCLES; + if (currentGame.area == 11) + setRadioMessage(FACE_KLINE, "Seems I underestimated you, Bainfield! We'll meet again!", 1); + else if (currentGame.area == 25) + setRadioMessage(FACE_SID, "Chris, Kethlan is getting away!", 1); + } + } + else + { + setKlineAttackMethod(theEnemy); + } + } + } + + if ((theEnemy->flags & FL_RUNSAWAY) && ((rand() % 50) == 0)) + { + if (!(theEnemy->flags & FL_LEAVESECTOR)) + theEnemy->flags += FL_LEAVESECTOR; + } + + if (bullet->id != WT_CHARGER) + { + bullet->active = 0; + bullet->shield = 0; + } + else if (bullet->id == WT_CHARGER) + { + bullet->shield -= theEnemy->shield; + if (bullet->shield < 0) + bullet->active = 0; + } + + playSound(SFX_HIT); + if (theEnemy->AIType == AI_EVASIVE) + theEnemy->thinktime = 0; + + if (theEnemy->shield < 1) + destroyAlien(bullet, theEnemy); + + if (theEnemy->systemPower < 1) + { + if (!(theEnemy->flags & FL_DISABLED)) + { + theEnemy->flags += FL_DISABLED; + updateMissionRequirements(M_DISABLE_TARGET, theEnemy->classDef, 1); + } + + theEnemy->systemPower = 0; + if (theEnemy->classDef == CD_KLINE) + theEnemy->systemPower = theEnemy->maxShield; + } + + if (bullet->id == WT_ROCKET) + addExplosion(bullet->x, bullet->y, E_BIG_EXPLOSION); + else + addExplosion(bullet->x, bullet->y, E_SMALL_EXPLOSION); + } + } + } + + // Check for bullets hitting player + if ((bullet->flags & WF_WEAPCO) || (bullet->id == WT_ROCKET) || (bullet->id == WT_LASER) || (bullet->id == WT_CHARGER)) + { + if ((bullet->active == 1) && (player.shield > 0) && (Collision::collision(bullet, &player)) && (bullet->owner != &player)) + { + if ((!engine.cheatShield) || (engine.missionCompleteTimer != 0)) + { + if (player.shield > engine.lowShield) + { + if (player.shield - bullet->damage <= engine.lowShield) + { + setInfoLine("!!! WARNING: SHIELD LOW !!!", FONT_RED); + } + } + player.shield -= bullet->damage; + Math::limitInt(&player.shield, 0, player.maxShield); + player.hit = 5; + } + + if ((bullet->owner->classDef == CD_PHOEBE) || (bullet->owner->classDef == CD_URSULA)) + getPlayerHitMessage(bullet->owner); + + if (bullet->id != WT_CHARGER) + { + bullet->active = 0; + bullet->shield = 0; + } + else if (bullet->id == WT_CHARGER) + { + bullet->shield -= theEnemy->shield; + if (bullet->shield < 0) + bullet->active = 0; + } + + playSound(SFX_HIT); + + if (bullet->id == WT_ROCKET) + addExplosion(bullet->x, bullet->y, E_BIG_EXPLOSION); + else + addExplosion(bullet->x, bullet->y, E_SMALL_EXPLOSION); + } + } + } + + if (((bullet->owner == &player)) || (bullet->id == WT_ROCKET)) + { + for (int j = 0 ; j < 20 ; j++) + { + theCargo = &cargo[j]; + if (theCargo->active) + { + if (Collision::collision(bullet, theCargo)) + { + bullet->active = 0; + addExplosion(bullet->x, bullet->y, E_SMALL_EXPLOSION); + playSound(SFX_HIT); + if (theCargo->collectType != P_PHOEBE) + { + theCargo->active = 0; + playSound(SFX_EXPLOSION); + for (int i = 0 ; i < 10 ; i++) + addExplosion(theCargo->x + Math::rrand(-15, 15), theCargo->y + Math::rrand(-15, 15), E_BIG_EXPLOSION); + updateMissionRequirements(M_PROTECT_PICKUP, P_CARGO, 1); + } + } + } + } + } + + // check to see if a bullet (on any side) hits a mine + checkMineBulletCollisions(bullet); + + bullet->shield--; + + if (bullet->shield < 1) + { + if ((bullet->flags & WF_TIMEDEXPLOSION) || (bullet->id == WT_CHARGER)) + { + playSound(SFX_EXPLOSION); + for (int i = 0 ; i < 10 ; i++) + addExplosion(bullet->x + Math::rrand(-35, 35), bullet->y + Math::rrand(-35, 35), E_BIG_EXPLOSION); + + if (bullet->flags & WF_TIMEDEXPLOSION) + if (checkPlayerShockDamage(bullet->x, bullet->y, 75)) + setInfoLine("Warning: Missile Shockwave Damage!!", FONT_RED); + } + bullet->active = 0; + } + + if (bullet->active == 1) + { + prevBullet = bullet; + engine.bulletTail = bullet; + } + else + { + prevBullet->next = bullet->next; + delete bullet; + bullet = prevBullet; + } + } +} + diff --git a/code/bullets.h b/code/bullets.h new file mode 100644 index 0000000..1ae2b49 --- /dev/null +++ b/code/bullets.h @@ -0,0 +1,54 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void playSound(int sid); +extern void updateMissionRequirements(int type, int id, int value); +extern void addCollectable(float x, float y, int type, int value, int life); +extern void addExplosion(float x, float y, int type); +extern void generateShockWave(object *bullet); +extern void setInfoLine(char *in, int color); +extern void getKillMessage(object *ally); +extern void getMissFireMessage(object *ally); +extern void getPlayerHitMessage(object *ally); +extern void checkMineBulletCollisions(object *bullet); +extern void setKlineAttackMethod(object *theEnemy); +extern void setRadioMessage(signed char face, char *in, int priority); + +extern globalEngineVariables engine; +extern devVariables dev; +extern object weapon[MAX_WEAPONS]; +extern object player; +extern mission currentMission; +extern object enemy[MAX_ALIENS]; +extern object cargo[20]; +extern Game currentGame; +extern Graphics graphics; diff --git a/code/cargo.cpp b/code/cargo.cpp new file mode 100644 index 0000000..77b556f --- /dev/null +++ b/code/cargo.cpp @@ -0,0 +1,120 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "cargo.h" + +void initCargo() +{ + for (int i = 0 ; i < MAX_CARGO ; i++) + { + cargo[i].active = 0; + cargo[i].owner = NULL; + } +} + +/* +* I think you all know what this does by now! ;) +*/ +int getCargo() +{ + for (int i = 0 ; i < MAX_CARGO ; i++) + { + if (cargo[i].active == 0) + return i; + } + + return -1; +} + +object *addCargo(object *owner, int cargoType) +{ + int index = getCargo(); + + if (index == -1) + return NULL; + + cargo[index].active = 1; + cargo[index].owner = owner; + cargo[index].x = owner->x; + cargo[index].y = owner->y; + cargo[index].dx = 0; + cargo[index].dy = 0; + cargo[index].collectType = cargoType; + cargo[index].image[0] = graphics.shape[32]; + if (cargoType == P_PHOEBE) + cargo[index].image[0] = graphics.shipShape[20]; + + return &cargo[index]; +} + +void becomeCollectable(int i) +{ + if (cargo[i].collectType != P_PHOEBE) + { + addCollectable(cargo[i].x, cargo[i].y, cargo[i].collectType, 1, 600); + } + else + { + enemy[FR_PHOEBE].active = 1; + enemy[FR_PHOEBE].x = cargo[i].x; + enemy[FR_PHOEBE].y = cargo[i].y; + setRadioMessage(FACE_PHOEBE, "Thanks!! Watch out, WEAPCO! Phoebe's loose and she's ANGRY!!!", 1); + } + + cargo[i].active = 0; +} + +void doCargo() +{ + float dx, dy, chainX, chainY; + + for (int i = 0 ; i < MAX_CARGO ; i++) + { + if (cargo[i].active) + { + if (!cargo[i].owner->active) + { + becomeCollectable(i); + continue; + } + + graphics.blit(cargo[i].image[0], (int)cargo[i].x, (int)cargo[i].y); + + cargo[i].x += engine.ssx; + cargo[i].y += engine.ssy; + + Math::limitFloat(&cargo[i].x, cargo[i].owner->x - 50, cargo[i].owner->x + 50); + Math::limitFloat(&cargo[i].y, cargo[i].owner->y - 50, cargo[i].owner->y + 50); + + dx = (cargo[i].x - cargo[i].owner->x) / 10; + dy = (cargo[i].y - cargo[i].owner->y) / 10; + chainX = cargo[i].x - cargo[i].dx; + chainY = cargo[i].y - cargo[i].dy; + + // draw the chain link line + for (int j = 0 ; j < 10 ; j++) + { + graphics.blit(graphics.shape[30], (int)chainX, (int)chainY); + chainX -= dx; + chainY -= dy; + } + } + } +} diff --git a/code/cargo.h b/code/cargo.h new file mode 100644 index 0000000..a22ef1d --- /dev/null +++ b/code/cargo.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void addCollectable(float x, float y, int type, int value, int life); +extern void setRadioMessage(signed char face, char *in, int priority); + +extern globalEngineVariables engine; +extern Game currentGame; +extern Graphics graphics; +extern object enemy[MAX_ALIENS]; +extern object player; +extern object cargo[MAX_CARGO]; diff --git a/code/classes.h b/code/classes.h new file mode 100644 index 0000000..821a805 --- /dev/null +++ b/code/classes.h @@ -0,0 +1,710 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +extern void showErrorAndExit(int errorId, char *name); + +class Collision { + + private: + + Collision(){} + + public: + + static signed char collision(float x0, float y0, int w0, int h0, float x2, float y2, int w1, int h1) + { + float x1 = x0 + w0; + float y1 = y0 + h0; + + float x3 = x2 + w1; + float y3 = y2 + h1; + + return !(x1x; + float y0 = object1->y; + float w0 = object1->image[0]->w; + float h0 = object1->image[0]->h; + + float x2 = object2->x; + float y2 = object2->y; + float w1 = object2->image[0]->w; + float h1 = object2->image[0]->h; + + float x1 = x0 + w0; + float y1 = y0 + h0; + + float x3 = x2 + w1; + float y3 = y2 + h1; + + return !(x1x; + float y0 = object1->y; + float w0 = object1->image->w; + float h0 = object1->image->h; + + float x2 = object2->x; + float y2 = object2->y; + float w1 = object2->image[0]->w; + float h1 = object2->image[0]->h; + + float x1 = x0 + w0; + float y1 = y0 + h0; + + float x3 = x2 + w1; + float y3 = y2 + h1; + + return !(x1 high) + *in = high; + } + + static void limitChar(unsigned char *in, int low, int high) + { + if (*in < low) + *in = low; + if (*in > high) + *in = high; + } + + static void limitInt(int *in, int low, int high) + { + if (*in < low) + *in = low; + if (*in > high) + *in = high; + } + + static void limitFloat(float *in, float low, float high) + { + if (*in < low) + *in = low; + if (*in > high) + *in = high; + } + + static void wrapChar(signed char *in, signed char low, signed char high) + { + if (*in < low) + *in = high; + if (*in > high) + *in = low; + } + + static void wrapInt(int *in, int low, int high) + { + if (*in < low) + *in = high; + if (*in > high) + *in = low; + } + + static void wrapFloat(float *in, float low, float high) + { + if (*in < low) + *in = high; + if (*in > high) + *in = low; + } + + static int rrand(int min, int max) + { + int r = min; + + max++; + + if ((max - min) == 0) + return min; + + r += rand() % (max - min); + + return r; + } + +}; + + +class Graphics { + + public: + + Uint32 red; + Uint32 darkRed; + Uint32 yellow; + Uint32 darkYellow; + Uint32 green; + Uint32 darkGreen; + Uint32 blue; + Uint32 darkBlue; + Uint32 darkerBlue; + Uint32 black; + Uint32 white; + Uint32 lightGrey; + Uint32 darkGrey; + SDL_Surface *screen, *background; + SDL_Surface *shape[MAX_SHAPES]; + SDL_Surface *shipShape[MAX_SHIPSHAPES]; + SDL_Surface *fontShape[MAX_FONTSHAPES]; + SDL_Surface *shopSurface[MAX_SHOPSHAPES]; + bRect *bufferHead; + bRect *bufferTail; + textObject textShape[MAX_TEXTSHAPES]; + SDL_Rect blitRect; + SDL_Surface *messageBox; + + Graphics() + { + bufferHead = new bRect; + bufferHead->next = NULL; + bufferTail = bufferHead; + + for (int i = 0 ; i < MAX_SHAPES ; i++) + shape[i] = NULL; + + for (int i = 0 ; i < MAX_SHIPSHAPES ; i++) + shipShape[i] = NULL; + + for (int i = 0 ; i < MAX_TEXTSHAPES ; i++) + textShape[i].image = NULL; + + for (int i = 0 ; i < MAX_SHOPSHAPES ; i++) + shopSurface[i] = NULL; + + background = NULL; + messageBox = NULL; + } + + SDL_Surface *setTransparent(SDL_Surface *sprite) + { + SDL_SetColorKey(sprite, (SDL_SRCCOLORKEY|SDL_RLEACCEL), SDL_MapRGB(sprite->format, 0, 0, 0)); + return sprite; + } + + void addBuffer(int x, int y, int w, int h) + { + bRect *rect = new bRect; + + rect->next = NULL; + rect->x = x; + rect->y = y; + rect->w = w; + rect->h = h; + + bufferTail->next = rect; + bufferTail = rect; + } + + void blit(SDL_Surface *image, int x, int y, SDL_Surface *dest) + { + // Set up a rectangle to draw to + blitRect.x = x; + blitRect.y = y; + blitRect.w = image->w; + blitRect.h = image->h; + + /* Blit onto the screen surface */ + if(SDL_BlitSurface(image, NULL, dest, &blitRect) < 0) + { + printf("BlitSurface error: %s\n", SDL_GetError()); + showErrorAndExit(2, ""); + } + + addBuffer(blitRect.x, blitRect.y, blitRect.w, blitRect.h); + } + + void blit(SDL_Surface *image, int x, int y) + { + blit(image, x, y, screen); + } + + void blitText(int i) + { + blit(textShape[i].image, (int)textShape[i].x, (int)textShape[i].y, screen); + } + + void flushBuffer() + { + bRect *prevRect = bufferHead; + bRect *rect = bufferHead; + bufferTail = bufferHead; + + while (rect->next != NULL) + { + rect = rect->next; + + prevRect->next = rect->next; + delete rect; + rect = prevRect; + } + + bufferHead->next = NULL; + } + + void unBuffer() + { + bRect *prevRect = bufferHead; + bRect *rect = bufferHead; + bufferTail = bufferHead; + + while (rect->next != NULL) + { + rect = rect->next; + + blitRect.x = rect->x; + blitRect.y = rect->y; + blitRect.w = rect->w; + blitRect.h = rect->h; + + if (SDL_BlitSurface(background, &blitRect, screen, &blitRect) < 0) + { + printf("BlitSurface error: %s\n", SDL_GetError()); + showErrorAndExit(2, ""); + } + + prevRect->next = rect->next; + delete rect; + rect = prevRect; + } + + bufferHead->next = NULL; + } + + /* + In 16 bit mode this is slow. VERY slow. Don't write directly to a surface + that constantly needs updating (eg - the main game screen) + */ + int renderString(char *in, int x, int y, int fontColor, signed char wrap, SDL_Surface *dest) + { + SDL_Rect area; + area.x = x; + area.y = y; + area.w = 8; + area.h = 14; + + SDL_Rect letter; + letter.y = 0; + letter.w = 8; + letter.h = 14; + + while (*in != '\0') + { + if (*in != 32) + { + letter.x = (*in - 33); + letter.x *= 8; + letter.x--; // Temp fix + + /* Blit onto the screen surface */ + if(SDL_BlitSurface(fontShape[fontColor], &letter, dest, &area) < 0) + { + printf("BlitSurface error: %s\n", SDL_GetError()); + showErrorAndExit(2, ""); + } + } + + area.x += 9; + + if (wrap) + { + if ((area.x > (dest->w - 70)) && (*in == 32)) + { + area.y += 16; + area.x = x; + } + } + + *in++; + } + + return area.y; + } + + int drawString(char *in, int x, int y, int fontColor, signed char wrap, SDL_Surface *dest) + { + renderString(in, x, y - 1, FONT_OUTLINE, wrap, dest); + renderString(in, x, y + 1, FONT_OUTLINE, wrap, dest); + renderString(in, x, y + 2, FONT_OUTLINE, wrap, dest); + renderString(in, x - 1, y, FONT_OUTLINE, wrap, dest); + renderString(in, x - 2, y, FONT_OUTLINE, wrap, dest); + renderString(in, x + 1, y, FONT_OUTLINE, wrap, dest); + return renderString(in, x, y, fontColor, wrap, dest); + } + + int drawString(char *in, int x, int y, int fontColor, SDL_Surface *dest) + { + if (x == -1) + x = (dest->w - (strlen(in) * 9)) / 2; + return drawString(in, x, y, fontColor, 0, dest); + } + + int drawString(char *in, int x, int y, int fontColor) + { + if (x == -1) + x = (800 - (strlen(in) * 9)) / 2; + return drawString(in, x, y, fontColor, 0, screen); + } + + /* + Finds the location of the requested color within the palette and returns + it's number. This colors are used for drawing rectangles, circle, etc in + the correct colors. + */ + void setColorIndexes() + { + red = SDL_MapRGB(screen->format, 0xff, 0x00, 0x00); + darkRed = SDL_MapRGB(screen->format, 0x66, 0x00, 0x00); + + yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0x00); + darkYellow = SDL_MapRGB(screen->format, 0x66, 0x66, 0x00); + + green = SDL_MapRGB(screen->format, 0x00, 0xff, 0x00); + darkGreen = SDL_MapRGB(screen->format, 0x00, 0x66, 0x00); + + blue = SDL_MapRGB(screen->format, 0x00, 0x00, 0xff); + darkBlue = SDL_MapRGB(screen->format, 0x00, 0x00, 0x99); + darkerBlue = SDL_MapRGB(screen->format, 0x00, 0x00, 0x44); + + black = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00); + white = SDL_MapRGB(screen->format, 0xff, 0xff, 0xff); + lightGrey = SDL_MapRGB(screen->format, 0xcc, 0xcc, 0xcc); + darkGrey = SDL_MapRGB(screen->format, 0x99, 0x99, 0x99); + } + + /* + Draws the background surface that has been loaded + */ + void drawBackGround() + { + blit(background, 0, 0, screen); + } + + void clearScreen(Uint32 color) + { + SDL_FillRect(screen, NULL, color); + } + + void updateScreen() + { + SDL_Flip(screen); + // Give the audio (and possibly the X server) time to work... + SDL_Delay(1); + } + + /* + * Set the pixel at (x, y) to the given value + * NOTE: The surface must be locked before calling this! + */ + void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel) + { + int bpp = surface->format->BytesPerPixel; + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) { + case 1: + *p = pixel; + break; + + case 2: + *(Uint16 *)p = pixel; + break; + + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; + } else { + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; + } + break; + + case 4: + *(Uint32 *)p = pixel; + break; + } + } + + void drawLine(SDL_Surface *dest, int x1, int y1, int x2, int y2, int col) + { + int counter = 0; + + if ( SDL_MUSTLOCK(dest) ) { + if ( SDL_LockSurface(dest) < 0 ) { + printf("Can't lock screen: %s\n", SDL_GetError()); + showErrorAndExit(2, ""); + } + } + + while(1) + { + putpixel(dest, x1, y1, col); + + if (x1 > x2) x1--; + if (x1 < x2) x1++; + if (y1 > y2) y1--; + if (y1 < y2) y1++; + + if ((x1 == x2) && (y1 == y2)) + {break;} + if (counter == 1000) + {printf("Loop Error!\n"); break;} + counter++; + } + + if ( SDL_MUSTLOCK(dest) ) { + SDL_UnlockSurface(dest); + } + } + + void drawLine(int x1, int y1, int x2, int y2, int col) + { + drawLine(screen, x1, y1, x2, y2, col); + } + + /* + A quick(?) circle draw function. This code was posted to the SDL + mailing list... I didn't write it myself. + */ + void circle(int xc, int yc, int R, SDL_Surface *PIX, int col) + { + int x = 0, xx = 0; + int y = R, yy = R+R; + int p = 1-R; + + putpixel(PIX, xc, yc - y, col); + putpixel(PIX, xc, yc + y, col); + putpixel(PIX, xc - y, yc, col); + putpixel(PIX, xc + y, yc, col); + + while(x < y) + { + xx += 2; + ++x; + if (p >= 0) + { + yy -= 2; + --y; + p -= yy; + } + p += xx + 1; + + putpixel(PIX, xc - x, yc - y, col); + putpixel(PIX, xc + x, yc - y, col); + putpixel(PIX, xc - x, yc + y, col); + putpixel(PIX, xc + x, yc + y, col); + putpixel(PIX, xc - y, yc - x, col); + putpixel(PIX, xc + y, yc - x, col); + putpixel(PIX, xc - y, yc + x, col); + putpixel(PIX, xc + y, yc + x, col); + } + + if ((x = y)) + { + putpixel(PIX, xc - x, yc - y, col); + putpixel(PIX, xc + x, yc - y, col); + putpixel(PIX, xc - x, yc + y, col); + putpixel(PIX, xc + x, yc + y, col); + } + } + + void blevelRect(SDL_Surface *dest, int x, int y, int w, int h, Uint8 red, Uint8 green, Uint8 blue) + { + SDL_Rect r = {x, y, w, h}; + SDL_FillRect(dest, &r, SDL_MapRGB(screen->format, red, green, blue)); + + drawLine(dest, x, y, x + w, y, SDL_MapRGB(screen->format, 255, 255, 255)); + drawLine(dest, x, y, x, y + h, SDL_MapRGB(screen->format, 255, 255, 255)); + drawLine(dest, x, y + h, x + w, y + h, SDL_MapRGB(screen->format, 128, 128, 128)); + drawLine(dest, x + w, y + 1, x + w, y + h, SDL_MapRGB(screen->format, 128, 128, 128)); + } + + void blevelRect(int x, int y, int w, int h, Uint8 red, Uint8 green, Uint8 blue) + { + blevelRect(screen, x, y, w, h, red, green, blue); + } + + SDL_Surface *createSurface(int width, int height) + { + SDL_Surface *surface, *newImage; + Uint32 rmask, gmask, bmask, amask; + + /* SDL interprets each pixel as a 32-bit number, so our masks must depend + on the endianness (byte order) of the machine */ + #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; + #else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; + #endif + + surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, rmask, gmask, bmask, amask); + + if (surface == NULL) { + printf("CreateRGBSurface failed: %s\n", SDL_GetError()); + showErrorAndExit(2, ""); + } + + newImage = SDL_DisplayFormat(surface); + + SDL_FreeSurface(surface); + + return newImage; + } + + SDL_Surface *textSurface(char *inString, int color) + { + SDL_Surface *surface = createSurface(strlen(inString) * 9, 16); + + drawString(inString, 1, 1, color, surface); + + return setTransparent(surface); + } + + void textSurface(int index, char *inString, int x, int y, int fontColor) + { + strcpy(textShape[index].text, inString); + textShape[index].x = x; + textShape[index].y = y; + textShape[index].fontColor = fontColor; + if (textShape[index].image != NULL) + { + SDL_FreeSurface(textShape[index].image); + } + textShape[index].image = textSurface(inString, fontColor); + if (x == -1) + textShape[index].x = (800 - textShape[index].image->w) / 2; + } + + SDL_Surface *alphaRect(int width, int height, Uint8 red, Uint8 green, Uint8 blue) + { + SDL_Surface *surface = createSurface(width, height); + + SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, red, green, blue)); + + SDL_SetAlpha(surface, SDL_SRCALPHA|SDL_RLEACCEL, 128); + + return surface; + } + + void createMessageBox(SDL_Surface *face, char *message, signed char transparent) + { + if (messageBox != NULL) + { + SDL_FreeSurface(messageBox); + messageBox = NULL; + } + + if (transparent) + messageBox = alphaRect(550, 60, 0x00, 0x00, 0x00); + else + messageBox = createSurface(550, 60); + + signed char x = 60; + + if (face != NULL) + { + blevelRect(messageBox, 0, 0, messageBox->w - 1, messageBox->h - 1, 0x00, 0x00, 0xaa); + blit(face, 5, 5, messageBox); + } + else + { + blevelRect(messageBox, 0, 0, messageBox->w - 1, messageBox->h - 1, 0x00, 0x00, 0x00); + x = 10; + } + + drawString(message, x, 5, FONT_WHITE, 1, messageBox); + } + + void freeGraphics() + { + for (int i = 0 ; i < MAX_SHAPES ; i++) + { + if (shape[i] != NULL) + { + SDL_FreeSurface(shape[i]); + shape[i] = NULL; + } + } + + for (int i = 0 ; i < MAX_SHIPSHAPES ; i++) + { + if (shipShape[i] != NULL) + { + SDL_FreeSurface(shipShape[i]); + shipShape[i] = NULL; + } + } + + for (int i = 0 ; i < MAX_TEXTSHAPES ; i++) + { + if (textShape[i].image != NULL) + { + SDL_FreeSurface(textShape[i].image); + textShape[i].image = NULL; + } + } + + for (int i = 0 ; i < MAX_SHOPSHAPES ; i++) + { + if (shopSurface[i] != NULL) + { + SDL_FreeSurface(shopSurface[i]); + shopSurface[i] = NULL; + } + } + + if (messageBox != NULL) + { + SDL_FreeSurface(messageBox); + messageBox = NULL; + } + } +}; diff --git a/code/collectable.cpp b/code/collectable.cpp new file mode 100644 index 0000000..6f43993 --- /dev/null +++ b/code/collectable.cpp @@ -0,0 +1,430 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "collectable.h" + +/* +Create a new collectable item based on supplied arguments. +*/ +void addCollectable(float x, float y, int type, int value, int life) +{ + if (type == P_ANYTHING) + { + type = P_CASH; + + int r = rand() % 9; + + switch (r) + { + case 0: + type = P_PLASMA_AMMO; + break; + case 1: + type = P_SHIELD; + break; + case 2: + type = P_ROCKET; + value /= 10; + break; + } + } + else if (type == P_WEAPONS) + { + type = P_PLASMA_RATE; + + int r = rand() % 61; + + if (r <= 19) + type = P_PLASMA_DAMAGE; + else if (r <= 39) + type = P_PLASMA_SHOT; + else if (r <= 59) + type = P_PLASMA_RATE; + else + type = P_SUPER; + } + + if (type == P_SUPER) + value = 1; + + /* + Cash is rare on interceptions. Stops people from point leeching(!) + */ + if ((currentGame.area == MAX_MISSIONS - 1) && (type == P_CASH)) + { + if (rand() % 10 > 0) + return; + } + + if (value == 0) + return; // don't bother! + + // If the player has a charge cannon or a laser cannon, don't give them + // rockets. Causes problems otherwise :) + if (type == P_ROCKET) + { + if ((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER)) + { + type = P_CASH; + } + } + + collectables *collectable = new collectables; + + collectable->next = NULL; + collectable->active = 1; + collectable->x = x; + collectable->y = y; + + collectable->dx = Math::rrand(-100, 100); + if (collectable->dx != 0) + collectable->dx /= 100; + + collectable->dy = Math::rrand(-100, 100); + if (collectable->dy != 0) + collectable->dy /= 100; + + collectable->type = type; + collectable->value = value; + collectable->life = life; + + switch(type) + { + case P_CASH: + collectable->image = graphics.shape[24]; + break; + + case P_ROCKET: + collectable->image = graphics.shape[49]; + break; + + case P_PLASMA_AMMO: + collectable->image = graphics.shape[25]; + break; + + case P_SHIELD: + collectable->image = graphics.shape[26]; + break; + + case P_PLASMA_SHOT: + collectable->image = graphics.shape[27]; + break; + + case P_PLASMA_RATE: + collectable->image = graphics.shape[28]; + break; + + case P_PLASMA_DAMAGE: + collectable->image = graphics.shape[29]; + break; + + case P_CARGO: + collectable->image = graphics.shape[32]; + break; + + case P_SUPER: + collectable->image = graphics.shape[50]; + break; + + case P_MINE: + collectable->image = graphics.shape[31]; + break; + + case P_SLAVES: + case P_ESCAPEPOD: + collectable->image = graphics.shape[45]; + break; + + case P_ORE: + collectable->image = graphics.shape[46 + rand() % 3]; + break; + } + + engine.collectableTail->next = collectable; + engine.collectableTail = collectable; +} + +void explodeMine(collectables *collectable) +{ + if ((collectable->x >= 0) && (collectable->x <= 800) && (collectable->y >= 0) && (collectable->y <= 600)) + playSound(SFX_EXPLOSION); + + for (int i = 0 ; i < 10 ; i++) + addExplosion(collectable->x + rand() % 25 - rand() % 25, collectable->y + rand() % 25 - rand() % 25, E_BIG_EXPLOSION); + + if (checkPlayerShockDamage(collectable->x, collectable->y, 50)) + setInfoLine("Warning: Mine damage to shield!!", FONT_RED); +} + +void checkMineBulletCollisions(object *bullet) +{ + collectables *collectable = engine.collectableHead; + collectables *prevCollectable = engine.collectableHead; + engine.collectableTail = engine.collectableHead; + + while (collectable->next != NULL) + { + collectable = collectable->next; + + if (collectable->type == P_MINE) + { + if (Collision::collision(collectable, bullet)) + { + collectable->active = 0; + + if (bullet->id != WT_CHARGER) + { + bullet->active = 0; + } + else + { + bullet->shield--; + if (bullet->shield < 0) + bullet->active = 0; + } + + if (bullet->owner == &player) + { + currentGame.minesKilled++; + currentGame.hits++; + } + } + } + + if (collectable->active == 1) + { + prevCollectable = collectable; + engine.collectableTail = collectable; + } + else + { + explodeMine(collectable); + prevCollectable->next = collectable->next; + delete collectable; + collectable = prevCollectable; + } + } +} + +/* +Loops through the currently active collectables (in a linked list). The collectable +will travel in the direction that was defined when it was made. Its life will decreased +whilst it remains active. It will be removed if the player touches it or if its life +reaches 0. When it is picked up, depending on the type of collectable it is, mission requirements +will be updated. Information will be displayed and appropriate player variables altered. +*/ +void doCollectables() +{ + collectables *collectable = engine.collectableHead; + collectables *prevCollectable = engine.collectableHead; + engine.collectableTail = engine.collectableHead; + + while (collectable->next != NULL) + { + collectable = collectable->next; + + if (collectable->active == 1) + { + if ((collectable->x + collectable->image->w > 0) && (collectable->x < 800) && (collectable->y + collectable->image->h > 0) && (collectable->y < 600)) + graphics.blit(collectable->image, (int)collectable->x, (int)collectable->y); + + collectable->x += engine.ssx; + collectable->y += engine.ssy; + collectable->x += collectable->dx; + collectable->y += collectable->dy; + + collectable->life--; + + if ((player.shield > 0) && (Collision::collision(collectable, &player))) + { + char temp[40]; + switch(collectable->type) + { + case P_CASH: + currentGame.cash += collectable->value; + currentGame.cashEarned += collectable->value; + sprintf(temp, "Got $%d ", collectable->value); + break; + + case P_ROCKET: + Math::limitChar(&(player.ammo[1] += collectable->value), 0, currentGame.maxRocketAmmo); + if (player.ammo[1] == currentGame.maxRocketAmmo) + sprintf(temp, "Rocket Ammo at Maximum"); + else + { + if (collectable->value > 1) + sprintf(temp, "Got %d rockets", collectable->value); + else + sprintf(temp, "Got a rocket"); + } + currentGame.rocketPickups += collectable->value; + break; + + case P_SHIELD: + Math::limitInt(&(player.shield += 10), 0, player.maxShield); + currentGame.shieldPickups ++; + sprintf(temp, "Restored 10 shield points"); + break; + + case P_PLASMA_RATE: + Math::limitChar(&(weapon[1].reload[0] -= 2), currentGame.maxPlasmaRate, 15); + player.weaponType[0] = 1; + if (player.ammo[0] < 50) + player.ammo[0] = 50; + Math::limitChar(&(player.ammo[0]), 0, currentGame.maxPlasmaAmmo); + if (weapon[1].reload[0] == currentGame.maxPlasmaRate) + sprintf(temp, "Firing Rate at Maximum"); + else + sprintf(temp, "Firing rate increased"); + currentGame.powerups++; + break; + + case P_PLASMA_SHOT: + Math::limitChar(&(weapon[1].ammo[0] += 1), 1, currentGame.maxPlasmaOutput); + if (player.ammo[0] < 50) + player.ammo[0] = 50; + Math::limitChar(&(player.ammo[0]), 0, currentGame.maxPlasmaAmmo); + if (weapon[1].ammo[0] == currentGame.maxPlasmaOutput) + sprintf(temp, "Plasma output at Maximum"); + else + sprintf(temp, "Plasma output increased"); + player.weaponType[0] = 1; + currentGame.powerups++; + break; + + case P_PLASMA_DAMAGE: + Math::limitChar(&(weapon[1].damage += 1), 1, currentGame.maxPlasmaDamage); + if (player.ammo[0] < 50) + player.ammo[0] = 50; + Math::limitChar(&(player.ammo[0]), 0, currentGame.maxPlasmaAmmo); + if (weapon[1].damage == currentGame.maxPlasmaDamage) + sprintf(temp, "Plasma damage at Maximum"); + else + sprintf(temp, "Plasma damage increased"); + player.weaponType[0] = 1; + currentGame.powerups++; + break; + + case P_SUPER: + weapon[1].ammo[0] = 5; + weapon[1].damage = 5; + weapon[1].reload[0] = 7; + + if (weapon[1].flags & WF_STRAIGHT) + weapon[1].flags -= WF_STRAIGHT; + + if (weapon[1].flags & WF_THIN_SPREAD) + weapon[1].flags -= WF_THIN_SPREAD; + + if (!(weapon[1].flags & WF_WIDE_SPREAD)) + weapon[1].flags += WF_WIDE_SPREAD; + + sprintf(temp, "Picked up a Super Charge!!"); + + if (player.ammo[0] < 50) + player.ammo[0] = 50; + player.weaponType[0] = 1; + currentGame.powerups++; + break; + + case P_PLASMA_AMMO: + Math::limitChar(&(player.ammo[0] += collectable->value), 0, currentGame.maxPlasmaAmmo); + if (player.ammo[0] == currentGame.maxPlasmaAmmo) + sprintf(temp, "Plasma cells at Maximum"); + else + { + if (collectable->value > 1) + { + sprintf(temp, "Got %d plasma cells", collectable->value); + } + else + { + sprintf(temp, "Got a plasma cell"); + if ((rand() % 25) == 0) + sprintf(temp, "Got one whole plasma cell (wahoo!)"); + } + } + player.weaponType[0] = 1; + currentGame.cellPickups += collectable->value; + break; + + case P_CARGO: + strcpy(temp, "Picked up some Cargo"); + currentGame.cargoPickups++; + break; + + case P_SLAVES: + sprintf(temp, "Rescued %d slaves", collectable->value); + currentGame.slavesRescued += collectable->value; + break; + + case P_ESCAPEPOD: + sprintf(temp, "Picked up an Escape Pod"); + break; + + case P_ORE: + sprintf(temp, "Picked up some Ore"); + break; + } + + updateMissionRequirements(M_COLLECT, collectable->type, collectable->value); + + collectable->active = 0; + if (collectable->type != P_MINE) + { + setInfoLine(temp, FONT_WHITE); + if (collectable->type == P_SHIELD) + playSound(SFX_SHIELDUP); + else + playSound(SFX_PICKUP); + } + } + + // stop people from exploiting a weapon check condition + if (player.ammo[0] == 0) + { + player.weaponType[0] = 0; + weapon[1] = weapon[0]; // reset to weapon 1 defaults + } + } + + if (collectable->life < 1) + { + collectable->active = 0; + if ((collectable->type == P_CARGO) || (collectable->type == P_ESCAPEPOD) || (collectable->type == P_SLAVES)) + updateMissionRequirements(M_PROTECT_PICKUP, collectable->type, 1); + } + + if (collectable->active == 1) + { + prevCollectable = collectable; + engine.collectableTail = collectable; + } + else + { + if (collectable->type == P_MINE) + explodeMine(collectable); + prevCollectable->next = collectable->next; + delete collectable; + collectable = prevCollectable; + } + } +} + diff --git a/code/collectable.h b/code/collectable.h new file mode 100644 index 0000000..121e310 --- /dev/null +++ b/code/collectable.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void updateMissionRequirements(int type, int id, int value); +extern void setInfoLine(char *in, int color); +extern object *addCargo(object *owner, int cargoType); +extern void addExplosion(float x, float y, int type); +extern void playSound(int sid); +extern char checkPlayerShockDamage(float x, float y, int radius); + +extern globalEngineVariables engine; +extern object player; +extern Game currentGame; +extern object weapon[MAX_WEAPONS]; +extern Graphics graphics; diff --git a/code/comms.cpp b/code/comms.cpp new file mode 100644 index 0000000..d4432c1 --- /dev/null +++ b/code/comms.cpp @@ -0,0 +1,156 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "comms.h" + +void updateCommsSurface(SDL_Surface *comms) +{ + if (engine.commsSection == 1) + return; + + char string[255]; + + graphics.blevelRect(comms, 0, 10, comms->w - 1, 55, 0x00, 0x22, 0x00); + graphics.blit(graphics.shape[FACE_CHRIS], 20, 15, comms); + graphics.drawString("Chris Bainfield", 80, 15, FONT_WHITE, comms); + sprintf(string, "Current Location: %s", systemPlanet[currentGame.stationedPlanet].name); + graphics.drawString(string, 80, 35, FONT_WHITE, comms); +} + +void createCommsSurface(SDL_Surface *comms) +{ + engine.commsSection = 0; + + graphics.blevelRect(comms, 0, 0, comms->w - 1, comms->h - 1, 0x00, 0x00, 0x25); + + graphics.drawString("+++ RECIEVED MESSAGES +++", 115, 80, FONT_GREEN, comms); + + int yOffset; + + for (int i = 0 ; i < 10 ; i++) + { + if ((systemPlanet[i].messageSlot != -1) && (systemPlanet[i].missionCompleted == 0)) + { + yOffset = systemPlanet[i].messageSlot * 60; + graphics.blevelRect(comms, 0, 105 + yOffset, comms->w - 1, 55, 0x00, 0x00, 0x77); + graphics.blit(graphics.shape[systemPlanet[i].faceImage], 20, 110 + yOffset, comms); + graphics.drawString(systemPlanet[i].from, 80, 110 + yOffset, FONT_WHITE, comms); + graphics.drawString(systemPlanet[i].subject, 80, 130 + yOffset, FONT_CYAN, comms); + graphics.drawString("INCOMPLETE", 350, 110 + yOffset, FONT_RED, comms); + } + } + + updateCommsSurface(comms); +} + +void createMissionDetailSurface(SDL_Surface *comms, int missionSlot) +{ + char name[50]; + char string[2000]; + int lines = 0; + int y = 50; + int newY = y; + int col = FONT_WHITE; + int mission = -1; + int faceNumber = -1; + FILE *fp; + + for (int i = 0 ; i < 10 ; i++) + { + if ((systemPlanet[i].messageSlot == missionSlot) && (systemPlanet[i].missionCompleted == 0)) + { + //printf("Slot %d - Mission %d - Completed %d\n", missionSlot, systemPlanet[i].messageMission, systemPlanet[i].missionCompleted); + mission = systemPlanet[i].messageMission; + } + } + + if (mission == -1) + return; + + graphics.blevelRect(comms, 0, 0, comms->w - 1, comms->h - 1, 0x00, 0x00, 0x25); + + strcpy(string, ""); + sprintf(string, "data/brief%d.txt", mission); + + #if USEPACK + int dataLocation = locateDataInPak(string, 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen(string, "rb"); + #endif + + fscanf(fp, "%[^\n]%*c", name); + sprintf(string, "+++ Communication with %s +++", name); + graphics.drawString(string, -1, 20, FONT_GREEN, comms); + + fscanf(fp, "%d%*c", &lines); + + for (int i = 0 ; i < lines ; i++) + { + fscanf(fp, "%[^\n]%*c", string); + faceNumber = getFace(string); + if (faceNumber > -1) + { + graphics.blit(graphics.shape[faceNumber], 10, y, comms); + col = FONT_WHITE; + } + else + { + newY = graphics.drawString(string, 80, y, col, 1, comms) + 25; + if (newY < y + 60) + newY += (60 - (newY - y)); + y = newY; + } + } + + fclose(fp); + + graphics.blevelRect(comms, 5, comms->h - 28, 180, 20, 0x25, 0x00, 0x00); + graphics.drawString("RETURN TO MESSAGES", 15, comms->h - 25, FONT_WHITE, 1, comms); + + engine.commsSection = 1; +} + +void doComms(SDL_Surface *comms) +{ + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + { + if (engine.commsSection == 0) + { + for (int i = 0 ; i < 4 ; i++) + { + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 170, 180 + (i * 60), 430, 50)) + { + createMissionDetailSurface(comms, i); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + } + } + } + else + { + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 170, 440, 160, 20)) + { + createCommsSurface(comms); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + } + } + } +} diff --git a/code/comms.h b/code/comms.h new file mode 100644 index 0000000..a1c00eb --- /dev/null +++ b/code/comms.h @@ -0,0 +1,38 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern int locateDataInPak(char *file, signed char required); +extern int getFace(char *face); + +extern globalEngineVariables engine; +extern Game currentGame; +extern Graphics graphics; +extern Planet systemPlanet[10]; diff --git a/code/debris.cpp b/code/debris.cpp new file mode 100644 index 0000000..e6033a1 --- /dev/null +++ b/code/debris.cpp @@ -0,0 +1,94 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "debris.h" + +void addDebris(int x, int y, int amount) +{ + if ((rand() % 2) == 0) + playSound(SFX_DEBRIS); + else + playSound(SFX_DEBRIS2); + + object *debris; + + amount = Math::rrand(3, rand() % amount); + Math::limitInt(&amount, 3, 8); + + for (int i = 0 ; i < amount ; i++) + { + debris = new object; + + debris->next = NULL; + debris->x = x; + debris->y = y; + + debris->thinktime = Math::rrand(60, 180); + + debris->dx = Math::rrand(-500, 500); + debris->dy = Math::rrand(-500, 500); + + if (debris->dx != 0) + debris->dx /= 100; + + if (debris->dy != 0) + debris->dy /= 100; + + engine.debrisTail->next = debris; + engine.debrisTail = debris; + } +} + +void doDebris() +{ + object *prevDebris = engine.debrisHead; + object *debris = engine.debrisHead; + engine.debrisTail = engine.debrisHead; + + while (debris->next != NULL) + { + debris = debris->next; + + if (debris->thinktime > 0) + { + debris->thinktime--; + + debris->x += engine.ssx; + debris->y += engine.ssy; + debris->x += debris->dx; + debris->y += debris->dy; + + addExplosion(debris->x + Math::rrand(-10, 10), debris->y + Math::rrand(-10, 10), E_BIG_EXPLOSION); + } + + if (debris->thinktime < 1) + { + prevDebris->next = debris->next; + delete debris; + debris = prevDebris; + } + else + { + prevDebris = debris; + engine.debrisTail = debris; + } + + } +} diff --git a/code/debris.h b/code/debris.h new file mode 100644 index 0000000..8d940df --- /dev/null +++ b/code/debris.h @@ -0,0 +1,36 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void addExplosion(float x, float y, int type); +extern void playSound(int sid); + +extern globalEngineVariables engine; +extern Graphics graphics; diff --git a/code/defs.h b/code/defs.h new file mode 100644 index 0000000..c43a661 --- /dev/null +++ b/code/defs.h @@ -0,0 +1,315 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +// ALL +#define NONE 0 + +//AI Types +enum { + + AI_NORMAL = 1, + AI_DEFENSIVE, + AI_OFFENSIVE, + AI_EVASIVE, + AI_WANDER +}; + +// Object Flags +#define FL_WEAPCO 1 +#define FL_FRIEND 2 +#define FL_IMMORTAL 4 +#define FL_NOMOVE 8 +#define FL_NOFIRE 16 +#define FL_FIRERAY 32 +#define FL_DAMAGEOWNER 64 +#define FL_LEAVESECTOR 128 +#define FL_ESCAPED 256 +#define FL_DROPMINES 512 +#define FL_AIMS 1024 +#define FL_DISABLED 2048 +#define FL_CANNOTDIE 4096 // This will only apply to Kline before the final mission +#define FL_RUNSAWAY 8192 +#define FL_ALWAYSFACE 16384 // Kline doesn't turn his back on you! ;) +#define FL_CIRCLES 32768 // Kline can circle around +#define FL_CONTINUOUS_FIRE 65536 // Go absolutely nutts(!) +#define FL_DEPLOYDRONES 131072 // Deploys small drone - Used by Boss 2 +#define FL_CANCLOAK 262144 +#define FL_ISCLOAKED 524288 +#define FL_ACTIVATE 1048576 +#define FL_HASMINIMUMSPEED 2097152 +#define FL_FIRELASER 4194304 + +// These are for Alien *indexes* NOT classdefs!! +enum { + + WC_BOSS = 14, + WC_KLINE, + FR_PHOEBE, + FR_URSULA, + FR_SID +}; + +// Droppables +enum { + + P_ANYTHING = 1, + P_WEAPONS, + P_CASH, + P_ROCKET, + P_SHIELD, + P_CARGO, + P_PLASMA_AMMO, + P_PLASMA_RATE, + P_PLASMA_SHOT, + P_PLASMA_DAMAGE, + P_MINE, // mines detonate when you "pick them up!" + P_PHOEBE, // only used as an attachment(!) + P_SLAVES, + P_ESCAPEPOD, + P_ORE, + P_SUPER +}; + +// Jobs +enum { + + WT_PLASMA = 1, + WT_ROCKET, + WT_ENERGYRAY, + WT_LASER, + WT_MICROROCKET, + WT_CHARGER, + WT_DIRECTIONAL, + WT_SPREAD +}; + +// Explosions +#define E_SMALL_EXPLOSION 4 +#define E_BIG_EXPLOSION 8 +#define E_SMOKE 12 +#define E_TINY_EXPLOSION 16 +#define E_ELECTRICAL 20 + +// Weapons +enum { + + W_NONE = -1, + W_PLAYER_WEAPON, + W_PLAYER_WEAPON2, + W_SINGLE_SHOT, + W_DOUBLE_SHOT, + W_TRIPLE_SHOT, + W_ROCKETS, + W_DOUBLE_ROCKETS, + W_MICRO_ROCKETS, + W_ENERGYRAY, + W_LASER, + W_CHARGER, + W_HOMING_MISSILE, + W_DOUBLE_HOMING_MISSILES, + W_MICRO_HOMING_MISSILES, + W_AIMED_SHOT, + W_SPREADSHOT, + W_IONCANNON, + W_DIRSHOCKMISSILE +}; + +// Weapon flags +#define WF_STRAIGHT 1 +#define WF_THIN_SPREAD 2 +#define WF_WIDE_SPREAD 4 +#define WF_SCATTER 8 +#define WF_VARIABLE_SPEED 16 +#define WF_HOMING 32 +#define WF_SHOCKWAVE 64 +#define WF_WEAPCO 128 +#define WF_FRIEND 256 +#define WF_AIMED 512 +#define WF_DISABLE 1024 +#define WF_TIMEDEXPLOSION 2048 + +// Missions +enum { + + M_DESTROY_ALL_TARGETS = 1, + M_DESTROY_TARGET_TYPE, + M_COLLECT, + M_PROTECT_PICKUP, + M_PROTECT_TARGET, + M_DISABLE_TARGET, + M_ESCAPE_TARGET +}; + +enum { + + OB_JUST_FAILED = -2, + OB_FAILED, + OB_INCOMPLETE, + OB_COMPLETED, + OB_JUST_COMPLETED, + OB_CONDITION, + OB_HIDDEN +}; + +// Class Defs - Some of these are just place holders +enum { + + CD_DUALFIGHTER, // 0 + CD_MISSILEBOAT, + CD_PROTOFIGHTER, + CD_FRIEND, + CD_FRIGATE, + CD_FRIGATE_WING1, + CD_FRIGATE_WING2, + CD_TRANSPORTSHIP, + CD_CARGOSHIP, + CD_MINER, + CD_KLINE, // 10 + CD_AIMFIGHTER, + CD_SLAVETRANSPORT, + CD_GOODTRANSPORT, + CD_SID, + CD_MINEBOSS, + CD_BOSS2_WING1, + CD_BOSS2_WING2, + CD_BOSS2_WING3, + CD_BOSS2_WING4, + CD_DRONE, // 20 + CD_CLOAKFIGHTER, + CD_EVILURSULA, + CD_KRASS, + CD_EXEC, + CD_ASTEROID, + CD_ASTEROID2, + CD_ESCORT, + CD_MOBILE_RAY, + CD_REBELCARRIER, + CD_PLUTOBOSS, // 30 + CD_BARRIER, + CD_NEPTUNEBOSS, + CD_MOBILESHIELD, + CD_PIRATE, + CD_FIREFLY, + CD_URANUSBOSS, + CD_URANUSBOSSWING1, + CD_URANUSBOSSWING2, + + // Some special ones + CD_ANY = 100, + CD_BOSS, + CD_PHOEBE, + CD_URSULA +}; + +// Font Colors +enum { + + FONT_WHITE, + FONT_RED, + FONT_YELLOW, + FONT_GREEN, + FONT_CYAN, + FONT_OUTLINE // a dark blue color +}; + +// Sounds +enum { + + SFX_EXPLOSION, + SFX_HIT, + SFX_DEATH, + SFX_MISSILE, + SFX_PLASMA, + SFX_CLOCK, + SFX_FLY, + SFX_ENERGYRAY, + SFX_PICKUP, + SFX_SHIELDUP, + SFX_CLOAK, + SFX_DEBRIS, + SFX_DEBRIS2, + SFX_LASER, + SFX_PLASMA2, + SFX_PLASMA3 +}; + +enum { + + SECTION_TITLE, + SECTION_INTERMISSION, + SECTION_GAME +}; + +enum { + + FACE_CHRIS = 90, + FACE_SID, + FACE_KRASS, + FACE_KLINE, + FACE_PHOEBE, + FACE_URSULA, + FACE_CREW +}; + +#define MAX_WEAPONS 20 +#define MAX_SHAPES 100 +#define MAX_SHIPSHAPES 120 +#define MAX_SOUNDS 17 +#define MAX_ALIENS 25 +#define MAX_TEXTSHAPES 70 +#define MAX_FONTSHAPES 6 +#define MAX_SHOPSHAPES 6 +#define MAX_DEFALIENS 40 +#define MAX_MISSIONS 28 +#define MAX_CARGO 20 +#define MAX_SHOPITEMS 17 + +#define SHIP_HIT_INDEX 60 + +#define USEPACK 1 +#ifndef PACKLOCATION +#define PACKLOCATION "starfighter.pak" +#endif +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +enum { + + PAK_IMG, + PAK_WAV, + PAK_MOD, + PAK_FONT, + PAK_S3M +}; + +const char systemNames[][15] = {"Spirit", "Eyananth", "Mordor", "Sol"}; + +const char faces[][12] = { + "FACE_CHRIS", "FACE_SID", "FACE_KRASS", + "FACE_KLINE", "FACE_PHOEBE", "FACE_URSULA", + "FACE_CREW"}; + +const char systemBackground[][20] = { + "gfx/spirit.jpg", "gfx/eyananth.jpg", + "gfx/mordor.jpg", "gfx/sol.jpg"}; diff --git a/code/events.cpp b/code/events.cpp new file mode 100644 index 0000000..a1a56e0 --- /dev/null +++ b/code/events.cpp @@ -0,0 +1,87 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "events.h" + +/* +Checked during the main game loop. When the game is paused +it goes into a constant loop checking this routine. If escape is +pressed, the game automatically ends and goes back to the title screen +*/ +signed char checkPauseRequest() +{ + getPlayerInput(); + + if (engine.keyState[SDLK_ESCAPE]) + { + engine.paused = 0; + engine.done = 1; + player.shield = 0; + return 1; + } + + if (engine.keyState[SDLK_p]) + { + engine.paused = 0; + engine.keyState[SDLK_p] = 0; + } + + return 0; +} + +void compareLastKeyInputs() +{ + if (strstr(lastKeyEvents, "humansdoitbetter") != NULL) + {engine.cheat = 1; memset(lastKeyEvents, ' ', 25);} + + if (strstr(lastKeyEvents, "credits") != NULL) + {engine.cheatCredits = 1; memset(lastKeyEvents, ' ', 25);} +} + +void addKeyEvent(char *keyName) +{ + if (strlen(keyName) > 1) + return; + + int index = -1; + + for (int i = 0 ; i < 25 ; i++) + { + if (lastKeyEvents[i] == ' ') + { + index = i; + break; + } + } + + if (index == -1) + { + for (int i = 0 ; i < 25 ; i++) + { + lastKeyEvents[i] = lastKeyEvents[i + 1]; + } + + index = 24; + } + + lastKeyEvents[index] = keyName[0]; + + compareLastKeyInputs(); +} diff --git a/code/events.h b/code/events.h new file mode 100644 index 0000000..25be92f --- /dev/null +++ b/code/events.h @@ -0,0 +1,37 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "structs.h" + +extern void getPlayerInput(); + +extern globalEngineVariables engine; +extern object player; +extern Game currentGame; +extern devVariables dev; + +char lastKeyEvents[] = " "; diff --git a/code/explosions.cpp b/code/explosions.cpp new file mode 100644 index 0000000..9bd670b --- /dev/null +++ b/code/explosions.cpp @@ -0,0 +1,111 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "explosions.h" + +/* +Create a new explosion based on supplied parameters. +The "type" will actually be used as an explosion frame check. +All explosion types have 4 images. The "thinktime" will be used +to change frames on a 21, 14, 7 basis. +*/ +void addExplosion(float x, float y, int type) +{ + object *explosion = new object; + + explosion->next = NULL; + explosion->active = 1; + explosion->x = x; + explosion->y = y; + explosion->thinktime = 28; + explosion->face = type; + explosion->image[0] = graphics.shape[type]; + + engine.explosionTail->next = explosion; + engine.explosionTail = explosion; +} + +/* +* This very simply just adds a tiny explosion at the coordinate specified. +* It creates a small engine like effect. +*/ +void addEngine(object *craft) +{ + if (rand() % 2 == 0) + return; + + float x = craft->x + (craft->engineX * craft->face); + float y = craft->y + craft->engineY; + + y += Math::rrand(-3, 3); + addExplosion(x, y, E_TINY_EXPLOSION); +} + +/* +Loops through active explosions and decrements their think time. +If their thinktime is divisable by 5, then the frame is changed to +the next one up (for example 0->1->2-3). When their think time is 0, +the explosion is killed off. +*/ +void doExplosions() +{ + object *prevExplosion = engine.explosionHead; + object *explosion = engine.explosionHead; + engine.explosionTail = engine.explosionHead; + + while (explosion->next != NULL) + { + explosion = explosion->next; + + if (explosion->active == 1) + { + explosion->thinktime--; + + explosion->x += engine.ssx; + explosion->y += engine.ssy; + + if (isOnScreen((int)explosion->x, (int)explosion->y, explosion->image[0]->w, explosion->image[0]->h)) + graphics.blit(explosion->image[0], (int)explosion->x, (int)explosion->y); + + if (explosion->thinktime < 1) + { + explosion->active = 0; + } + else if (explosion->thinktime % 7 == 0) + { + explosion->face++; + explosion->image[0] = graphics.shape[explosion->face]; + } + } + + if (explosion->active == 1) + { + prevExplosion = explosion; + engine.explosionTail = explosion; + } + else + { + prevExplosion->next = explosion->next; + delete explosion; + explosion = prevExplosion; + } + } +} + diff --git a/code/explosions.h b/code/explosions.h new file mode 100644 index 0000000..9647f7b --- /dev/null +++ b/code/explosions.h @@ -0,0 +1,37 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern int isOnScreen(int x, int y, int w, int h); + +extern globalEngineVariables engine; +extern devVariables dev; +extern object enemy[MAX_ALIENS]; +extern Graphics graphics; diff --git a/code/game.cpp b/code/game.cpp new file mode 100644 index 0000000..eea4718 --- /dev/null +++ b/code/game.cpp @@ -0,0 +1,344 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "game.h" + +void newGame() +{ + currentGame.system = 0; + currentGame.area = 0; + currentGame.sfxVolume = 0; + currentGame.musicVolume = 0; + + if (!engine.useAudio) + { + currentGame.useSound = 0; + currentGame.useMusic = 0; + } + + currentGame.autoSaveSlot = -1; + + currentGame.cash = 0; + currentGame.cashEarned = 0; + currentGame.shots = 0; + currentGame.hits = 0; + currentGame.accuracy = 0; + currentGame.totalKills = currentGame.wingMate1Kills = currentGame.wingMate2Kills = 0; + currentGame.totalOtherKills = 0; + currentGame.hasWingMate1 = currentGame.hasWingMate2 = 0; + currentGame.wingMate1Ejects = currentGame.wingMate2Ejects = 0; + currentGame.secondaryMissions = currentGame.secondaryMissionsCompleted = 0; + currentGame.shieldPickups = currentGame.rocketPickups = currentGame.cellPickups = 0; + currentGame.powerups = currentGame.minesKilled = currentGame.cargoPickups = 0; + + currentGame.slavesRescued = 0; + currentGame.experimentalShield = 1000; + + currentGame.timeTaken = 0; + + currentGame.stationedPlanet = -1; + currentGame.destinationPlanet = -1; + for (int i = 0 ; i < 10 ; i++) + currentGame.missionCompleted[i] = 0; + currentGame.distanceCovered = 0; + + currentGame.maxPlasmaRate = 13; + currentGame.maxPlasmaOutput = 2; + currentGame.maxPlasmaDamage = 2; + currentGame.maxPlasmaAmmo = 100; + currentGame.maxRocketAmmo = 10; + + currentGame.shieldUnits = 1; + + player.maxShield = 25; + player.shield = 25; + player.ammo[0] = 0; + player.ammo[1] = 5; + player.weaponType[0] = W_PLAYER_WEAPON; + player.weaponType[1] = W_ROCKETS; + + initWeapons(); + initMissions(); + initPlanetMissions(currentGame.system); +} + +int mainGameLoop() +{ + Uint32 then, now, frames; + + resetLists(); + + setMission(currentGame.area); + missionBriefScreen(); + + initCargo(); + initPlayer(); + initAliens(); + clearInfoLines(); + + loadScriptEvents(); + + engine.ssx = 0; + engine.ssy = 0; + + engine.done = 0; + frames = 0; + + engine.counter = (SDL_GetTicks() + 1000); + engine.counter2 = (SDL_GetTicks() + 1000); + + engine.missionCompleteTimer = 0; + engine.musicVolume = 100; + + int rtn = 0; + unsigned long frameLimit = SDL_GetTicks(); + + int allowableAliens = 999999999; + + for (int i = 0 ; i < 3 ; i++) + { + if ((currentMission.primaryType[i] == M_DESTROY_TARGET_TYPE) && (currentMission.target1[i] == CD_ANY)) + allowableAliens = currentMission.targetValue1[i]; + + if (currentMission.primaryType[i] == M_DESTROY_ALL_TARGETS) + allowableAliens = 999999999; + } + + for (int i = 0 ; i < MAX_ALIENS ; i++) + { + if ((enemy[i].active) && (enemy[i].flags & FL_WEAPCO)) + { + allowableAliens--; + } + } + + then = SDL_GetTicks(); + + graphics.drawBackGround(); + graphics.flushBuffer(); + + // Default to no aliens dead... + engine.allAliensDead = 0; + + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + flushInput(); + + while (engine.done != 1) + { + ++frames; + + graphics.updateScreen(); + + if ((allMissionsCompleted()) && (engine.missionCompleteTimer == 0)) + { + engine.missionCompleteTimer = SDL_GetTicks() + 4000; + } + + if ((missionFailed()) && (engine.missionCompleteTimer == 0)) + { + if (currentGame.area != 5) + engine.missionCompleteTimer = SDL_GetTicks() + 4000; + } + + if (engine.missionCompleteTimer != 0) + { + engine.gameSection = SECTION_INTERMISSION; + if (player.shield > 0) + { + if (SDL_GetTicks() >= engine.missionCompleteTimer) + { + if ((!missionFailed()) && (currentGame.area != 26)) + { + leaveSector(); + if ((engine.done == 2) && (currentGame.area != 10) && (currentGame.area != 15)) + { + if ((enemy[FR_PHOEBE].shield > 0) && (currentGame.area != 25)) + { + enemy[FR_PHOEBE].x = player.x - 40; + enemy[FR_PHOEBE].y = player.y - 35; + enemy[FR_PHOEBE].face = 0; + } + + if ((enemy[FR_URSULA].shield > 0) && (currentGame.area != 25)) + { + enemy[FR_URSULA].x = player.x - 40; + enemy[FR_URSULA].y = player.y + 45; + enemy[FR_URSULA].face = 0; + } + + if ((currentGame.area == 9) || (currentGame.area == 17)) + { + enemy[FR_SID].x = player.x - 100; + enemy[FR_SID].y = player.y; + enemy[FR_SID].face = 0; + } + } + } + else if ((currentGame.area == 26) && (engine.musicVolume > 0)) + { + Math::limitFloat(&(engine.musicVolume -= 0.2), 0, 100); + Mix_VolumeMusic((int)engine.musicVolume); + } + else + { + engine.done = 1; + } + } + else + { + getPlayerInput(); + } + } + else + { + Math::limitFloat(&(engine.musicVolume -= 0.2), 0, 100); + Mix_VolumeMusic((int)engine.musicVolume); + if (SDL_GetTicks() >= engine.missionCompleteTimer) + { + engine.done = 1; + } + } + } + else + { + getPlayerInput(); + } + + graphics.unBuffer(); + doStarfield(); + doCollectables(); + doBullets(); + doAliens(); + doPlayer(); + doCargo(); + doDebris(); + doExplosions(); + doInfo(); + + Math::wrapChar(&(--engine.eventTimer), 0, 60); + + while (engine.paused) + { + engine.done = checkPauseRequest(); + then = SDL_GetTicks(); + frames = 0; + graphics.updateScreen(); + } + + if ((currentGame.area == 24) && (engine.addAliens > -1)) + { + if ((rand() % 10) == 0) + addCollectable(Math::rrand(800, 100), player.y, P_MINE, 25, 180 + rand() % 60); + } + + if (engine.addAliens > -1) + { + Math::wrapInt(&(--engine.addAliens), 0, currentMission.addAliens); + if ((engine.addAliens == 0) && (allowableAliens > 0)) + { + allowableAliens -= addAlien(); + } + } + + if ((player.shield <= 0) && (engine.missionCompleteTimer == 0)) + engine.missionCompleteTimer = SDL_GetTicks() + 7000; + + // specific to Boss 1 + if ((currentGame.area == 5) && (enemy[WC_BOSS].flags & FL_ESCAPED)) + { + playSound(SFX_DEATH); + graphics.clearScreen(graphics.white); + graphics.updateScreen(); + for (int i = 0 ; i < 300 ; i++) + { + SDL_Delay(10); + if ((rand() % 25) == 0) + playSound(SFX_EXPLOSION); + } + SDL_Delay(1000); + break; + } + + // (Attempt to) Limit us to 60 frame a second + while (SDL_GetTicks() < (frameLimit + 16)) + { + // Do nothing. If we were to insert an SDL_Delay(1) in here + // then we would actually lose around 10 frames per second!! + } + frameLimit = SDL_GetTicks(); + } + + graphics.flushBuffer(); + + now = SDL_GetTicks(); + if ( now > then ) + { + //printf("Mission %d: %2.2f frames per second\n", currentGame.area, ((double)frames*1000)/(now-then)); + } + + if ((player.shield > 0) && (!missionFailed())) + { + if (currentGame.area < 26) + missionFinishedScreen(); + + switch (currentGame.area) + { + case 5: + doCutscene(1); + doCutscene(2); + break; + case 7: + doCutscene(3); + break; + case 11: + doCutscene(4); + break; + case 13: + doCutscene(5); + break; + case 18: + doCutscene(6); + break; + case 26: + doCredits(); + break; + } + + if (currentGame.area < 26) + { + updateSystemStatus(); + + if (currentGame.autoSaveSlot > -1) + saveGame(currentGame.autoSaveSlot + 1); + } + + rtn = 1; + + if (currentGame.area == 26) + rtn = 0; + } + else + { + gameover(); + rtn = 0; + } + + return rtn; +} diff --git a/code/game.h b/code/game.h new file mode 100644 index 0000000..d582095 --- /dev/null +++ b/code/game.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void resetLists(); +extern void setMission(int mission); +extern void missionBriefScreen(); +extern void initPlayer(); +extern void initAliens(); +extern signed char allMissionsCompleted(); +extern void getPlayerInput(); +extern void leaveSector(); +extern void doStarfield(); +extern void doCollectables(); +extern void doCargo(); +extern void doBullets(); +extern void doAliens(); +extern void doPlayer(); +extern void doExplosions(); +extern void doDebris(); +extern void doInfo(); +extern signed char allMissionsCompleted(); +extern signed char checkPauseRequest(); +extern signed char addAlien(); +extern void missionFinishedScreen(); +extern void gameover(); +extern void clearInfoLines(); +extern signed char missionFailed(); +extern void playSound(int sid); +extern void loadScriptEvents(); +extern void doCutscene(int scene); +extern void addCollectable(float x, float y, int type, int value, int life); +extern void doCredits(); +extern void updateSystemStatus(); +extern void saveGame(int slot); +extern void flushInput(); + +extern void initWeapons(); +extern void initMissions(); +extern void initCargo(); +extern void initPlanetMissions(signed char system); + +extern globalEngineVariables engine; +extern object player; +extern object enemy[MAX_ALIENS]; +extern mission currentMission; +extern Game currentGame; +extern Graphics graphics; diff --git a/code/globals.cpp b/code/globals.cpp new file mode 100644 index 0000000..06e6992 --- /dev/null +++ b/code/globals.cpp @@ -0,0 +1,75 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "globals.h" + +void defineGlobals() +{ + engine.musicVolume = 100; + engine.useAudio = 2; + + engine.maxAliens = 9; + + engine.ssx = 0; + engine.ssy = 0; + + engine.bulletHead = new object; + engine.bulletHead->next = NULL; + engine.bulletTail = engine.bulletHead; + + engine.explosionHead = new object; + engine.explosionHead->next = NULL; + engine.explosionTail = engine.explosionHead; + + engine.collectableHead = new collectables; + engine.collectableHead->next = NULL; + engine.collectableTail = engine.collectableHead; + + engine.debrisHead = new object; + engine.debrisHead->next = NULL; + engine.debrisTail = engine.debrisHead; + + engine.commsSection = 0; + + for (int i = 0 ; i < 350 ; i++) + engine.keyState[i] = 0; + + engine.eventTimer = 0; + engine.counter2 = 0; + engine.timeTaken = 0; + engine.timeMission = 0; + engine.counter = 0; + engine.seconds = 0; + engine.minutes = 0; + engine.paused = 0; + engine.gameSection = SECTION_TITLE; + + engine.targetArrow = -1; + engine.targetArrowTimer = 0; + + engine.cheat = 0; + engine.cheatShield = 0; + engine.cheatAmmo = 0; + engine.cheatCash = 0; + + // All Development Stuff... + dev.moveAliens = 1; + dev.fireAliens = 1; +} diff --git a/code/globals.h b/code/globals.h new file mode 100644 index 0000000..38c4a70 --- /dev/null +++ b/code/globals.h @@ -0,0 +1,33 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" + +extern globalEngineVariables engine; +extern devVariables dev; diff --git a/code/graphics.cpp b/code/graphics.cpp new file mode 100644 index 0000000..3b41cbc --- /dev/null +++ b/code/graphics.cpp @@ -0,0 +1,103 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "graphics.h" + +SDL_Surface *loadImage(char *filename) +{ + SDL_Surface *image, *newImage; + + #if USEPACK + unpack(filename, PAK_IMG); + image = IMG_Load_RW(engine.sdlrw, 1); + #else + image = IMG_Load(filename); + #endif + + if (image == NULL) { + printf("Couldn't load %s: %s\n", filename, SDL_GetError()); + showErrorAndExit(0, filename); + } + + newImage = SDL_DisplayFormat(image); + if ( newImage ) { + SDL_FreeSurface(image); + } else { + // This happens when we are loading the window icon image + newImage = image; + } + + return graphics.setTransparent(newImage); +} + +/* +Simply draws the stars in their positions on screen and moves +them around. They are wrapped around using the wrapFloat() +function, as defined above, and putpixel as defined in graphics.cpp +*/ +void doStarfield() +{ + /* Lock the screen for direct access to the pixels */ + if (SDL_MUSTLOCK(graphics.screen)) + { + if (SDL_LockSurface(graphics.screen) < 0 ) + { + showErrorAndExit(2, ""); + } + } + + int color = 0; + + SDL_Rect r; + + for (int i = 0 ; i < 200 ; i++) + { + if (star[i].speed == 3) + color = graphics.white; + else if (star[i].speed == 2) + color = graphics.lightGrey; + else if (star[i].speed == 1) + color = graphics.darkGrey; + + Math::wrapFloat(&(star[i].x += (engine.ssx * star[i].speed)), 0, 799); + Math::wrapFloat(&(star[i].y += (engine.ssy * star[i].speed)), 0, 599); + + graphics.putpixel(graphics.screen, (int)star[i].x, (int)star[i].y, color); + r.x = (int)star[i].x; + r.y = (int)star[i].y; + r.w = 1; + r.h = 1; + + graphics.addBuffer(r.x, r.y, r.w, r.h); + } + + if (SDL_MUSTLOCK(graphics.screen)) + { + SDL_UnlockSurface(graphics.screen); + } +} + +int isOnScreen(int x, int y, int w, int h) +{ + if ((x + w > 0) && (x < 800) && (y + h > 0) && (y < 600)) + return 1; + + return 0; +} diff --git a/code/graphics.h b/code/graphics.h new file mode 100644 index 0000000..fe883a3 --- /dev/null +++ b/code/graphics.h @@ -0,0 +1,37 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void unpack(char *file, signed char fileType); +extern void showErrorAndExit(int errorId, char *name); + +extern Star star[200]; +extern globalEngineVariables engine; +extern Graphics graphics; diff --git a/code/init.cpp b/code/init.cpp new file mode 100644 index 0000000..8f9a0fd --- /dev/null +++ b/code/init.cpp @@ -0,0 +1,271 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "init.h" + +/* +Initalises a whole load of variables +*/ +void initVars() +{ + srand(time(NULL)); + + for (int i = 0 ; i < 200 ; i++) + { + star[i].x = rand() % 800; + star[i].y = rand() % 600; + star[i].speed = 1 + (rand() % 3); + } + + // These are good values for sound and music + + if (engine.useAudio) + { + Mix_Volume(-1, 25); + Mix_VolumeMusic((int)engine.musicVolume); + } +} + +/* +Something went wrong. This stops the game, present the error message and +prompts the user to press space or ctrl to exit the game. This is unlikely to +be seen by people unless something really stoopid happens! +*/ +void showErrorAndExit(int errorId, char *name) +{ + graphics.clearScreen(graphics.black); + + if (errorId != 2) + { + graphics.drawString("A file error has occurred", -1, 200, FONT_RED); + } + else + { + printf("Couldn't create or write to directory '%s'\n", name); + exit(1); + } + + char string[255]; + + switch(errorId) + { + case 0: + strcpy(string, ""); + sprintf(string, "%s was not found in the Starfighter data package", name); + graphics.drawString(string, -1, 250, FONT_WHITE); + graphics.drawString("Please try again. If this error persists, contact Parallel Realities", -1, 275, FONT_WHITE); + graphics.drawString("or reinstall the game", -1, 300, FONT_WHITE); + break; + case 1: + graphics.drawString("Project: Starfighter encountered an error whilst", -1, 250, FONT_WHITE); + graphics.drawString("attempting to load game data. Please try running", -1, 275, FONT_WHITE); + graphics.drawString("the game again. If the errors persist, reinstall the game", -1, 300, FONT_WHITE); + break; + case 2: + graphics.drawString("Project: Starfighter encountered a critical error", -1, 250, FONT_WHITE); + graphics.drawString("while attempting to perform a required program function.", -1, 275, FONT_WHITE); + graphics.drawString("Please contact Parallel Realities with details", -1, 300, FONT_WHITE); + break; + } + + graphics.drawString("Project: Starfighter will now exit", -1, 450, FONT_WHITE); + graphics.drawString("Press Space to continue", -1, 475, FONT_WHITE); + + engine.keyState[SDLK_SPACE] = 0; + + while (!engine.keyState[SDLK_SPACE]) + { + getPlayerInput(); + graphics.updateScreen(); + } + + exit(1); +} + +/* +This bit is just for Linux users. It attempts to get the user's +home directory, then creates the .parallelrealities and .parallelrealities/starfighter +directories so that saves and temporary data files can be written there. Good, eh? :) +*/ +#if LINUX +void setupUserHomeDirectory() +{ + char *userHome; + + char *name = getlogin(); + + passwd *pass; + + if (name != NULL) + pass = getpwnam(name); + else + pass = getpwuid(geteuid()); + + if (pass == NULL) + { + printf("Couldn't determine the user home directory. Exitting.\n"); + exit(1); + } + + userHome = pass->pw_dir; + + char dir[PATH_MAX]; + strcpy(dir, ""); + + sprintf(dir, "%s/.parallelrealities", userHome); + if ((mkdir(dir, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH) != 0) && (errno != EEXIST)) + showErrorAndExit(2, dir); + + sprintf(dir, "%s/.parallelrealities/starfighter", userHome); + if ((mkdir(dir, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH) != 0) && (errno != EEXIST)) + showErrorAndExit(2, dir); + + sprintf(engine.userHomeDirectory, "%s/.parallelrealities/starfighter/", userHome); +} +#endif + +/* +Chugg chugg chugg.... brrr... chugg chugg chugg...brrrrrr... chugg ch.. +BRRRRRRRRRRRRRRRRRMMMMMMMMMMMMMMMMMMM!! Well, hopefully anyway! ;) +*/ +void initSystem() +{ + strcpy(engine.userHomeDirectory, ""); + + #if LINUX + setupUserHomeDirectory(); + #endif + + /* Initialize the SDL library */ + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) { + printf("Couldn't initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + currentGame.useSound = 1; + currentGame.useMusic = 1; + currentGame.fullScreen = 0; + + char filename[PATH_MAX]; + int fullScreen = 0, useSound = 1, useMusic = 1; + + FILE *fp; + sprintf(filename, "%sconf", engine.userHomeDirectory); + fp = fopen(filename, "rb"); + + if (fp != NULL) + { + fscanf(fp, "%d %d %d", &fullScreen, &useSound, &useMusic); + fclose(fp); + } + + currentGame.fullScreen = fullScreen; + currentGame.useSound = useSound; + currentGame.useMusic = useMusic; + + SDL_WM_SetCaption("Project: Starfighter", "starfighter"); + SDL_WM_SetIcon(loadImage("gfx/alienDevice.png"), NULL); + + if (currentGame.fullScreen) + graphics.screen = SDL_SetVideoMode(800, 600, 16, SDL_HWPALETTE|SDL_FULLSCREEN); + else + graphics.screen = SDL_SetVideoMode(800, 600, 0, SDL_HWPALETTE); + + if (graphics.screen == NULL) { + printf("Couldn't set 800x600x16 video mode: %s\n", SDL_GetError()); + exit(1); + } + + if (engine.useAudio) + { + if (Mix_OpenAudio(22050, AUDIO_S16, engine.useAudio, 1024) < 0) + { + printf("Warning: Couldn't set 22050 Hz 16-bit audio - Reason: %s\n", Mix_GetError()); + printf("Sound and Music will be disabled\n"); + engine.useAudio = 0; + } + } + + SDL_ShowCursor(SDL_DISABLE); + SDL_EventState(SDL_MOUSEMOTION, SDL_DISABLE); +} + +/* +Removes [hopefully] all the resources that has been +loaded and created during the game. This is called by +atexit(); +*/ +void cleanUp() +{ + printf("Cleaning Up...\n"); + printf("Freeing Graphics\n"); + graphics.freeGraphics(); + printf("Freeing Background\n"); + SDL_FreeSurface(graphics.background); + printf("Freeing Sounds\n"); + freeSound(); + printf("Resetting Lists\n"); + resetLists(); + delete(engine.bulletHead); + delete(engine.explosionHead); + delete(engine.collectableHead); + delete(graphics.bufferHead); + + printf("Freeing Font\n"); + for (int i = 0 ; i < MAX_FONTSHAPES ; i++) + { + if (graphics.fontShape[i] != NULL) + SDL_FreeSurface(graphics.fontShape[i]); + } + + printf("Removing Mod\n"); + char filename[PATH_MAX]; + strcpy(filename, ""); + + sprintf(filename, "%smusic.mod", engine.userHomeDirectory); + remove(filename); + + sprintf(filename, "%smusic.s3m", engine.userHomeDirectory); + remove(filename); + + if (engine.useAudio) + { + printf("Closing Audio\n"); + Mix_CloseAudio(); + } + + // Save the config using current settings + FILE *fp; + sprintf(filename, "%sconf", engine.userHomeDirectory); + fp = fopen(filename, "wb"); + if (fp != NULL) + { + fprintf(fp, "%d %d %d\n", currentGame.fullScreen, currentGame.useSound, currentGame.useMusic); + fclose(fp); + } + else + { + printf("Error saving config\n"); + } + + SDL_Quit(); + printf("Done Cleaning Up...\nThank You for playing Starfighter\n"); +} + diff --git a/code/init.h b/code/init.h new file mode 100644 index 0000000..bc8f7cb --- /dev/null +++ b/code/init.h @@ -0,0 +1,49 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include + +#if LINUX +#include +#include +#include +#include +#endif + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void freeSound(); +extern void resetLists(); +extern void getPlayerInput(); +extern void drawString(char *in, int x, int y, int fontColor); +extern SDL_Surface *loadImage(char *filename); + +extern globalEngineVariables engine; +extern Game currentGame; +extern Graphics graphics; +extern Star star[200]; diff --git a/code/intermission.cpp b/code/intermission.cpp new file mode 100644 index 0000000..4e04073 --- /dev/null +++ b/code/intermission.cpp @@ -0,0 +1,869 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "intermission.h" + +/* +Drives the cursor. Is used by some other screens too +*/ +void doCursor() +{ + getPlayerInput(); + + Math::limitInt(&engine.cursor_x, 10, 790); + Math::limitInt(&engine.cursor_y, 10, 590); + graphics.blit(graphics.shape[0], engine.cursor_x, engine.cursor_y); +} + +/* +Sets the player's current status information lines. These are the lines +that are scrolled up the screen when the player clicks on Current Status +These are set only once. +*/ +void setStatusLines() +{ + char string[50]; + + sprintf(string, "System : %s", systemNames[currentGame.system]); + + graphics.textSurface(0, string, 0, 0, FONT_WHITE); + + signed char total = 0; + signed char completed = 0; + + for (int i = 0 ; i < 10 ; i++) + { + if (systemPlanet[i].missionNumber > -1) + { + switch(systemPlanet[i].missionCompleted) + { + case 0: + total++; + break; + case 1: + total++; + completed++; + break; + } + } + } + + for (int i = 0 ; i < 30 ; i++) + graphics.textSurface(i, "", 0, 0, FONT_WHITE); + + sprintf(string, "Missions Completed : %d/%d", completed, total); + graphics.textSurface(1, string, 0, 0, FONT_WHITE); + + sprintf(string, "Shots Fired : %d", currentGame.shots); + graphics.textSurface(2, string, 0, 0, FONT_WHITE); + + sprintf(string, "Hits Scored : %d", currentGame.hits); + graphics.textSurface(3, string, 0, 0, FONT_WHITE); + + sprintf(string, "Accuracy : %d%%", currentGame.accuracy); + graphics.textSurface(4, string, 0, 0, FONT_WHITE); + + sprintf(string, "Enemies Killed by Others : %d", currentGame.totalOtherKills); + graphics.textSurface(5, string, 0, 0, FONT_WHITE); + + sprintf(string, "Total Cash Earned : %d", currentGame.cashEarned); + graphics.textSurface(6, string, 0, 0, FONT_WHITE); + + graphics.textSurface(7, "*** Chris ***", 0, 0, FONT_WHITE); + + sprintf(string, "Enemies Killed : %d", currentGame.totalKills); + graphics.textSurface(8, string, 0, 0, FONT_WHITE); + + sprintf(string, "Shield Restores Picked Up : %d", currentGame.shieldPickups); + graphics.textSurface(9, string, 0, 0, FONT_WHITE); + + sprintf(string, "Plasma Cells Picked Up : %d", currentGame.cellPickups); + graphics.textSurface(10, string, 0, 0, FONT_WHITE); + + sprintf(string, "Rockets Picked Up : %d", currentGame.rocketPickups); + graphics.textSurface(11, string, 0, 0, FONT_WHITE); + + sprintf(string, "Powerups Picked Up : %d", currentGame.rocketPickups); + graphics.textSurface(12, string, 0, 0, FONT_WHITE); + + sprintf(string, "Mines Destroyed : %d", currentGame.minesKilled); + graphics.textSurface(13, string, 0, 0, FONT_WHITE); + + sprintf(string, "Slaves Rescued : %d", currentGame.slavesRescued); + graphics.textSurface(14, string, 0, 0, FONT_WHITE); + + sprintf(string, "Cargo Picked Up : %d", currentGame.cargoPickups); + graphics.textSurface(15, string, 0, 0, FONT_WHITE); + + if (currentGame.hasWingMate1) + { + graphics.textSurface(16, "*** Phoebe ***", 0, 0, FONT_WHITE); + + sprintf(string, "Enemies Killed : %d", currentGame.wingMate1Kills); + graphics.textSurface(17, string, 0, 0, FONT_WHITE); + + sprintf(string, "Ejections : %d", currentGame.wingMate1Ejects); + graphics.textSurface(18, string, 0, 0, FONT_WHITE); + } + + if (currentGame.hasWingMate2) + { + graphics.textSurface(19, "*** Ursula ***", 0, 0, FONT_WHITE); + + sprintf(string, "Enemies Killed : %d", currentGame.wingMate2Kills); + graphics.textSurface(20, string, 0, 0, FONT_WHITE); + + sprintf(string, "Ejections : %d", currentGame.wingMate2Ejects); + graphics.textSurface(21, string, 0, 0, FONT_WHITE); + } + + signed char percentage = 0; + if ((currentGame.secondaryMissions > 0) && (currentGame.secondaryMissionsCompleted > 0)) + percentage = (currentGame.secondaryMissionsCompleted / currentGame.secondaryMissions) * 100; + sprintf(string, "Seconday Missions Completed : %d / %d (%d%%)", currentGame.secondaryMissionsCompleted, currentGame.secondaryMissions, percentage); + graphics.textSurface(24, string, 0, 0, FONT_WHITE); + + int timeTaken = currentGame.timeTaken; + + signed char clock = 0; + while (timeTaken > 3599) + { + clock++; + timeTaken -= 3600; + } + + sprintf(string, "Total Time : %.2d", clock); + + clock = 0; + while (timeTaken > 59) + { + clock++; + timeTaken -= 60; + } + + sprintf(string, "%s:%.2d:%.2d", string, clock, timeTaken); + graphics.textSurface(26, string, -1, 0, FONT_WHITE); + graphics.textSurface(27, "Current Status", -1, 0, FONT_WHITE); + + graphics.textShape[0].y = 400; + graphics.textShape[0].x = 150; + + for (int i = 1 ; i < 25 ; i++) + { + graphics.textShape[i].y = graphics.textShape[i - 1].y + 20; + if ((i == 7) || (i == 16) || (i == 19)) + graphics.textShape[i].y += 25; + + graphics.textShape[i].x = 150; + } + + graphics.textShape[26].y = 404; + graphics.textShape[27].y = 83; +} + +/* +Sets the names and stats of the planets within the current system. +This will later be placed into a data file. +*/ +void setSystemPlanets() +{ + FILE *fp; + + char string[100]; + strcpy(string, ""); + + switch (currentGame.system) + { + case 0: + strcpy(string, "data/planets_spirit.dat"); + break; + case 1: + strcpy(string, "data/planets_eyananth.dat"); + break; + case 2: + strcpy(string, "data/planets_mordor.dat"); + break; + case 3: + strcpy(string, "data/planets_sol.dat"); + break; + } + + #if USEPACK + int dataLocation = locateDataInPak(string, 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen(string, "rb"); + #endif + + int distance; + char name[50]; + int image; + + for (int i = 0 ; i < 10 ; i++) + { + fscanf(fp, "%d %s %d", &distance, name, &image); + + systemPlanet[i].y = distance; + strcpy(systemPlanet[i].name, name); + systemPlanet[i].image = graphics.shape[image]; + } + + int messageMission; + int messageSlot; + char face[50]; + char from[100]; + char subject[100]; + + for (int i = 0 ; i < 10 ; i++) + { + fscanf(fp, "%d %d %s%*c", &messageMission, &messageSlot, face); + fscanf(fp, "%[^\n]%*c", from); + fscanf(fp, "%[^\n]%*c", subject); + + systemPlanet[i].messageMission = messageMission; + systemPlanet[i].messageSlot = messageSlot; + systemPlanet[i].faceImage = getFace(face); + + strcpy(systemPlanet[i].from, from); + strcpy(systemPlanet[i].subject, subject); + } + + fclose(fp); +} + +/* +Spins the planets around the sun, spaced according to their Y value +as defined in setSystemPlanets(). Moving the cursor over the planet +will show their name and their current status +*/ +signed char showSystem(float x, float y) +{ + SDL_Rect r; + signed char planet = 0; + int planetSpace = systemPlanet[planet].y; + signed char rtn = 0; + + // Blit the sun + graphics.blit(graphics.shape[30], 370, 220); + + for (int i = 50 ; i < 300 ; i+= planetSpace) + { + x *= 0.75; + y *= 0.75; + + graphics.circle(400, 250, i, graphics.screen, graphics.darkGrey); + + r.x = int(400 + (sin(x) * i)); + r.y = int(250 + (cos(y) * i)); + r.w = 10; + r.h = 10; + + r.x -= (systemPlanet[planet].image->w / 2); + r.y -= (systemPlanet[planet].image->h / 2); + graphics.blit(systemPlanet[planet].image, r.x, r.y); + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, r.x, r.y, systemPlanet[planet].image->w, systemPlanet[planet].image->h)) + { + graphics.drawString(systemPlanet[planet].name, -1, 545, FONT_WHITE); + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + { + if (currentGame.system == 0) + { + currentGame.stationedPlanet = planet; + currentGame.destinationPlanet = planet; + currentGame.area = systemPlanet[currentGame.stationedPlanet].missionNumber; + strcpy(currentGame.stationedName, systemPlanet[currentGame.stationedPlanet].name); + } + else + { + currentGame.destinationPlanet = planet; + strcpy(currentGame.destinationName, systemPlanet[currentGame.destinationPlanet].name); + } + + rtn = 1; + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + } + } + + planet++; + if (systemPlanet[planet].y == -1) + break; + planetSpace = systemPlanet[planet].y; + } + + return rtn; +} + +/* +Scrolls the player's current information up the screen. When +the specified status line reaches a certain Y value, the entire +list is reset and the information lines begin again from the bottom +(in other words, they loop around). +*/ +void showStatus(SDL_Surface *infoSurface) +{ + graphics.blit(infoSurface, 100, 80); + + for (int i = 0 ; i < 22 ; i++) + { + graphics.textShape[i].y -= 0.25; + if ((graphics.textShape[i].y > 80) && (graphics.textShape[i].y < 400)) + graphics.blitText(i); + } + + if (graphics.textShape[21].y < 65) + { + graphics.textShape[0].y = 400; + + for (int i = 1 ; i < 25 ; i++) + { + graphics.textShape[i].y = graphics.textShape[i - 1].y + 20; + if ((i == 7) || (i == 16) || (i == 19)) + graphics.textShape[i].y += 25; + } + } + + graphics.blevelRect(100, 80, 600, 20, 0x00, 0x00, 0x99); + + graphics.blevelRect(100, 400, 600, 20, 0x00, 0x00, 0x99); + + graphics.blitText(26); + graphics.blitText(27); +} + +void createOptions(SDL_Surface *optionsSurface) +{ + SDL_FillRect(optionsSurface, NULL, graphics.black); + + graphics.blevelRect(optionsSurface, 0, 0, optionsSurface->w - 2, optionsSurface->h - 2, 0x00, 0x00, 0x44); + + graphics.drawString("++ OPTIONS ++", 105, 8, FONT_WHITE, optionsSurface); + + graphics.blevelRect(optionsSurface, 190, 45, 50, 22, 0x00, 0x00, 0x00); + graphics.blevelRect(optionsSurface, 250, 45, 50, 22, 0x00, 0x00, 0x00); + graphics.blevelRect(optionsSurface, 20, 45, 150, 22, 0x00, 0x00, 0x00); + if (currentGame.useSound) + graphics.blevelRect(optionsSurface, 190, 45, 50, 22, 0xff, 0x00, 0x00); + else + graphics.blevelRect(optionsSurface, 250, 45, 50, 22, 0xff, 0x00, 0x00); + graphics.drawString("ON", 207, 50, FONT_WHITE, optionsSurface); + graphics.drawString("OFF", 263, 50, FONT_WHITE, optionsSurface); + graphics.drawString("SOUND", 30, 50, FONT_WHITE, optionsSurface); + + graphics.blevelRect(optionsSurface, 190, 95, 50, 22, 0x00, 0x00, 0x00); + graphics.blevelRect(optionsSurface, 250, 95, 50, 22, 0x00, 0x00, 0x00); + graphics.blevelRect(optionsSurface, 20, 95, 150, 22, 0x00, 0x00, 0x00); + if (currentGame.useMusic) + graphics.blevelRect(optionsSurface, 190, 95, 50, 22, 0xff, 0x00, 0x00); + else + graphics.blevelRect(optionsSurface, 250, 95, 50, 22, 0xff, 0x00, 0x00); + graphics.drawString("ON", 207, 100, FONT_WHITE, optionsSurface); + graphics.drawString("OFF", 263, 100, FONT_WHITE, optionsSurface); + graphics.drawString("MUSIC", 30, 100, FONT_WHITE, optionsSurface); + + graphics.blevelRect(optionsSurface, 190, 145, 50, 22, 0x00, 0x00, 0x00); + graphics.blevelRect(optionsSurface, 250, 145, 50, 22, 0x00, 0x00, 0x00); + graphics.blevelRect(optionsSurface, 20, 145, 150, 22, 0x00, 0x00, 0x00); + if (currentGame.fullScreen) + graphics.blevelRect(optionsSurface, 190, 145, 50, 22, 0xff, 0x00, 0x00); + else + graphics.blevelRect(optionsSurface, 250, 145, 50, 22, 0xff, 0x00, 0x00); + graphics.drawString("ON", 207, 150, FONT_WHITE, optionsSurface); + graphics.drawString("OFF", 263, 150, FONT_WHITE, optionsSurface); + graphics.drawString("FULLSCREEN", 30, 150, FONT_WHITE, optionsSurface); + + graphics.blevelRect(optionsSurface, 20, 195, 150, 22, 0x00, 0x00, 0x00); + graphics.blevelRect(optionsSurface, 190, 195, 110, 22, 0x00, 0x00, 0x00); + if (currentGame.autoSaveSlot == -1) + { + graphics.drawString("NONE", 225, 200, FONT_WHITE, optionsSurface); + } + else + { + char string[] = "Slot %d"; + sprintf(string, "Slot %d", currentGame.autoSaveSlot + 1); + graphics.blevelRect(optionsSurface, 190, 195, 110, 22, 0xff, 0x00, 0x00); + graphics.drawString(string, 225, 200, FONT_WHITE, optionsSurface); + } + graphics.drawString("AUTOSAVE SLOT", 30, 200, FONT_WHITE, optionsSurface); +} + +void showOptions(SDL_Surface *optionsSurface) +{ + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + { + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 417, 172, 45, 22)) + currentGame.useSound = 1; + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 478, 172, 45, 22)) + currentGame.useSound = 0; + + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 417, 222, 45, 22)) + { + currentGame.useMusic = 1; + if (engine.useAudio) + { + if (Mix_PausedMusic() == 1) + Mix_ResumeMusic(); + else + Mix_PlayMusic(engine.music, -1); + } + } + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 478, 222, 45, 22)) + { + currentGame.useMusic = 0; + if (engine.useAudio) + Mix_PauseMusic(); + } + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 417, 272, 45, 22)) + { + if (!currentGame.fullScreen) + { + #if LINUX + SDL_WM_ToggleFullScreen(graphics.screen); + #else + graphics.screen = SDL_SetVideoMode(800, 600, 16, SDL_HWSURFACE|SDL_HWPALETTE|SDL_FULLSCREEN); + graphics.drawBackground(); + flushBuffer(); + #endif + currentGame.fullScreen = 1; + } + } + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 478, 272, 45, 22)) + { + if (currentGame.fullScreen) + { + #if LINUX + SDL_WM_ToggleFullScreen(graphics.screen); + #else + graphics.screen = SDL_SetVideoMode(800, 600, 0, SDL_HWSURFACE|SDL_HWPALETTE); + graphics.drawBackground(); + flushBuffer(); + #endif + currentGame.fullScreen = 0; + } + } + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 417, 322, 100, 22)) + { + Math::wrapChar(&(++currentGame.autoSaveSlot), -1, 4); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + } + + createOptions(optionsSurface); + } +} + +/* +Oddly named function that controls the entire intermission +screen. This simply draws a background, stars, gridlines and the icons +at the bottom of the screen. Will call (and continue to call) the specified +functions when the player has selected an icon. +*/ +int galaxyMap() +{ + graphics.freeGraphics(); + + checkForBossMission(); // double check just to make sure! + + // Tell the game we are not in a mission so + // do not perform certain keyboard actions + engine.gameSection = SECTION_INTERMISSION; + + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + graphics.clearScreen(graphics.black); + + initSaveSlots(); + + SDL_Delay(1000); + + loadMusic("music/3DParadise.mod"); + + loadBackground((char *)systemBackground[currentGame.system]); + + char string[25]; + + engine.cursor_x = engine.cursor_y = 500; + graphics.shape[0] = loadImage("gfx/cursor.bmp"); + + // Icons 1 - 29 + for (int i = 0 ; i < 26 ; i++) + { + sprintf(string, "gfx/icon%d.bmp", (i + 1)); + graphics.shape[i + 1] = loadImage(string); + } + + graphics.shape[27] = loadImage("gfx/buyIcon.bmp"); + graphics.shape[28] = loadImage("gfx/sellIcon.bmp"); + graphics.shape[29] = loadImage("gfx/firefly1.png"); + + // Planets 30 - 39 + graphics.shape[30] = loadImage("gfx/planet_sun.gif"); + graphics.shape[31] = loadImage("gfx/planet_green.gif"); + graphics.shape[32] = loadImage("gfx/planet_blue.gif"); + graphics.shape[33] = loadImage("gfx/planet_red.gif"); + graphics.shape[34] = loadImage("gfx/planet_orange.gif"); + + // Faces (as defines) + graphics.shape[FACE_CHRIS] = loadImage("gfx/face_chris.png"); + graphics.shape[FACE_SID] = loadImage("gfx/face_sid.png"); + graphics.shape[FACE_KRASS] = loadImage("gfx/face_krass.png"); + graphics.shape[FACE_PHOEBE] = loadImage("gfx/face_phoebe.png"); + graphics.shape[FACE_URSULA] = loadImage("gfx/face_ursula.png"); + graphics.shape[FACE_KLINE] = loadImage("gfx/face_kline.png"); + + engine.done = 0; + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + engine.ssx = engine.ssy = 0; + + SDL_Rect r; + SDL_Rect destRect; + int distance = 0; + int interceptionChance = 0; + + setStatusLines(); + initShop(); + setSystemPlanets(); + + SDL_Surface *statsSurface = graphics.alphaRect(600, 330, 0x00, 0x00, 0x99); + SDL_Surface *savesSurface = graphics.createSurface(350, 300); + SDL_Surface *optionsSurface = graphics.createSurface(320, 240); + SDL_Surface *commsSurface = graphics.createSurface(450, 400); + + createSavesSurface(savesSurface, -1); + createOptions(optionsSurface); + createCommsSurface(commsSurface); + + signed char section = 1; + + float sinX = 300; + float cosY = 300; + signed char movePlanets = 1; + signed char saveSlot = -1; + + if (currentGame.system > 0) + interceptionChance = (300 / currentGame.system); + + // There is no chance of being interceptted after the final attack on Earth + if ((currentGame.system == 3) && (systemPlanet[2].missionCompleted)) + interceptionChance = 0; + + int rtn = 0; + + if ((engine.useAudio) && (currentGame.useMusic)) + Mix_PlayMusic(engine.music, -1); + + textObject iconInfo[12]; + + iconInfo[0].image = graphics.textSurface("Start Next Mission", FONT_WHITE); + iconInfo[1].image = graphics.textSurface("View System Map", FONT_WHITE); + iconInfo[2].image = graphics.textSurface("Current Status", FONT_WHITE); + iconInfo[3].image = graphics.textSurface("Save Game", FONT_WHITE); + iconInfo[4].image = graphics.textSurface("Upgrade FIREFLY", FONT_WHITE); + iconInfo[5].image = graphics.textSurface("Comms", FONT_WHITE); + iconInfo[6].image = graphics.textSurface("Options", FONT_WHITE); + iconInfo[7].image = graphics.textSurface("Exit to Title Screen", FONT_WHITE); + + sprintf(string, "System : %s", systemNames[currentGame.system]); + iconInfo[8].image = graphics.textSurface(string, FONT_WHITE); + + sprintf(string, "Stationed At: %s", systemPlanet[currentGame.stationedPlanet].name); + iconInfo[9].image = graphics.textSurface(string, FONT_WHITE); + + strcpy(string, "Destination: None"); + if (currentGame.destinationPlanet > -1) + sprintf(string, "Destination: %s", systemPlanet[currentGame.destinationPlanet].name); + iconInfo[10].image = graphics.textSurface(string, FONT_WHITE); + for (int i = 0 ; i < 9 ; i++) + iconInfo[i].x = (800 - iconInfo[i].image->w) / 2; + + iconInfo[11].image = graphics.textSurface("Go to Destination Planet", FONT_WHITE); + + signed char redrawBackGround = 1; + + player.maxShield = (25 * currentGame.shieldUnits); + + if (currentGame.distanceCovered > 0) + section = 0; + else + player.shield = player.maxShield; + + unsigned long frameLimit = SDL_GetTicks(); + + flushInput(); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + engine.done = 0; + + while (!engine.done) + { + graphics.updateScreen(); + + if (redrawBackGround) + { + graphics.drawBackGround(); + redrawBackGround = 0; + } + else + { + graphics.unBuffer(); + } + + doStarfield(); + + r.x = 0; + r.y = 0; + r.h = 600; + r.w = 1; + for (int i = 40 ; i < 800 ; i+= 40) + { + r.x = i; + SDL_FillRect(graphics.screen, &r, graphics.darkerBlue); + } + + r.x = 0; + r.y = 0; + r.h = 1; + r.w = 800; + for (int i = 40 ; i < 600 ; i+= 40) + { + r.y = i; + SDL_FillRect(graphics.screen, &r, graphics.darkerBlue); + } + + + if (rand() % 1000 < 2) + { + engine.ssx = Math::rrand(100, 100); + engine.ssy = Math::rrand(100, 100); + engine.ssx /= 100; + engine.ssy /= 100; + } + + graphics.blit(iconInfo[8].image, (int)iconInfo[8].x, 15); + + switch(section) + { + case 0: + if (currentGame.stationedPlanet == currentGame.destinationPlanet) + { + currentGame.area = systemPlanet[currentGame.stationedPlanet].missionNumber; + rtn = 2; + engine.done = 1; + } + else + { + distance = abs(currentGame.stationedPlanet - currentGame.destinationPlanet); + distance = (5 / distance); + if (distance < 1) + distance = 1; + + SDL_FreeSurface(iconInfo[9].image); + iconInfo[9].image = graphics.textSurface(systemPlanet[currentGame.stationedPlanet].name, FONT_WHITE); + + SDL_FreeSurface(iconInfo[10].image); + iconInfo[10].image = graphics.textSurface(systemPlanet[currentGame.destinationPlanet].name, FONT_WHITE); + + section = 8; + + destRect.x = 180; + destRect.y = 450; + destRect.w = 1; + if (currentGame.distanceCovered > 0) + destRect.w = currentGame.distanceCovered; + destRect.h = 20; + } + break; + + case 1: + if (engine.keyState[SDLK_SPACE]) + { + movePlanets = !movePlanets; + engine.keyState[SDLK_SPACE] = 0; + } + + if (movePlanets) + { + sinX += 0.01; + cosY += 0.01; + } + + if (showSystem(sinX, cosY)) + { + if (currentGame.system == 0) + { + sprintf(string, "Stationed At: %s", systemPlanet[currentGame.stationedPlanet].name); + SDL_FreeSurface(iconInfo[9].image); + iconInfo[9].image = graphics.textSurface(string, FONT_WHITE); + updateCommsSurface(commsSurface); + } + else + { + sprintf(string, "Destination: %s", systemPlanet[currentGame.destinationPlanet].name); + SDL_FreeSurface(iconInfo[10].image); + iconInfo[10].image = graphics.textSurface(string, FONT_WHITE); + } + } + + graphics.blit(iconInfo[9].image, 90, 450); + if ((currentGame.system > 0) && (currentGame.stationedPlanet != currentGame.destinationPlanet)) + graphics.blit(iconInfo[10].image, 550, 450); + break; + + case 2: + showStatus(statsSurface); + break; + + case 3: + graphics.blit(savesSurface, 200, 100); + saveSlot = showSaveSlots(savesSurface, saveSlot); + break; + + case 4: + showShop(); + break; + + case 5: + graphics.blit(commsSurface, 170, 70); + doComms(commsSurface); + break; + + case 6: + graphics.blit(optionsSurface, 230, 130); + showOptions(optionsSurface); + break; + + case 7: + rtn = 0; + engine.done = 1; + break; + + case 8: + showSystem(sinX, cosY); + + graphics.blit(systemPlanet[currentGame.stationedPlanet].image, 150, 450); + graphics.blit(iconInfo[9].image, 135, 480); + graphics.blit(systemPlanet[currentGame.destinationPlanet].image, 650, 450); + graphics.blit(iconInfo[10].image, 635, 480); + + destRect.w += distance; + SDL_FillRect(graphics.screen, &destRect, graphics.red); + + if (destRect.w >= 450) + { + currentGame.stationedPlanet = currentGame.destinationPlanet; + currentGame.distanceCovered = 0; + player.shield = player.maxShield; + sprintf(string, "Stationed At: %s", systemPlanet[currentGame.stationedPlanet].name); + strcpy(currentGame.stationedName, systemPlanet[currentGame.stationedPlanet].name); + SDL_FreeSurface(iconInfo[9].image); + iconInfo[9].image = graphics.textSurface(string, FONT_WHITE); + updateCommsSurface(commsSurface); + section = 1; + redrawBackGround = 1; + } + + if (interceptionChance > 0) + { + if ((rand() % interceptionChance) == 0) + { + currentGame.area = MAX_MISSIONS - 1; + rtn = 2; + engine.done = 1; + currentGame.distanceCovered = destRect.w; + } + } + + break; + } + + graphics.addBuffer(300, 545, 200, 15); + + if (section != 8) + { + for (int i = 0 ; i < 8 ; i++) + { + // if the mission has been completed, there is no "Start Next Mission" icon + if (i == 0) + { + if ((currentGame.stationedPlanet == currentGame.destinationPlanet) && (systemPlanet[currentGame.stationedPlanet].missionCompleted != 0)) + continue; + else if (currentGame.stationedPlanet == currentGame.destinationPlanet) + graphics.blit(graphics.shape[1], 80 + (i * 90), 500); + else if (currentGame.stationedPlanet != currentGame.destinationPlanet) + graphics.blit(graphics.shape[26], 80 + (i * 90), 500); + } + else + { + graphics.blit(graphics.shape[i + 1], 80 + (i * 90), 500); + } + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 80 + (i * 90), 500, 32, 32)) + { + if (i != 0) + { + graphics.blit(iconInfo[i].image, (int)iconInfo[i].x, 545); + } + else + { + if (currentGame.stationedPlanet == currentGame.destinationPlanet) + graphics.blit(iconInfo[0].image, (int)iconInfo[i].x, 545); + else + graphics.blit(iconInfo[11].image, (int)iconInfo[i].x, 545); + } + + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + { + redrawBackGround = 1; + section = i; + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + } + } + } + } + + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + doCursor(); + + // Limit us to 60 frame a second + while (SDL_GetTicks() < (frameLimit + 16)){} + frameLimit = SDL_GetTicks(); + } + + Mix_HaltMusic(); + SDL_FreeSurface(statsSurface); + SDL_FreeSurface(savesSurface); + SDL_FreeSurface(optionsSurface); + SDL_FreeSurface(commsSurface); + for (int i = 0 ; i < 12 ; i++) + SDL_FreeSurface(iconInfo[i].image); + + player.maxShield = (25 * currentGame.shieldUnits); + if (currentGame.distanceCovered == 0) + player.shield = player.maxShield; + + return rtn; +} + diff --git a/code/intermission.h b/code/intermission.h new file mode 100644 index 0000000..13c940f --- /dev/null +++ b/code/intermission.h @@ -0,0 +1,56 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern SDL_Surface *loadImage(char *filename); +extern void doStarfield(); +extern void getPlayerInput(); +extern void showShop(); +extern void initShop(); +extern int initSaveSlots(); +extern int showSaveSlots(SDL_Surface *savesSurface, signed char saveSlot); +extern void saveGame(int slot); +extern void loadMusic(char *filename); +extern void loadBackground(char *filename); +extern void createCommsSurface(SDL_Surface *comms); +extern void updateCommsSurface(SDL_Surface *comms); +extern void createSavesSurface(SDL_Surface *savesSurface, signed char clickedSlot); +extern void checkForBossMission(); +extern void doComms(SDL_Surface *comms); +extern int locateDataInPak(char *file, signed char required); +extern int getFace(char *face); +extern void flushInput(); + +extern globalEngineVariables engine; +extern object player; +extern Game currentGame; +extern Graphics graphics; +extern Planet systemPlanet[10]; diff --git a/code/loadSave.cpp b/code/loadSave.cpp new file mode 100644 index 0000000..f5bc867 --- /dev/null +++ b/code/loadSave.cpp @@ -0,0 +1,253 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "loadSave.h" + +/* +Reads in each save game that it finds and gives it an appropriate +description using the area variable contained in the game binary +data. It returns the slot number (1 - 10) of the most recently +used file. On the title screen, this is used to determine whether +a player can "Continue Current Game" and "Load Saved Game". +*/ +int initSaveSlots() +{ + char fileName[PATH_MAX]; + int imagePos = 350; + Game tempGame; + struct stat fileInfo; + int modTime = 0; + int continueSaveIndex = 0; + + FILE *fp; + + //READ SAVE GAME DATA + for (int i = 0 ; i < 5 ; i++) + { + sprintf(fileName, "%ssave%.2d.dat", engine.userHomeDirectory, (i + 1)); + fp = fopen(fileName, "rb"); + if (fp == NULL) + { + sprintf(saveSlot[i], "%.2d - Empty", (i + 1)); + if (engine.gameSection == SECTION_TITLE) + graphics.textSurface(13 + i, saveSlot[i], -1, imagePos, FONT_WHITE); + } + else + { + if (fread(&tempGame, sizeof(Game), 1, fp) != 1) + { + sprintf(saveSlot[i], "%.2d - Corrupt Game Data", (i + 1)); + } + else + { + sprintf(saveSlot[i], "%.2d - %s, %s", (i + 1), systemNames[tempGame.system], tempGame.stationedName); + if (engine.gameSection == SECTION_TITLE) + graphics.textSurface(13 + i, saveSlot[i], -1, imagePos, FONT_WHITE); + } + + if (stat(fileName, &fileInfo) != -1) + { + if (fileInfo.st_mtime > modTime) + {modTime = fileInfo.st_mtime; continueSaveIndex = (i + 1);} + } + + fclose(fp); + } + imagePos += 20; + } + + return continueSaveIndex; +} + +/* +Fill in later... +*/ +signed char loadGame(int slot) +{ + char filename[PATH_MAX]; + FILE *fp; + sprintf(filename, "%ssave%.2d.dat", engine.userHomeDirectory, slot); + + fp = fopen(filename, "rb"); + + if (fp == NULL) + return 0; + + if (fread(¤tGame, sizeof(Game), 1, fp) != 1) + { + printf("Save game error. The file was not of the expected format.\n"); + fclose(fp); + return 0; + } + + fclose(fp); + + weapon[0] = currentGame.playerWeapon; + weapon[1] = currentGame.playerWeapon2; + player = currentGame.thePlayer; + + // Re-init all the planets in this system... + initPlanetMissions(currentGame.system); + + // ... and then override with completition status + for (int i = 0 ; i < 10 ; i++) + systemPlanet[i].missionCompleted = currentGame.missionCompleted[i]; + + return 1; +} + +void saveGame(int slot) +{ + if ((slot < 1) || (slot > 5)) + { + printf("Error - Saves may only be 1 to 5\n"); + return; + } + + FILE *fp; + char fileName[PATH_MAX]; + sprintf(fileName, "%ssave%.2d.dat", engine.userHomeDirectory, slot); + fp = fopen(fileName, "wb"); + + currentGame.playerWeapon = weapon[0]; + currentGame.playerWeapon2 = weapon[1]; + currentGame.thePlayer = player; + for (int i = 0 ; i < 10 ; i++) + currentGame.missionCompleted[i] = systemPlanet[i].missionCompleted; + + if (fp != NULL) + { + if (fwrite(¤tGame, sizeof(Game), 1, fp) != 1) + { + printf("Error Saving Game to Slot %d\n", slot); + } + fclose(fp); + } + else + { + printf("Error Saving Game to Slot %d\n", slot); + } + + // Recall to update the save slots... (lazy, yes) + initSaveSlots(); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; +} + +void createSavesSurface(SDL_Surface *savesSurface, signed char clickedSlot) +{ + graphics.blevelRect(savesSurface, 0, 0, 348, 298, 0x00, 0x00, 0x00); + + int y = 10; + + for (int i = 0 ; i < 5 ; i++) + { + if (clickedSlot == i) + graphics.blevelRect(savesSurface, 5, y, 338, 25, 0x99, 0x00, 0x00); + else + graphics.blevelRect(savesSurface, 5, y, 338, 25, 0x00, 0x00, 0x99); + graphics.drawString(saveSlot[i], 70, y + 5, FONT_WHITE, savesSurface); + y += 30; + } + + graphics.drawString("*** HELP ***", 120, 170, FONT_WHITE, savesSurface); + + switch(clickedSlot) + { + case 0: + case 1: + case 2: + case 3: + case 4: + graphics.blevelRect(savesSurface, 5, 265, 100, 25, 0x00, 0x99, 0x00); + graphics.blevelRect(savesSurface, 125, 265, 100, 25, 0x99, 0x99, 0x00); + graphics.blevelRect(savesSurface, 243, 265, 100, 25, 0x99, 0x00, 0x00); + graphics.drawString("SAVE", 40, 270, FONT_WHITE, savesSurface); + graphics.drawString("CANCEL", 150, 270, FONT_WHITE, savesSurface); + graphics.drawString("DELETE", 270, 270, FONT_WHITE, savesSurface); + + graphics.drawString("SAVE will save the game", 17, 200, FONT_WHITE, savesSurface); + graphics.drawString("CANCEL will unselect that slot", 17, 220, FONT_WHITE, savesSurface); + graphics.drawString("DELETE will remove the save", 17, 240, FONT_WHITE, savesSurface); + break; + case -1: + graphics.drawString("First click a Save game slot to use", 17, 200, FONT_WHITE, savesSurface); + break; + case -10: + graphics.drawString("Game Saved", 130, 200, FONT_WHITE, savesSurface); + break; + case -11: + graphics.drawString("Save Deleted", 130, 200, FONT_WHITE, savesSurface); + break; + } + + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; +} + +/* +Displays the save slot available. For use with an interface that +has the cursor enabled. It returns the index number of the slot clicked +so that the function invoking it can perform a load or save on that slot. +*/ +int showSaveSlots(SDL_Surface *savesSurface, signed char saveSlot) +{ + SDL_Rect r; + r.x = 201; + r.y = 115; + r.w = 348; + r.h = 25; + + int clickedSlot = -1; + + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + { + for (int i = 0 ; i < 5 ; i++) + { + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, r.x, r.y, r.w, r.h)) + { + clickedSlot = i; + createSavesSurface(savesSurface, i); + } + r.y += 30; + } + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 215, 365, 100, 25)) + { + saveGame(saveSlot + 1); + createSavesSurface(savesSurface, -10); + } + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 335, 365, 100, 25)) + createSavesSurface(savesSurface, -1); + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 453, 365, 100, 25)) + { + char filename[PATH_MAX]; + sprintf(filename, "%ssave%.2d.dat", engine.userHomeDirectory, (saveSlot + 1)); + remove(filename); + initSaveSlots(); + createSavesSurface(savesSurface, -11); + } + } + + if (clickedSlot > -1) + saveSlot = clickedSlot; + + return saveSlot; +} diff --git a/code/loadSave.h b/code/loadSave.h new file mode 100644 index 0000000..97895b7 --- /dev/null +++ b/code/loadSave.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +char saveSlot[10][25]; + +extern void getPlayerInput(); +extern void initPlanetMissions(signed char system); + +extern globalEngineVariables engine; +extern object player; +extern object weapon[MAX_WEAPONS]; +extern Game currentGame; +extern Graphics graphics; +extern Planet systemPlanet[10]; diff --git a/code/messages.cpp b/code/messages.cpp new file mode 100644 index 0000000..b01ed50 --- /dev/null +++ b/code/messages.cpp @@ -0,0 +1,232 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "messages.h" + +void setKillMessages() +{ + strcpy(killMessage[0], "Chalk another one up for me!"); + strcpy(killMessage[1], "That'll teach you!"); + strcpy(killMessage[2], "One more for me!"); + strcpy(killMessage[3], "Target destroyed!"); + strcpy(killMessage[4], "You aint so tough!"); + strcpy(killMessage[5], "Kicked your ass!"); + + strcpy(killMessage[6], "That takes me up to %d"); + + strcpy(killMessage[7], "Hey %s, you asleep over there?!"); + strcpy(killMessage[8], "I'm catching up with you, %s!"); + + strcpy(killMessage[9], "Number One, Baby!"); +} + +void setPlayerDeadMessages() +{ + strcpy(deathMessage[0], "Oh my God... No!"); + strcpy(deathMessage[1], "NOOOOOOOOOOOOOOOOOOOOOOOOOOO!!!!"); + strcpy(deathMessage[2], "Please tell me that didn't just happen..."); + strcpy(deathMessage[3], "Chris, Answer Me!!"); + strcpy(deathMessage[4], "What the hell happened?!"); + strcpy(deathMessage[5], "Chriiiiiiiiiiiiiiiiiiiiiiiiiiis!!!!"); +} + +void setMissFireMessages() +{ + strcpy(missFireMessage[0], "I am NOT your enemy!"); + strcpy(missFireMessage[1], "Hey! Watch it!"); + strcpy(missFireMessage[2], "What are you doing?! Shoot THEM!"); + strcpy(missFireMessage[3], "OW!!! I hope that was an accident!"); + strcpy(missFireMessage[4], "Open your eyes!!"); +} + +void setHitPlayerMessages() +{ + strcpy(playerHitMessage[0], "Oops! Sorry!"); + strcpy(playerHitMessage[1], "Get out of the way!"); + strcpy(playerHitMessage[2], "Don't fly into my missiles!"); +} + +void setAllyMessages() +{ + setKillMessages(); + setPlayerDeadMessages(); + setMissFireMessages(); + setHitPlayerMessages(); +} + +void getKillMessage(object *ally) +{ + char in[50], name[30], otherName[30]; + int kills, difference; + signed char firstPlace = 0; + int faceToUse = FACE_PHOEBE; + + if (ally == &enemy[FR_PHOEBE]) + { + strcpy(name, "Phoebe"); + strcpy(otherName, "Ursula"); + kills = currentGame.wingMate1Kills; + difference = currentGame.wingMate1Kills - currentGame.wingMate2Kills; + if ((currentGame.wingMate1Kills > currentGame.wingMate2Kills) && (currentGame.wingMate1Kills > currentGame.totalKills)) + firstPlace = 1; + faceToUse = FACE_PHOEBE; + } + else + { + strcpy(name, "Ursula"); + strcpy(otherName, "Phoebe"); + kills = currentGame.wingMate2Kills; + difference = currentGame.wingMate2Kills - currentGame.wingMate1Kills; + if ((currentGame.wingMate2Kills > currentGame.wingMate1Kills) && (currentGame.wingMate2Kills > currentGame.totalKills)) + firstPlace = 1; + faceToUse = FACE_URSULA; + } + + int r = rand() % 10; + + if (currentGame.hasWingMate2 == 0) + r = rand() % 7; + + switch(r) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + sprintf(in, killMessage[rand() % 6]); + break; + + case 6: + case 7: + sprintf(in, killMessage[6], kills); + break; + + case 8: + if (difference > 0) + { + sprintf(in, killMessage[7], otherName); + } + else + { + sprintf(in, killMessage[8], otherName); + } + break; + + case 9: + if (firstPlace == 1) + { + sprintf(in, killMessage[9]); + } + else + { + sprintf(in, killMessage[rand() % 6]); + } + break; + } + + setRadioMessage(faceToUse, in, 0); +} + +char *getKlineInsult() +{ + static char insult[][40] = { + "Pathetic", "How very disappointing...", "Heroic. And stupid", "Fool", "And now you're nothing but a DEAD hero" + }; + + if (currentGame.area != 26) + return (insult[rand() % 3]); + else + return (insult[3 + (rand() % 2)]); +} + +void getPlayerDeathMessage() +{ + if (enemy[WC_KLINE].active) + { + setRadioMessage(FACE_KLINE, getKlineInsult(), 1); + return; + } + + if ((enemy[WC_BOSS].active) && (enemy[WC_BOSS].classDef == CD_KRASS)) + { + setRadioMessage(FACE_KRASS, "That was the easiest $90,000,000 I've ever earned! Bwwah!! Ha!! Ha!! Ha!!", 1); + return; + } + + char name[30], in[50]; + int faceToUse = FACE_PHOEBE; + + strcpy(name, "Phoebe"); + faceToUse = FACE_PHOEBE; + + if (currentGame.hasWingMate2) + { + if ((rand() % 2) == 0) + { + strcpy(name, "Ursula"); + faceToUse = FACE_URSULA; + } + } + + sprintf(in, deathMessage[rand() % 6]); + setRadioMessage(faceToUse, in, 1); +} + +void getMissFireMessage(object *ally) +{ + char name[30], in[50]; + int faceToUse = FACE_PHOEBE; + + if (ally == &enemy[FR_PHOEBE]) + { + strcpy(name, "Phoebe"); + faceToUse = FACE_PHOEBE; + } + else + { + strcpy(name, "Ursula"); + faceToUse = FACE_URSULA; + } + + sprintf(in, missFireMessage[rand() % 5]); + setRadioMessage(faceToUse, in, 0); +} + +void getPlayerHitMessage(object *ally) +{ + char name[30], in[50]; + int faceToUse = FACE_PHOEBE; + + if (ally == &enemy[FR_PHOEBE]) + { + strcpy(name, "Phoebe"); + faceToUse = FACE_PHOEBE; + } + else + { + strcpy(name, "Ursula"); + faceToUse = FACE_URSULA; + } + + sprintf(in, playerHitMessage[rand() % 3]); + setRadioMessage(faceToUse, in, 0); +} diff --git a/code/messages.h b/code/messages.h new file mode 100644 index 0000000..15470a0 --- /dev/null +++ b/code/messages.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" + +extern void setRadioMessage(signed char face, char *in, int priority); + +char killMessage[10][50]; +char deathMessage[6][50]; +char missFireMessage[5][50]; +char playerHitMessage[3][50]; + +extern object enemy[MAX_ALIENS]; +extern Game currentGame; + diff --git a/code/misc.cpp b/code/misc.cpp new file mode 100644 index 0000000..27b418a --- /dev/null +++ b/code/misc.cpp @@ -0,0 +1,473 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "misc.h" + +void clearInfoLines() +{ + for (int i = 0 ; i < 4 ; i++) + { + graphics.textShape[i].life = 0; + } +} + +// from a to b +void copyInfoLine(int a, int b) +{ + graphics.textSurface(b, graphics.textShape[a].text, -1, 0, graphics.textShape[a].fontColor); + graphics.textShape[b].life = graphics.textShape[a].life; +} + +/* +Sets one of the three information lines on the screen. The accepts the +string and colors. It will set the information to the first free infoline +it finds (top to bottom). If it doesn't find any free ones, it will push +all the other info lines down one and add itself to the top. +*/ +void setInfoLine(char *in, int color) +{ + int index = -1; + + for (int i = 0 ; i < 3 ; i++) + { + if ((graphics.textShape[i].life == 0) && (index == -1)) + { + index = i; + } + } + + // Bump down + if (index == -1) + { + index = 2; + copyInfoLine(1, 0); + copyInfoLine(2, 1); + } + + graphics.textSurface(index, in, -1, 0, color); + graphics.textShape[index].life = 240; +} + +/* +Sets a radio message that appears at the top of the screen. Used for +script events, etc. We send a message priority too, since we don't want +Phoebe or Ursula's banter to interrupt an important message +*/ +void setRadioMessage(signed char face, char *in, int priority) +{ + if ((graphics.textShape[3].life > 0) && (priority == 0)) + return; + + graphics.textSurface(3, in, -1, 50, FONT_WHITE); + graphics.textShape[3].life = 240; + + SDL_Surface *faceShape = NULL; + if (face > -1) + faceShape = graphics.shape[face]; + + graphics.createMessageBox(faceShape, in, 1); +} + +void doTargetArrow() +{ + if ((engine.targetArrowTimer == 0) || (enemy[engine.targetIndex].shield < 1)) + return; + + if (enemy[engine.targetIndex].flags & FL_ISCLOAKED) + return; + + if (graphics.textShape[3].life > 0) + return; + + engine.targetArrow = -1; + + if (engine.targetArrowTimer > 0) + engine.targetArrowTimer--; + + int distX = (int)(enemy[engine.targetIndex].x - player.x); + int distY = (int)(enemy[engine.targetIndex].y - player.y); + + if (distY < -300) + engine.targetArrow = 36; + + if (distY > 300) + engine.targetArrow = 40; + + if (distX < -400) + engine.targetArrow = 42; + + if (distX > 400) + engine.targetArrow = 38; + + if ((distY < -300) && (distX > 400)) + engine.targetArrow = 37; + + if ((distY > 300) && (distX > 400)) + engine.targetArrow = 39; + + if ((distY > 300) && (distX < -400)) + engine.targetArrow = 41; + + if ((distY < -300) && (distX < -400)) + engine.targetArrow = 43; + + if (engine.targetArrow != -1) + { + graphics.blit(graphics.shape[engine.targetArrow], 380, 50); + graphics.blit(graphics.shape[44], 365, 70); + } +} + +/* +Fill in later... +*/ +void doInfo() +{ + int shieldColor = 0; + SDL_Rect bar; + signed char fontColor; + char text[25]; + + graphics.addBuffer(0, 20, 800, 25); + graphics.addBuffer(0, 545, 800, 25); + + if (engine.minutes > -1) + { + if ((engine.minutes == 0) && (engine.seconds <= 29)) + fontColor = FONT_RED; + else if ((engine.minutes == 0) && (engine.seconds > 29)) + fontColor = FONT_YELLOW; + else + fontColor = FONT_WHITE; + graphics.blitText(10); // time remaining + sprintf(text, "%.2d:%.2d", engine.minutes, engine.seconds); + graphics.drawString(text, 410, 21, fontColor); + } + + if (currentGame.area != MAX_MISSIONS - 1) + { + graphics.blitText(9); // mission objectives + sprintf(text, "%d", (currentMission.remainingObjectives1 + currentMission.remainingObjectives2)); + graphics.drawString(text, 745, 21, FONT_WHITE); + } + + graphics.blitText(8); // cash + sprintf(text, "%.6d", currentGame.cash); + graphics.drawString(text, 90, 21, FONT_WHITE); + + doTargetArrow(); + + fontColor = FONT_WHITE; + if (player.ammo[0] > 0) + { + if (player.ammo[0] <= 25) fontColor = FONT_YELLOW; + if (player.ammo[0] <= 10) fontColor = FONT_RED; + } + graphics.blitText(5); // plasma ammo + sprintf(text, "%.3d", player.ammo[0]); + graphics.drawString(text, 320, 551, fontColor); + + graphics.blitText(6); + + if ((player.weaponType[1] != W_CHARGER) && (player.weaponType[1] != W_LASER)) + { + if (player.ammo[1] == 1) + fontColor = FONT_RED; + else + fontColor = FONT_WHITE; + sprintf(text, "%.3d", player.ammo[1]); // rocket ammo + graphics.drawString(text, 465, 551, fontColor); + } + + if (((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER)) && (player.ammo[1] > 0)) + { + int c = graphics.white; + if (player.ammo[1] > 100) + c = graphics.red; + + bar.x = 450; + bar.y = 550; + bar.h = 12; + + for (int i = 0 ; i < (player.ammo[1] / 5) ; i++) + { + bar.w = 1; + SDL_FillRect(graphics.screen, &bar, c); + bar.x += 2; + } + } + + if ((!allMissionsCompleted()) && (SDL_GetTicks() >= engine.counter2)) + { + engine.timeTaken++; + engine.counter2 = SDL_GetTicks() + 1000; + if (engine.missionCompleteTimer == 0) + checkScriptEvents(); + } + + if ((engine.timeMission) && (!engine.cheatTime) && (player.shield > 0)) + { + if (SDL_GetTicks() >= engine.counter) + { + if ((engine.seconds > 1) && (engine.seconds <= 11) && (engine.minutes == 0)) + { + playSound(SFX_CLOCK); + } + + if (engine.seconds > 0) + { + engine.seconds--; + engine.counter = (SDL_GetTicks() + 1000); + } + else if ((engine.seconds == 0) && (engine.minutes > 0)) + { + engine.minutes--; + engine.seconds = 59; + engine.counter = (SDL_GetTicks() + 1000); + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.timeLimit1[i] > -1) + currentMission.timeLimit1[i]--; + if (currentMission.timeLimit2[i] > -1) + currentMission.timeLimit2[i]--; + } + checkTimer(); + checkScriptEvents(); + } + + if ((engine.seconds == 0) && (engine.minutes == 0)) + { + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.timeLimit1[i] > -1) + currentMission.timeLimit1[i]--; + if (currentMission.timeLimit2[i] > -1) + currentMission.timeLimit2[i]--; + } + checkTimer(); + checkScriptEvents(); + //engine.counter = 0; + engine.counter = (SDL_GetTicks() + 1000); + } + } + } + + for (int i = 0 ; i < 3 ; i++) + { + if (graphics.textShape[i].life > 0) + { + graphics.textShape[i].y = (525 - (i * 20)); + graphics.blitText(i); + graphics.textShape[i].life--; + + if (graphics.textShape[i].life == 0) + { + copyInfoLine(i + 1, i); + copyInfoLine(i + 2, i + 1); + graphics.textShape[2].life = 0; + } + } + } + + // Show the radio message if there is one + if (graphics.textShape[3].life > 0) + { + graphics.blit(graphics.messageBox, (800 - graphics.messageBox->w) / 2, 50); + graphics.textShape[3].life--; + } + + // Do the target's remaining shield (if required) + if (currentGame.area != 10) + { + if ((engine.targetIndex > -1) && (enemy[engine.targetIndex].shield > 0) && (engine.targetIndex > 9)) + { + graphics.blitText(7); + bar.w = 1; + bar.h = 12; + bar.x = 620; + bar.y = 550; + + for (float i = 0 ; i < (engine.targetShield * enemy[engine.targetIndex].shield) ; i++) + { + if (i > 50) + shieldColor = graphics.green; + else if ((i >= 25) && (i <= 50)) + shieldColor = graphics.yellow; + else + shieldColor = graphics.red; + SDL_FillRect(graphics.screen, &bar, shieldColor); + bar.x += 2; + } + } + } + + graphics.blitText(11); + + bar.w = 25; + bar.h = 12; + bar.x = 80; + bar.y = 571; + SDL_FillRect(graphics.screen, &bar, graphics.green); + + for (int i = 1 ; i < currentGame.maxPlasmaDamage ; i++) + { + bar.x += 30; + if (weapon[1].damage >= (i + 1)) + SDL_FillRect(graphics.screen, &bar, graphics.green); + else + SDL_FillRect(graphics.screen, &bar, graphics.darkGreen); + } + + graphics.blitText(12); + + bar.w = 25; + bar.h = 12; + bar.x = 315; + bar.y = 571; + SDL_FillRect(graphics.screen, &bar, graphics.yellow); + + for (int i = 1 ; i < currentGame.maxPlasmaDamage ; i++) + { + bar.x += 30; + if (weapon[1].ammo[0] >= (i + 1)) + SDL_FillRect(graphics.screen, &bar, graphics.yellow); + else + SDL_FillRect(graphics.screen, &bar, graphics.darkYellow); + } + + graphics.blitText(13); + + bar.w = 25; + bar.h = 12; + bar.x = 550; + bar.y = 571; + + for (int i = 15 ; i > (currentGame.maxPlasmaRate - 1) ; i -= 2) + { + if (weapon[1].reload[0] <= i) + SDL_FillRect(graphics.screen, &bar, graphics.blue); + else + SDL_FillRect(graphics.screen, &bar, graphics.darkerBlue); + bar.x += 30; + } + + graphics.blitText(4); + if (player.shield < 1) + return; + + if ((!engine.keyState[SDLK_SPACE]) && (player.weaponType[1] == W_LASER) && (engine.eventTimer % 8 == 1)) + Math::limitChar(&(--player.ammo[1]), 1, 255); + + if ((engine.eventTimer < 30) && (player.shield <= engine.lowShield)) + return; + + signed char blockSize = 1; + + bar.w = blockSize; + bar.h = 12; + bar.x = 95; + bar.y = 550; + + for (int i = 0 ; i < player.shield ; i += blockSize) + { + if (i >= engine.averageShield) + shieldColor = graphics.green; + else if ((i >= engine.lowShield) && (i < engine.averageShield)) + shieldColor = graphics.yellow; + else + shieldColor = graphics.red; + SDL_FillRect(graphics.screen, &bar, shieldColor); + bar.x += blockSize; + if (player.maxShield < 75) + bar.x++; + } +} + +int getFace(char *face) +{ + for (int i = 0 ; i < 7 ; i++) + { + if (strcmp(faces[i], face) == 0) + return 90 + i; + } + + return -1; +} + +void resetLists() +{ + object *ob, *ob2; + collectables *c1, *c2; + bRect *r1, *r2; + + ob = engine.bulletHead->next; + while(ob != NULL) + { + ob2 = ob; + ob = ob->next; + delete ob2; + } + engine.bulletHead->next = NULL; + engine.bulletTail = engine.bulletHead; + + ob = engine.explosionHead->next; + while(ob != NULL) + { + ob2 = ob; + ob = ob->next; + delete ob2; + } + engine.explosionHead->next = NULL; + engine.explosionTail = engine.explosionHead; + + c1 = engine.collectableHead->next; + while (c1 != NULL) + { + c2 = c1; + c1 = c1->next; + delete c2; + } + + engine.collectableHead->next = NULL; + engine.collectableTail = engine.collectableHead; + + r1 = graphics.bufferHead->next; + while (r1 != NULL) + { + r2 = r1; + r1 = r1->next; + delete r2; + } + + graphics.bufferHead->next = NULL; + graphics.bufferTail = graphics.bufferHead; + + ob = engine.debrisHead->next; + while(ob != NULL) + { + ob2 = ob; + ob = ob->next; + delete ob2; + } + engine.debrisHead->next = NULL; + engine.debrisTail = engine.debrisHead; +} + + diff --git a/code/misc.h b/code/misc.h new file mode 100644 index 0000000..6b64108 --- /dev/null +++ b/code/misc.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern signed char allMissionsCompleted(); +extern void playSound(int sid); +extern void checkTimer(); +extern void checkScriptEvents(); + +extern globalEngineVariables engine; +extern devVariables dev; +extern object player; +extern object enemy[MAX_ALIENS]; +extern object weapon[MAX_WEAPONS]; +extern mission currentMission; +extern Game currentGame; +extern Graphics graphics; diff --git a/code/missions.cpp b/code/missions.cpp new file mode 100644 index 0000000..33dca2a --- /dev/null +++ b/code/missions.cpp @@ -0,0 +1,1460 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "missions.h" + +// God, I hate this file! :(( + +void initPlanetMissions(signed char system) +{ + for (int i = 0 ; i < 10 ; i++) + { + systemPlanet[i].missionNumber = -1; // no mission for this planet + systemPlanet[i].missionCompleted = 1; + } + + switch(system) + { + // Spirit + case 0: + for (int i = 0 ; i < 4 ; i++) + { + systemPlanet[i].missionNumber = i + 1; + systemPlanet[i].missionCompleted = 0; + } + + systemPlanet[4].missionNumber = 5; + systemPlanet[4].missionCompleted = -1; + + break; + + // Eyananth + case 1: + systemPlanet[0].missionNumber = 7; + systemPlanet[0].missionCompleted = 0; + + systemPlanet[1].missionNumber = 8; + systemPlanet[1].missionCompleted = 0; + + systemPlanet[2].missionNumber = 9; + systemPlanet[2].missionCompleted = -1; + + systemPlanet[3].missionNumber = 10; + systemPlanet[3].missionCompleted = -1; + + systemPlanet[4].missionNumber = 11; + systemPlanet[4].missionCompleted = -2; + + // This one is for the slaves + systemPlanet[9].missionNumber = 6; + systemPlanet[9].missionCompleted = 0; + + break; + + // Mordor + case 2: + systemPlanet[0].missionNumber = 13; + systemPlanet[0].missionCompleted = 0; + + systemPlanet[1].missionNumber = 14; + systemPlanet[1].missionCompleted = 0; + + systemPlanet[2].missionNumber = 15; + systemPlanet[2].missionCompleted = -1; + + systemPlanet[3].missionNumber = 16; + systemPlanet[3].missionCompleted = -1; + + systemPlanet[4].missionNumber = 17; + systemPlanet[4].missionCompleted = -2; + + systemPlanet[5].missionNumber = 18; + systemPlanet[5].missionCompleted = -3; + + // This one is for the experimental fighter + systemPlanet[9].missionNumber = 12; + systemPlanet[9].missionCompleted = 0; + + break; + + // Sol + case 3: + systemPlanet[8].missionNumber = 19; + systemPlanet[8].missionCompleted = 0; + + systemPlanet[7].missionNumber = 20; + systemPlanet[7].missionCompleted = 0; + + systemPlanet[6].missionNumber = 21; + systemPlanet[6].missionCompleted = 0; + + systemPlanet[5].missionNumber = 22; + systemPlanet[5].missionCompleted = -1; + + systemPlanet[4].missionNumber = 23; + systemPlanet[4].missionCompleted = -2; + + systemPlanet[3].missionNumber = 24; + systemPlanet[3].missionCompleted = -3; + + systemPlanet[2].missionNumber = 25; + systemPlanet[2].missionCompleted = -4; + + systemPlanet[1].missionNumber = 26; + systemPlanet[1].missionCompleted = -5; + + break; + } +} + +void checkForBossMission() +{ + for (int i = 0 ; i < 10 ; i++) + { + if ((systemPlanet[i].missionCompleted == 0) && (systemPlanet[i].missionNumber != -1)) + return; + } + + for (int i = 0 ; i < 10 ; i++) + { + if (systemPlanet[i].missionCompleted < 0) + systemPlanet[i].missionCompleted++; + } +} + +void updateSystemStatus() +{ + if (currentGame.area == 0) + { + currentGame.stationedPlanet = 0; + currentGame.area = 1; + strcpy(currentGame.stationedName, "Hail"); + initPlanetMissions(currentGame.system); + } + else if (currentGame.area == 5) + { + currentGame.stationedPlanet = 0; + currentGame.system = 1; + currentGame.area = 6; + strcpy(currentGame.stationedName, "Nerod"); + initPlanetMissions(currentGame.system); + + currentGame.shieldUnits = 2; + } + else if (currentGame.area == 11) + { + currentGame.stationedPlanet = 0; + currentGame.system = 2; + currentGame.area = 12; + strcpy(currentGame.stationedName, "Odeon"); + initPlanetMissions(currentGame.system); + + currentGame.shieldUnits = 3; + } + else if (currentGame.area == 18) + { + currentGame.stationedPlanet = 8; + currentGame.system = 3; + currentGame.area = 19; + strcpy(currentGame.stationedName, "Pluto"); + initPlanetMissions(currentGame.system); + + currentGame.shieldUnits = 4; + } + else // Update the mission for the planet + { + systemPlanet[currentGame.stationedPlanet].missionCompleted = 1; + } + + strcpy(currentGame.destinationName, "None"); + currentGame.destinationPlanet = currentGame.stationedPlanet; +} + +/* +Mission Completed Variables: + +0 : Not Completed +1 : Completed +2 : Just Completed +3 : Constraint +-1 : Mission Failed +-2 : Just Failed + +Timer Variable: +1+ : Time in minutes +-1 : Time up +-2 : No timer +*/ +void clearAllMissions() +{ + for (int m = 0 ; m < MAX_MISSIONS ; m++) + { + for (int i = 0 ; i < 3 ; i++) + { + strcpy(missions[m].primaryObjective[i], ""); + missions[m].primaryType[i] = NONE; + missions[m].target1[i] = -1; + missions[m].targetValue1[i] = -1; + missions[m].timeLimit1[i] = -2; + missions[m].completed1[i] = 1; + } + + for (int i = 0 ; i < 3 ; i++) + { + strcpy(missions[m].secondaryObjective[i], ""); + missions[m].secondaryType[i] = NONE; + missions[m].target2[i] = -1; + missions[m].targetValue2[i] = -1; + missions[m].timeLimit2[i] = -2; + missions[m].completed2[i] = 1; + } + + missions[m].addAliens = -1; + } +} + +/* +Sets the currentMission object to the mission number the player +is currently on. Timing is assigned if it is required. The rate +at which to add enemies in this mission is also set. +*/ +void setMission(int mission) +{ + currentMission = missions[mission]; + engine.minutes = currentMission.timeLimit1[0]; + + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.timeLimit1[i] > engine.minutes) + engine.minutes = currentMission.timeLimit1[i]; + if (currentMission.timeLimit2[i] > engine.minutes) + engine.minutes = currentMission.timeLimit2[i]; + + if (currentMission.completed1[i] == 0) + currentMission.remainingObjectives1++; + if (currentMission.completed2[i] == 0) + currentMission.remainingObjectives1++; + } + + engine.addAliens = currentMission.addAliens; + + if (engine.minutes > -1) + { + engine.timeMission = 1; + engine.seconds = 0; + } + + engine.counter2 = 0; + engine.timeTaken = 0; +} + +void checkTimer() +{ + for (int i = 0 ; i < 3 ; i++) + { + if ((currentMission.timeLimit1[i] == -1) && (currentMission.completed1[i] == OB_INCOMPLETE)) + currentMission.completed1[i] = -2; + } + + for (int i = 0 ; i < 3 ; i++) + { + if ((currentMission.timeLimit2[i] == -1) && (currentMission.completed2[i] == OB_INCOMPLETE)) + currentMission.completed2[i] = -2; + } + + // Find out if there are any uncompleted missions that require the timer + engine.timeMission = 0; + for (int i = 0 ; i < 3 ; i++) + { + if ((currentMission.timeLimit1[i] > -1) && (currentMission.completed1[i] == OB_INCOMPLETE)) + engine.timeMission = 1; + if ((currentMission.timeLimit2[i] > -1) && (currentMission.completed2[i] == OB_INCOMPLETE)) + engine.timeMission = 1; + } + + // specific to Spirit Boss + if ((currentGame.area == 5) && (currentMission.completed1[0] < OB_INCOMPLETE)) + engine.timeMission = 1; + + // specific to the Asteroid belt + if ((currentGame.area == 24) && (currentMission.completed1[0] < OB_INCOMPLETE)) + { + currentMission.completed1[0] = OB_COMPLETED; + killAllAliens(); + engine.addAliens = -1; + setInfoLine("*** All Primary Objectives Completed ***", FONT_GREEN); + } +} + +void evaluteRequirement(int type, int id, int value, int *completed, int *targetValue, int fontColor) +{ + char message[25]; + + if ((*targetValue <= 0) && (type != M_PROTECT_TARGET) && (type != M_PROTECT_PICKUP)) + { + *completed = 2; + checkTimer(); + if ((currentGame.area == 9) && (type == M_DISABLE_TARGET)) + setRadioMessage(FACE_SID, "All vessels disabled!", 1); + } + else + { + strcpy(message, ""); + switch(type) + { + case M_COLLECT: + switch(id) + { + case P_CASH: + sprintf(message, "Collect $%d more...", *targetValue); + if ((rand() % 2) == 0) + sprintf(message, "$%d more to go...", *targetValue); + break; + case P_CARGO: + sprintf(message, "Collect %d more...", *targetValue); + if ((rand() % 2) == 0) + sprintf(message, "%d more to go...", *targetValue); + break; + case P_ORE: + sprintf(message, "Collect %d more...", *targetValue); + if ((rand() % 2) == 0) + sprintf(message, "%d more to go...", *targetValue); + break; + } + break; + case M_PROTECT_PICKUP: + *completed = -2; + switch(id) + { + case P_CARGO: + sprintf(message, "Cargo pod destroy!"); + if (currentGame.area == 2) // Get lectured by Sid + setRadioMessage(FACE_SID, "Chris, we needed that pod!! I warned you that we couldn't afford to lose a single one!!", 1); + break; + case P_ESCAPEPOD: + sprintf(message, "Escape Pod lost!"); + if (currentGame.area == 13) // Get lectured by Phoebe + setRadioMessage(FACE_PHOEBE, "No... Ursula...", 1); + break; + } + break; + case M_PROTECT_TARGET: + if (*targetValue <= 0) + { + *completed = -2; + switch (currentGame.area) + { + case 7: + setRadioMessage(FACE_SID, "Dammit, Chris! We just lost her!", 1); + break; + case 8: + setRadioMessage(FACE_CREW, "Noooo!! Hull bre...", 1); + break; + case 9: + setRadioMessage(FACE_SID, "Chris, we've got to disable them, not destroy them!!", 1); + break; + } + } + break; + case M_DESTROY_TARGET_TYPE: + if ((*targetValue <= 10) || (*targetValue % 10 == 0)) + { + sprintf(message, "Destroy %d more...", *targetValue); + if ((rand() % 2) == 0) + sprintf(message, "%d more to go...", *targetValue); + } + break; + case M_DISABLE_TARGET: + sprintf(message, "Disable %d more...", *targetValue); + break; + } + + if (strcmp(message, "") != 0) + setInfoLine(message, fontColor); + } +} + +void updateMissionRequirements(int type, int id, int value) +{ + // Can't complete missions if you're dead! + if (engine.missionCompleteTimer != 0) + return; + + char message[25]; + char matched = 0; + + // We don't need to worry here since if Sid dies, + // you will automatically fail the mission(!) + if ((type == M_DESTROY_TARGET_TYPE) && (id == CD_SID)) + { + setInfoLine("Sid has been killed!!", FONT_RED); + setRadioMessage(FACE_CHRIS, "Sid... I... I'm sorry...", 1); + currentMission.completed1[0] = -2; + } + + for (int i = 0 ; i < 3 ; i++) + { + if ((currentMission.completed1[i] == OB_INCOMPLETE) || (currentMission.completed1[i] == OB_CONDITION)) + { + if ((currentMission.primaryType[i] == type) && ((currentMission.target1[i] == id) || (currentMission.target1[i] == CD_ANY))) + { + matched = 1; + currentMission.targetValue1[i] -= value; + evaluteRequirement(type, id, value, ¤tMission.completed1[i], ¤tMission.targetValue1[i], FONT_CYAN); + } + } + } + + // Don't evaluate secondary objectives at the same time! + if (matched) + return; + + for (int i = 0 ; i < 3 ; i++) + { + if ((currentMission.completed2[i] == OB_INCOMPLETE) || (currentMission.completed2[i] == OB_CONDITION)) + { + if ((currentMission.secondaryType[i] == type) && ((currentMission.target2[i] == id) || (currentMission.target2[i] == CD_ANY))) + { + currentMission.targetValue2[i] -= value; + evaluteRequirement(type, id, value, ¤tMission.completed2[i], ¤tMission.targetValue2[i], FONT_YELLOW); + return; + } + } + } + + // Special Case - Interceptions + if (currentGame.area == MAX_MISSIONS - 1) + { + if ((type == M_COLLECT) && (id == P_SLAVES)) + { + if (systemPlanet[9].missionCompleted == 0) + { + if (currentGame.slavesRescued >= 250) + { + setInfoLine("*** Slaves Rescued - Mission Completed ***", FONT_GREEN); + systemPlanet[9].missionCompleted = 1; + } + else + { + sprintf(message, "Rescue %d more...", 250 - currentGame.slavesRescued); + setInfoLine(message, FONT_CYAN); + } + } + } + + if ((type == M_DESTROY_TARGET_TYPE) && (id == CD_CLOAKFIGHTER)) + { + setInfoLine("*** Experimental Fighter Destroyed - Mission Completed ***", FONT_GREEN); + systemPlanet[9].missionCompleted = 1; + setRadioMessage(FACE_CHRIS, "That's one less suprise that WEAPCO can spring on us!", 1); + currentGame.experimentalShield = 0; + } + } +} + +/* +This is only used as few times in the game. +Missions 11 and 23 to be exact! +*/ +char revealHiddenObjectives() +{ + char allDone = 1; + char string[255]; + strcpy(string, ""); + + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.completed1[i] == OB_HIDDEN) + { + currentMission.completed1[i] = OB_INCOMPLETE; + sprintf(string, "New Objective - %s", currentMission.primaryObjective[i]); + setInfoLine(string, FONT_CYAN); + allDone = 0; + } + } + + if (!allDone) + { + // Activate Kline!! :) + if (currentGame.area == 11) + { + killAllAliens(); + syncScriptEvents(); + enemy[WC_KLINE].active = 1; + enemy[WC_KLINE].x = player.x + 1000; + enemy[WC_KLINE].y = player.y; + enemy[WC_KLINE].flags += FL_IMMORTAL + FL_NOFIRE; + setTarget(WC_KLINE); + loadMusic("music/TranceGeneration.mod"); + Mix_PlayMusic(engine.music, -1); + } + } + + return allDone; +} + +signed char allMissionsCompleted() +{ + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.completed1[i] == OB_INCOMPLETE) + { + if ((currentMission.primaryType[i] == M_DESTROY_ALL_TARGETS) && (engine.allAliensDead) && (currentMission.remainingObjectives1 + currentMission.remainingObjectives2 == 1)) + { + currentMission.completed1[i] = 2; + checkTimer(); + } + } + } + + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.completed2[i] == OB_INCOMPLETE) + { + if ((currentMission.secondaryType[i] == M_DESTROY_ALL_TARGETS) && (engine.allAliensDead) && (currentMission.remainingObjectives1 + currentMission.remainingObjectives2 == 1)) + { + currentMission.completed2[i] = 2; + checkTimer(); + } + } + } + + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.completed1[i] == 2) + { + if (currentMission.remainingObjectives1 > 1) + { + if ((currentGame.area != 17) || ((currentGame.area == 17) && (i != 1))) + setInfoLine("*** Primary Objective Completed ***", FONT_GREEN); + else + setInfoLine(">>> Primary Objective Failed <<<", FONT_RED); + currentMission.completed1[i] = OB_COMPLETED; + } + else + { + if (currentGame.area != MAX_MISSIONS - 1) + setInfoLine("*** All Primary Objectives Completed ***", FONT_GREEN); + else + setInfoLine("*** Interception Destroyed ***", FONT_GREEN); + currentMission.completed1[i] = OB_COMPLETED; + + // do some area specific things + if ((currentGame.area == 5) || (currentGame.area == 10) || (currentGame.area == 18) || (currentGame.area == 24)) + { + if (currentMission.remainingObjectives2 == 0) + { + killAllAliens(); + engine.addAliens = -1; + } + } + + if (currentGame.area == 25) + setRadioMessage(FACE_CHRIS, "You guys stay here and keep things under control. I'm going after Kethlan!", 1); + } + } + + if (currentMission.completed2[i] == 2) + { + if (currentMission.remainingObjectives2 > 1) + { + setInfoLine("*** Secondary Objective Completed ***", FONT_GREEN); + currentMission.completed2[i] = OB_COMPLETED; + } + else + { + setInfoLine("*** All Secondary Objectives Completed ***", FONT_GREEN); + currentMission.completed2[i] = OB_COMPLETED; + + // do some area specific things + if ((currentGame.area == 10) && (currentMission.remainingObjectives1 == 0)) + { + killAllAliens(); + engine.addAliens = -1; + } + } + } + + if (currentMission.completed1[i] == -2) + { + setInfoLine(">>> MISSION FAILED <<<", FONT_RED); + currentMission.completed1[i] = OB_FAILED; + } + + if (currentMission.completed2[i] == -2) + { + setInfoLine(">>> Secondary Objective Failed <<<", FONT_RED); + currentMission.completed2[i] = OB_FAILED; + } + } + + signed char remaining = 0; + signed char add = 0; + signed char allDone = 1; + + // Zero objective list for a recount + currentMission.remainingObjectives1 = currentMission.remainingObjectives2 = 0; + + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.primaryType[i] != NONE) + { + if (currentMission.completed1[i] == 0) + { + currentMission.remainingObjectives1++; + if (currentMission.primaryType[i] == M_DESTROY_ALL_TARGETS) + add = 1; + allDone = 0; + } + + if (currentMission.completed1[i] < 0) + return 0; + } + if (currentMission.secondaryType[i] != NONE) + { + if (currentMission.completed2[i] == 0) + { + currentMission.remainingObjectives2++; + if (currentMission.secondaryType[i] == M_DESTROY_ALL_TARGETS) + add = 1; + allDone = 0; + } + } + } + + if (allDone) + allDone = revealHiddenObjectives(); + + remaining = currentMission.remainingObjectives1 + currentMission.remainingObjectives2; + + // We've only got one objective left and it's destroy all targets, + // so stop adding aliens (otherwise it might be impossible to finish!) + if ((add == 1) && (remaining == 1)) + engine.addAliens = -1; + + return allDone; +} + +signed char missionFailed() +{ + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.completed1[i] < 0) + { + return 1; + } + } + + return 0; +} + +void drawBriefScreen() +{ + SDL_Rect r = {0, 0, 800, 2}; + + for (int i = 0 ; i < 120 ; i++) + { + r.y = (i * 2) + 60; + SDL_FillRect(graphics.screen, &r, SDL_MapRGB(graphics.screen->format, 0, i, 0)); + r.y = (300 + (i * 2)); + SDL_FillRect(graphics.screen, &r, SDL_MapRGB(graphics.screen->format, 0, (120 - i), 0)); + } + + graphics.blevelRect(140, 70, 500, 20, 0x00, 0x77, 0x00); + graphics.blevelRect(140, 90, 500, 130, 0x00, 0x33, 0x00); + graphics.drawString("Primary Objectives", 150, 74, FONT_WHITE); + + for (int i = 0 ; i < 3 ; i++) + { + if ((currentMission.primaryType[i] != NONE) && (currentMission.completed1[i] != OB_HIDDEN)) + { + graphics.drawString(currentMission.primaryObjective[i], 160, 114 + (i * 30), FONT_WHITE); + } + } + + if (currentMission.secondaryType[0] != NONE) + { + graphics.blevelRect(140, 230, 500, 20, 0x00, 0x77, 0x77); + graphics.blevelRect(140, 250, 500, 130, 0x00, 0x33, 0x33); + graphics.drawString("Secondary Objectives", 150, 234, FONT_WHITE); + + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.secondaryType[i] != NONE) + { + graphics.drawString(currentMission.secondaryObjective[i], 160, 274 + (i * 30), FONT_WHITE); + currentGame.secondaryMissions++; + } + } + } + + graphics.blevelRect(140, 390, 500, 20, 0x77, 0x77, 0x00); + graphics.blevelRect(140, 410, 500, 130, 0x33, 0x33, 0x00); + graphics.drawString("Additional Information", 150, 394, FONT_WHITE); +} + +/* +Simply displays a screen with all the mission information on it, pulled +back from the data stored in the currentMission object. The music for the +mission begins playing here. +*/ +void missionBriefScreen() +{ + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + + SDL_Delay(1000); + + if (currentGame.area != MAX_MISSIONS - 1) + { + graphics.clearScreen(graphics.black); + drawBriefScreen(); + + if (currentMission.timeLimit1[0] > 0) + { + char temp[50]; + strcpy(temp, ""); + if (currentGame.area != 24) + sprintf(temp, "TIME LIMIT: %d minutes", currentMission.timeLimit1[0]); + else + sprintf(temp, "SURVIVAL FOR %d minutes", currentMission.timeLimit1[0]); + graphics.drawString(temp, -1, 500, FONT_RED); + } + + switch (currentGame.area) + { + case 9: + case 10: + case 15: + case 16: + case 18: + case 24: + case 26: + graphics.drawString("Phoebe Lexx will not be present", 160, 420, FONT_WHITE); + if (currentGame.hasWingMate2) + graphics.drawString("Ursula Lexx will not be present", 160, 450, FONT_WHITE); + break; + } + + if ((currentGame.area == 9) || (currentGame.area == 17) || (currentGame.area == 25)) + graphics.drawString("Sid Wilson will join you on this mission", 160, 480, FONT_WHITE); + + graphics.updateScreen(); + } + + loadGameGraphics(); + graphics.textSurface(4, "Shield", 25, 550, FONT_WHITE); + graphics.textSurface(5, "Plasma:", 250, 550, FONT_WHITE); + + if (player.weaponType[1] == W_CHARGER) + graphics.textSurface(6, "Charge", 385, 550, FONT_WHITE); + else if (player.weaponType[1] == W_LASER) + graphics.textSurface(6, "Heat", 405, 550, FONT_WHITE); + else + graphics.textSurface(6, "Rockets:", 385, 550, FONT_WHITE); + + graphics.textSurface(7, "Target", 550, 550, FONT_WHITE); + graphics.textSurface(8, "Cash: $", 25, 20, FONT_WHITE); + graphics.textSurface(9, "Objectives Remaining:", 550, 20, FONT_WHITE); + graphics.textSurface(10, "Time Remaining - ", 260, 20, FONT_WHITE); + graphics.textSurface(11, "Power", 25, 570, FONT_WHITE); + graphics.textSurface(12, "Output", 250, 570, FONT_WHITE); + graphics.textSurface(13, "Cooler", 485, 570, FONT_WHITE); + playRandomTrack(); + + if (currentGame.area != MAX_MISSIONS - 1) + { + graphics.drawString("PRESS CTRL TO CONTINUE...", -1, 550, FONT_WHITE); + + graphics.updateScreen(); + + flushInput(); + engine.done = 0; + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + + while (true) + { + graphics.updateScreen(); + getPlayerInput(); + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + break; + } + + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + graphics.clearScreen(graphics.black); + + SDL_Delay(1000); + } + + engine.gameSection = SECTION_GAME; +} + +/* +Display a screen showing all the information from the mission +the player has just done. This includes objectives that have been +completed and failed. A mission timer is also displayed at the bottom +of the screen. +*/ +void missionFinishedScreen() +{ + if (currentGame.area != MAX_MISSIONS - 1) + { + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + + SDL_Delay(1000); + + if (currentGame.shots > 0) + currentGame.accuracy = (currentGame.hits * 100) / currentGame.shots; + + graphics.clearScreen(graphics.black); + drawBriefScreen(); + + char temp[100]; + + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.primaryType[i] != NONE) + { + if ((currentGame.area != 17) || ((currentGame.area == 17) && (i != 1))) + graphics.drawString("COMPLETED", 550, 114 + (i * 30), FONT_GREEN); + else + graphics.drawString("FAILED", 550, 114 + (i * 30), FONT_RED); + } + } + + if (currentMission.secondaryType[0] != NONE) + { + for (int i = 0 ; i < 3 ; i++) + { + if (currentMission.secondaryType[i] != NONE) + { + sprintf(temp, currentMission.secondaryObjective[i]); + if (currentMission.completed2[i] >= 1) + { + graphics.drawString("COMPLETED", 550, 274 + (i * 30), FONT_GREEN); + currentGame.secondaryMissionsCompleted++; + } + else + { + graphics.drawString("FAILED", 550, 274 + (i * 30), FONT_RED); + } + } + } + } + + if (currentMission.remainingObjectives1 + currentMission.remainingObjectives2 == 0) + { + sprintf(temp, "Shield Bonus: $%.3d", (player.shield * 10)); + graphics.drawString(temp, -1, 430, FONT_WHITE); + currentGame.cash += (player.shield * 10); + currentGame.cashEarned += (player.shield * 10); + } + + currentGame.timeTaken += engine.timeTaken; + + signed char clock = 0; + while (engine.timeTaken > 3599) + { + clock++; + engine.timeTaken -= 3600; + } + + sprintf(temp, "Mission Time: %.2d", clock); + + clock = 0; + while (engine.timeTaken > 59) + { + clock++; + engine.timeTaken -= 60; + } + + sprintf(temp, "%s:%.2d:%.2d", temp, clock, engine.timeTaken); + + graphics.drawString(temp, -1, 500, FONT_WHITE); + + // Do some mission specific stuff here... + if (currentGame.area == 1) + currentGame.cash -= 500; + else if (currentGame.area == 13) + currentGame.hasWingMate2 = 1; + else if (currentGame.area == 16) + currentGame.cash -= 2000; + + checkForBossMission(); + + graphics.updateScreen(); + + flushInput(); + engine.done = 0; + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + + while (true) + { + graphics.updateScreen(); + getPlayerInput(); + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + break; + } + } + + // Stop people from "selling" Laser ammo as rockets. + if (player.weaponType[1] == W_LASER) + player.ammo[1] = 1; + + Mix_HaltMusic(); +} + +#if USEPACK + +void loadMissions() +{ + clearAllMissions(); + + FILE *fp; + int dataLocation; + char filename[65]; + + char objective[255]; + int type, target, value, time, complete, add; + + for (int i = 0 ; i < MAX_MISSIONS ; i++) + { + strcpy(filename, ""); + sprintf(filename, "data/mission%.2d.dat", i); + + #if USEPACK + dataLocation = locateDataInPak(filename, 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen(filename, "rb"); + #endif + + for (int j = 0 ; j < 3 ; j++) + { + fscanf(fp, "%[^\n]%*c", objective); + fscanf(fp, "%d", &type); + fscanf(fp, "%d", &target); + fscanf(fp, "%d", &value); + fscanf(fp, "%d", &time); + fscanf(fp, "%d%*c", &complete); + + strcpy(missions[i].primaryObjective[j], objective); + missions[i].primaryType[j] = type; + missions[i].target1[j] = target; + missions[i].targetValue1[j] = value; + missions[i].timeLimit1[j] = time; + missions[i].completed1[j] = complete; + + } + + for (int j = 0 ; j < 3 ; j++) + { + fscanf(fp, "%[^\n]%*c", objective); + fscanf(fp, "%d", &type); + fscanf(fp, "%d", &target); + fscanf(fp, "%d", &value); + fscanf(fp, "%d", &time); + fscanf(fp, "%d%*c", &complete); + + strcpy(missions[i].secondaryObjective[j], objective); + missions[i].secondaryType[j] = type; + missions[i].target2[j] = target; + missions[i].targetValue2[j] = value; + missions[i].timeLimit2[j] = time; + missions[i].completed2[j] = complete; + } + + fscanf(fp, "%d", &add); + + missions[i].addAliens = add; + + fclose(fp); + } +} + +void initMissions(){loadMissions();} + +#else + +void saveMissions() +{ + FILE *fp; + char filename[65]; + + for (int i = 0 ; i < MAX_MISSIONS ; i++) + { + strcpy(filename, ""); + sprintf(filename, "data/mission%.2d.dat", i); + + //WRITE MISSION DATA + fp = fopen(filename, "wb"); + if (fp == NULL) + { + printf("Unable to write Mission Data File (%s)\n", filename); + exit(1); + } + + for (int j = 0 ; j < 3 ; j++) + { + fprintf(fp, "%s\n", missions[i].primaryObjective[j]); + fprintf(fp, "%d ", missions[i].primaryType[j]); + fprintf(fp, "%d ", missions[i].target1[j]); + fprintf(fp, "%d ", missions[i].targetValue1[j]); + fprintf(fp, "%d ", missions[i].timeLimit1[j]); + fprintf(fp, "%d\n", missions[i].completed1[j]); + } + + for (int j = 0 ; j < 3 ; j++) + { + fprintf(fp, "%s\n", missions[i].secondaryObjective[j]); + fprintf(fp, "%d ", missions[i].secondaryType[j]); + fprintf(fp, "%d ", missions[i].target2[j]); + fprintf(fp, "%d ", missions[i].targetValue2[j]); + fprintf(fp, "%d ", missions[i].timeLimit2[j]); + fprintf(fp, "%d\n", missions[i].completed2[j]); + } + + fprintf(fp, "%d ", missions[i].addAliens); + + // Put an extra line for the PAK file "just in case" + fprintf(fp, "\n"); + + fclose(fp); + } +} + +/* +This is where all the missions are defined. This will be placed +into a data file when the game is finished. +*/ +void initMissions() +{ + clearAllMissions(); + + // Seconds to wait between attempting to add an enemy + int SOMETIMES = 40 * 60; + int NORMAL = 15 * 60; + int FREQUENT = 5 * 60; + int ALWAYS = 1 * 60; + int NEVER = -1; + + // Mission 0 + sprintf(missions[0].primaryObjective[0], "Escape from WEAPCO Persuit"); + missions[0].primaryType[0] = M_DESTROY_ALL_TARGETS; + missions[0].completed1[0] = OB_INCOMPLETE; + + // Mission 1 + sprintf(missions[1].primaryObjective[0], "Collect $500 to pay Mercenary for FIREFLY"); + missions[1].primaryType[0] = M_COLLECT; + missions[1].target1[0] = P_CASH; + missions[1].targetValue1[0] = 500; + missions[1].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[1].primaryObjective[1], "Destroy all remaining WEAPCO fighters"); + missions[1].primaryType[1] = M_DESTROY_ALL_TARGETS; + missions[1].completed1[1] = OB_INCOMPLETE; + + missions[1].addAliens = FREQUENT; + + // Mission 2 + sprintf(missions[2].primaryObjective[0], "Collect 6 Cargo Pods"); + missions[2].primaryType[0] = M_COLLECT; + missions[2].target1[0] = P_CARGO; + missions[2].targetValue1[0] = 6; + missions[2].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[2].primaryObjective[1], "Do not destroy *ANY* Cargo Pods"); + missions[2].primaryType[1] = M_PROTECT_PICKUP; + missions[2].target1[1] = P_CARGO; + missions[2].targetValue1[1] = 0; + missions[2].completed1[1] = OB_CONDITION; + + sprintf(missions[2].secondaryObjective[0], "Destroy all remaining WEAPCO fighters"); + missions[2].secondaryType[0] = M_DESTROY_ALL_TARGETS; + missions[2].completed2[0] = OB_INCOMPLETE; + + missions[2].addAliens = FREQUENT; + + // Mission 3 + sprintf(missions[3].primaryObjective[0], "Destroy 5 WEAPCO Missile Boats"); + missions[3].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[3].target1[0] = CD_MISSILEBOAT; + missions[3].targetValue1[0] = 5; + missions[3].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[3].secondaryObjective[0], "Destroy all remaining WEAPCO fighters"); + missions[3].secondaryType[0] = M_DESTROY_ALL_TARGETS; + missions[3].completed2[0] = OB_INCOMPLETE; + + missions[3].addAliens = NORMAL; + + // Mission 4 + sprintf(missions[4].primaryObjective[0], "Destroy 9 WEAPCO Miners"); + missions[4].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[4].target1[0] = CD_MINER; + missions[4].targetValue1[0] = 9; + missions[4].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[4].secondaryObjective[0], "Destroy all remaining WEAPCO fighters"); + missions[4].secondaryType[0] = M_DESTROY_ALL_TARGETS; + missions[4].completed2[0] = OB_INCOMPLETE; + + missions[4].addAliens = NORMAL; + + // Mission 5 + sprintf(missions[5].primaryObjective[0], "Destroy WEAPCO Frigate"); + missions[5].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[5].target1[0] = CD_BOSS; + missions[5].targetValue1[0] = 1; + missions[5].completed1[0] = OB_INCOMPLETE; + + missions[5].timeLimit1[0] = 3; + + missions[5].addAliens = SOMETIMES; + + /* + Mission 6 is the rescue 250 slaves mission. It doesn't actually + count in this list. This is just here as a place holder + */ + + // Mission 7 + sprintf(missions[7].primaryObjective[0], "Rescue Phoebe Lexx"); + missions[7].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[7].target1[0] = CD_CARGOSHIP; + missions[7].targetValue1[0] = 1; + missions[7].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[7].primaryObjective[1], "Do not allow Phoebe to be killed"); + missions[7].primaryType[1] = M_PROTECT_TARGET; + missions[7].target1[1] = CD_PHOEBE; + missions[7].targetValue1[1] = 0; + missions[7].completed1[1] = OB_CONDITION; + + sprintf(missions[7].primaryObjective[2], "Destroy all WEAPCO forces"); + missions[7].primaryType[2] = M_DESTROY_TARGET_TYPE; + missions[7].target1[2] = CD_ANY; + missions[7].targetValue1[2] = 35; + missions[7].completed1[2] = OB_INCOMPLETE; + + missions[7].addAliens = ALWAYS; + + // Mission 8 + sprintf(missions[8].primaryObjective[0], "Assist medical supply craft"); + missions[8].primaryType[0] = M_ESCAPE_TARGET; + missions[8].target1[0] = CD_GOODTRANSPORT; + missions[8].targetValue1[0] = 0; + missions[8].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[8].primaryObjective[1], "Do not allow supply craft to be destroyed"); + missions[8].primaryType[1] = M_PROTECT_TARGET; + missions[8].target1[1] = CD_GOODTRANSPORT; + missions[8].targetValue1[1] = 0; + missions[8].completed1[1] = OB_CONDITION; + + sprintf(missions[8].secondaryObjective[0], "Destroy all remaining WEAPCO fighters"); + missions[8].secondaryType[0] = M_DESTROY_ALL_TARGETS; + missions[8].completed2[0] = OB_INCOMPLETE; + + missions[8].addAliens = FREQUENT; + + // Mission 9 + sprintf(missions[9].primaryObjective[0], "Disable five WEAPCO supply craft"); + missions[9].primaryType[0] = M_DISABLE_TARGET; + missions[9].target1[0] = CD_CARGOSHIP; + missions[9].targetValue1[0] = 5; + missions[9].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[9].primaryObjective[1], "Destroy all remaining WEAPCO fighters"); + missions[9].primaryType[1] = M_DESTROY_ALL_TARGETS; + missions[9].completed1[1] = OB_INCOMPLETE; + + sprintf(missions[9].primaryObjective[2], "Protect supply craft AND Sid Wilson"); + missions[9].primaryType[2] = M_PROTECT_TARGET; + missions[9].target1[2] = CD_CARGOSHIP; + missions[9].targetValue1[2] = 0; + missions[9].completed1[2] = OB_CONDITION; + + missions[9].addAliens = FREQUENT; + + // Mission 10 + sprintf(missions[10].primaryObjective[0], "Locate doctor's escape pod"); + missions[10].primaryType[0] = M_COLLECT; + missions[10].target1[0] = P_ESCAPEPOD; + missions[10].targetValue1[0] = 1; + missions[10].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[10].primaryObjective[1], "Do not destroy doctor's escape pod"); + missions[10].primaryType[1] = M_PROTECT_PICKUP; + missions[10].target1[1] = P_ESCAPEPOD; + missions[10].targetValue1[1] = 1; // DONE ON PURPOSE!! DO NOT CHANGE THIS!!!! + missions[10].completed1[1] = OB_CONDITION; + + sprintf(missions[10].secondaryObjective[0], "Collect 10 pieces of Ore"); + missions[10].secondaryType[0] = M_COLLECT; + missions[10].target2[0] = P_ORE; + missions[10].targetValue2[0] = 10; + missions[10].completed2[0] = OB_INCOMPLETE; + + missions[10].addAliens = ALWAYS; + + missions[10].timeLimit1[0] = 3; + missions[10].timeLimit2[0] = 3; + + // Mission 11 (hmmm, that was too easy...) + sprintf(missions[11].primaryObjective[0], "Destroy WEAPCO ore mining craft"); + missions[11].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[11].target1[0] = CD_BOSS; + missions[11].targetValue1[0] = 1; + missions[11].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[11].secondaryObjective[0], "Save present slaves"); + missions[11].secondaryType[0] = M_PROTECT_PICKUP; + missions[11].target2[0] = P_SLAVES; + missions[11].completed2[0] = OB_CONDITION; + + sprintf(missions[11].primaryObjective[1], "Battle Kline"); + missions[11].primaryType[1] = M_ESCAPE_TARGET; + missions[11].target1[1] = CD_KLINE; + missions[11].targetValue1[1] = 1; + missions[11].completed1[1] = OB_HIDDEN; + + missions[11].addAliens = NEVER; + + /* + Mission 12 is the Destroy Experimental Fighter mission + this is just a place holder + */ + + // Mission 13 + sprintf(missions[13].primaryObjective[0], "Destroy Ursula's ship"); + missions[13].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[13].target1[0] = CD_EVILURSULA; + missions[13].targetValue1[0] = 0; + missions[13].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[13].primaryObjective[1], "Capture Ursula's escape pod"); + missions[13].primaryType[1] = M_COLLECT; + missions[13].target1[1] = P_ESCAPEPOD; + missions[13].targetValue1[1] = 1; + missions[13].completed1[1] = OB_INCOMPLETE; + + sprintf(missions[13].primaryObjective[2], "Do not kill Ursula"); + missions[13].primaryType[2] = M_PROTECT_PICKUP; + missions[13].target1[2] = P_ESCAPEPOD; + missions[13].targetValue1[2] = 0; + missions[13].completed1[2] = OB_CONDITION; + + sprintf(missions[13].secondaryObjective[0], "Destroy all remaining WEAPCO fighters"); + missions[13].secondaryType[0] = M_DESTROY_ALL_TARGETS; + missions[13].completed2[0] = OB_INCOMPLETE; + + missions[13].addAliens = FREQUENT; + + // Mission 14 + sprintf(missions[14].primaryObjective[0], "Assist attack on WEAPCO ore mining craft"); + missions[14].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[14].target1[0] = CD_BOSS; + missions[14].targetValue1[0] = 1; + missions[14].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[14].primaryObjective[1], "At least 1 rebel craft must survive"); + missions[14].primaryType[1] = M_PROTECT_TARGET; + missions[14].target1[1] = CD_REBELCARRIER; + missions[14].targetValue1[1] = 2; + missions[14].completed1[1] = OB_CONDITION; + + sprintf(missions[14].primaryObjective[2], "Destroy all present WEAPCO forces"); + missions[14].primaryType[2] = M_DESTROY_ALL_TARGETS; + missions[14].completed1[2] = OB_INCOMPLETE; + + missions[14].addAliens = ALWAYS; + + // Mission 15 + sprintf(missions[15].primaryObjective[0], "Collect 25 pieces of Ore"); + missions[15].primaryType[0] = M_COLLECT; + missions[15].target1[0] = P_ORE; + missions[15].targetValue1[0] = 25; + missions[15].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[15].secondaryObjective[0], "Collect 25 pieces of Ore"); + missions[15].secondaryType[0] = M_COLLECT; + missions[15].target2[0] = P_ORE; + missions[15].targetValue2[0] = 25; + missions[15].completed2[0] = OB_INCOMPLETE; + + missions[15].addAliens = ALWAYS; + + // Mission 16 + sprintf(missions[16].primaryObjective[0], "Collect $2000 to pay mercenary"); + missions[16].primaryType[0] = M_COLLECT; + missions[16].target1[0] = P_CASH; + missions[16].targetValue1[0] = 2000; + missions[16].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[16].primaryObjective[1], "Destroy all remaining WEAPCO fighters"); + missions[16].primaryType[1] = M_DESTROY_ALL_TARGETS; + missions[16].completed1[1] = OB_INCOMPLETE; + + missions[16].addAliens = ALWAYS; + + // Mission 17 + sprintf(missions[17].primaryObjective[0], "Destroy escorts"); + missions[17].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[17].target1[0] = CD_ESCORT; + missions[17].targetValue1[0] = 5; + missions[17].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[17].primaryObjective[1], "Disable executive transport"); + missions[17].primaryType[1] = M_ESCAPE_TARGET; + missions[17].target1[1] = CD_BOSS; + missions[17].targetValue1[1] = 1; + missions[17].completed1[1] = OB_INCOMPLETE; + + sprintf(missions[17].primaryObjective[2], "Destroy all remaining WEAPCO fighters"); + missions[17].primaryType[2] = M_DESTROY_ALL_TARGETS; + missions[17].completed1[2] = OB_INCOMPLETE; + + missions[17].addAliens = NORMAL; + + // Mission 18 + sprintf(missions[18].primaryObjective[0], "Destroy executive transport"); + missions[18].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[18].target1[0] = CD_BOSS; + missions[18].targetValue1[0] = 1; + missions[18].completed1[0] = OB_INCOMPLETE; + + missions[18].addAliens = ALWAYS; + + // Mission 19 + sprintf(missions[19].primaryObjective[0], "Destroy planetary guardian"); + missions[19].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[19].target1[0] = CD_PLUTOBOSS; + missions[19].targetValue1[0] = 1; + missions[19].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[19].primaryObjective[1], "Destroy all remaining WEAPCO fighters"); + missions[19].primaryType[1] = M_DESTROY_ALL_TARGETS; + missions[19].completed1[1] = OB_INCOMPLETE; + + missions[19].timeLimit1[0] = 5; + missions[19].timeLimit1[1] = 5; + + missions[19].addAliens = ALWAYS; + + // Mission 20 + sprintf(missions[20].primaryObjective[0], "Destroy planetary guardian"); + missions[20].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[20].target1[0] = CD_NEPTUNEBOSS; + missions[20].targetValue1[0] = 1; + missions[20].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[20].primaryObjective[1], "Destroy all remaining WEAPCO fighters"); + missions[20].primaryType[1] = M_DESTROY_ALL_TARGETS; + missions[20].completed1[1] = OB_INCOMPLETE; + + missions[20].timeLimit1[0] = 5; + missions[20].timeLimit1[1] = 5; + + missions[20].addAliens = ALWAYS; + + // Mission 21 + sprintf(missions[21].primaryObjective[0], "Destroy all present WEAPCO forces"); + missions[21].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[21].target1[0] = CD_URANUSBOSS; + missions[21].targetValue1[0] = 1; + missions[21].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[21].primaryObjective[1], "Destroy all remaining WEAPCO fighters"); + missions[21].primaryType[1] = M_DESTROY_ALL_TARGETS; + missions[21].completed1[1] = OB_INCOMPLETE; + + missions[21].timeLimit1[0] = 5; + missions[21].timeLimit1[1] = 5; + + missions[21].addAliens = ALWAYS; + + // Mission 22 + sprintf(missions[22].primaryObjective[0], "Destroy outer defence systems"); + missions[22].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[22].target1[0] = CD_MOBILE_RAY; + missions[22].targetValue1[0] = 6; + missions[22].completed1[0] = OB_INCOMPLETE; + + sprintf(missions[22].primaryObjective[1], "Destroy all remaining WEAPCO craft"); + missions[22].primaryType[1] = M_DESTROY_ALL_TARGETS; + missions[22].completed1[1] = OB_INCOMPLETE; + + missions[22].addAliens = NORMAL; + + // Mission 23 + sprintf(missions[23].primaryObjective[0], "Investigate distress call"); + missions[23].primaryType[0] = M_DESTROY_ALL_TARGETS; + missions[23].completed1[0] = OB_CONDITION; + + sprintf(missions[23].primaryObjective[1], "Defeat Krass Tyler"); + missions[23].primaryType[1] = M_DESTROY_TARGET_TYPE; + missions[23].target1[1] = CD_KRASS; + missions[23].targetValue1[1] = 1; + missions[23].completed1[1] = OB_HIDDEN; + + sprintf(missions[23].primaryObjective[2], "Destroy Krass' support group"); + missions[23].primaryType[2] = M_DESTROY_ALL_TARGETS; + missions[23].target1[1] = CD_FIREFLY; + missions[23].targetValue1[1] = 4; + missions[23].completed1[2] = OB_HIDDEN; + + missions[23].addAliens = ALWAYS; + + // Mission 24 + sprintf(missions[24].primaryObjective[0], "Navigate asteroid belt"); + missions[24].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[24].target1[0] = CD_BOSS; + missions[24].targetValue1[0] = 1; + missions[24].completed1[0] = OB_INCOMPLETE; + + missions[24].timeLimit1[0] = 2; + + missions[24].addAliens = ALWAYS; + + // Mission 25 + sprintf(missions[25].primaryObjective[0], "Destroy WEAPCO frontline forces"); + missions[25].primaryType[0] = M_DESTROY_TARGET_TYPE; + missions[25].target1[0] = CD_ANY; + missions[25].targetValue1[0] = 100; + missions[25].completed1[0] = OB_INCOMPLETE; + + missions[25].addAliens = ALWAYS; + + // Mission 26 + sprintf(missions[26].primaryObjective[0], "Defeat Kline"); + missions[26].primaryType[0] = M_DESTROY_ALL_TARGETS; + missions[26].completed1[0] = OB_INCOMPLETE; + + // Interception Mission + strcpy(missions[MAX_MISSIONS - 1].primaryObjective[0], ""); + missions[MAX_MISSIONS - 1].primaryType[0] = M_DESTROY_ALL_TARGETS; + missions[MAX_MISSIONS - 1].completed1[0] = OB_INCOMPLETE; + + saveMissions(); +} +#endif diff --git a/code/missions.h b/code/missions.h new file mode 100644 index 0000000..679c32c --- /dev/null +++ b/code/missions.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern SDL_Surface *loadImage(char *file_name); +extern void playRandomTrack(); +extern void getPlayerInput(); +extern void setInfoLine(char *in, int color); +extern void loadGameGraphics(); +extern void killAllAliens(); +extern int locateDataInPak(char *file, signed char required); +extern void setRadioMessage(signed char face, char *in, int priority); +extern void syncScriptEvents(); +extern void loadMusic(char *filename); +extern void setTarget(int index); +extern void flushInput(); + +extern globalEngineVariables engine; +extern object player; +extern object enemy[MAX_ALIENS]; +extern mission currentMission; +extern Game currentGame; +extern Graphics graphics; +extern Planet systemPlanet[10]; +extern mission missions[MAX_MISSIONS]; diff --git a/code/player.cpp b/code/player.cpp new file mode 100644 index 0000000..3bcdacb --- /dev/null +++ b/code/player.cpp @@ -0,0 +1,429 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "player.h" + +/* +Initialises the player for a new game. +*/ +void initPlayer() +{ + player.active = 1; + player.x = 200; + player.y = 200; + player.speed = 2; + player.maxShield = (25 * currentGame.shieldUnits); + player.systemPower = player.maxShield; + player.face = 0; + + player.image[0] = graphics.shipShape[0]; + player.image[1] = graphics.shipShape[1]; + + player.engineX = player.image[0]->w; + player.engineY = (player.image[0]->h / 2); + + player.owner = &player; + player.flags = FL_FRIEND; + + player.weaponType[0] = W_PLAYER_WEAPON; + + if (player.ammo[0] > 0) + { + player.weaponType[0] = W_PLAYER_WEAPON2; + } + else + { + player.weaponType[0] = W_PLAYER_WEAPON; + weapon[1] = weapon[0]; // reset to weapon 1 defaults + } + + player.hit = 0; + + engine.lowShield = (player.maxShield / 3); + engine.averageShield = engine.lowShield + engine.lowShield; + + if (player.weaponType[1] == W_CHARGER) + player.ammo[1] = 0; + + if (player.weaponType[1] == W_LASER) + player.ammo[1] = 1; +} + +void doPlayer() +{ + // This causes the motion to slow + engine.ssx *= 0.99; + engine.ssy *= 0.99; + + int shapeToUse; + + if (player.shield > -100) + { + if (player.shield > 0) + { + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + fireBullet(&player, 0); + + if ((engine.keyState[SDLK_SPACE]) && (player.weaponType[1] != W_NONE)) + { + if ((player.weaponType[1] != W_CHARGER) && (player.weaponType[1] != W_LASER) && (player.ammo[1] > 0)) + { + fireBullet(&player, 1); + } + + if (player.weaponType[1] == W_LASER) + { + if (player.ammo[1] < 100) + { + fireBullet(&player, 1); + player.ammo[1] += 2; + if (player.ammo[1] >= 100) + { + player.ammo[1] = 200; + setInfoLine("Laser Overheat!!", FONT_WHITE); + } + } + } + } + + if (player.weaponType[1] == W_CHARGER) + { + if (engine.keyState[SDLK_SPACE]) + { + Math::limitChar(&(++player.ammo[1]), 0, 200); + } + else + { + if (player.ammo[1] > 0) + fireBullet(&player, 1); + player.ammo[1] = 0; + } + } + + if ((engine.keyState[SDLK_LSHIFT]) || (engine.keyState[SDLK_RSHIFT])) + { + if (player.ammo[0] < 1) + { + if (weapon[0].ammo[0] == 3) + { + if (weapon[0].flags & WF_THIN_SPREAD) + { + weapon[0].flags -= WF_THIN_SPREAD; + weapon[0].flags += WF_STRAIGHT; + setInfoLine("Weapon set to Concentrate", FONT_WHITE); + } + else + { + weapon[0].flags -= WF_STRAIGHT; + weapon[0].flags += WF_THIN_SPREAD; + setInfoLine("Weapon set to Spread", FONT_WHITE); + } + } + } + else + { + if (weapon[1].ammo[0] == 3) + { + if (weapon[1].flags & WF_THIN_SPREAD) + { + weapon[1].flags -= WF_THIN_SPREAD; + weapon[1].flags += WF_STRAIGHT; + setInfoLine("Weapon set to Concentrate", FONT_WHITE); + } + else + { + weapon[1].flags -= WF_STRAIGHT; + weapon[1].flags += WF_THIN_SPREAD; + setInfoLine("Weapon set to Spread", FONT_WHITE); + } + } + else if (weapon[1].ammo[0] == 4) + { + if (weapon[1].flags & WF_THIN_SPREAD) + { + weapon[1].flags -= WF_THIN_SPREAD; + weapon[1].flags += WF_STRAIGHT; + setInfoLine("Weapon set to Concentrate", FONT_WHITE); + } + else + { + weapon[1].flags -= WF_STRAIGHT; + weapon[1].flags += WF_THIN_SPREAD; + setInfoLine("Weapon set to Spread", FONT_WHITE); + } + } + else if (weapon[1].ammo[0] == 5) + { + if (weapon[1].flags & WF_WIDE_SPREAD) + { + if (weapon[1].flags & WF_THIN_SPREAD) + weapon[1].flags -= WF_THIN_SPREAD; + weapon[1].flags -= WF_WIDE_SPREAD; + weapon[1].flags += WF_STRAIGHT; + setInfoLine("Weapon set to Concentrate", FONT_WHITE); + } + else + { + if (weapon[1].flags & WF_THIN_SPREAD) + weapon[1].flags -= WF_THIN_SPREAD; + weapon[1].flags -= WF_STRAIGHT; + weapon[1].flags += WF_WIDE_SPREAD; + setInfoLine("Weapon set to Spread", FONT_WHITE); + } + } + } + + engine.keyState[SDLK_LSHIFT] = engine.keyState[SDLK_RSHIFT] = 0; + } + + Math::limitChar(&--player.reload[0], 0, 999); + Math::limitChar(&--player.reload[1], 0, 999); + + if (engine.keyState[SDLK_UP]) + { + player.y -= player.speed; + engine.ssy += 0.1; + } + + if (engine.keyState[SDLK_DOWN]) + { + player.y += player.speed; + engine.ssy -= 0.1; + } + + if (engine.keyState[SDLK_LEFT]) + { + player.x -= player.speed; + engine.ssx += 0.1; + player.face = 1; + } + + if (engine.keyState[SDLK_RIGHT]) + { + player.x += player.speed; + engine.ssx -= 0.1; + player.face = 0; + } + + if (engine.keyState[SDLK_ESCAPE]) + { + if ((engine.done == 0) && (engine.gameSection == SECTION_GAME) && (currentMission.remainingObjectives1 == 0)) + { + playSound(SFX_FLY); + engine.done = 2; + engine.missionCompleteTimer = (SDL_GetTicks() - 1); + } + } + + if (engine.keyState[SDLK_p]) + { + engine.paused = 1; + engine.keyState[SDLK_p] = 0; + } + + if ((engine.keyState[SDLK_t]) && (currentGame.area != 10)) + { + if (engine.targetArrowTimer == -1) + engine.targetArrowTimer = 0; + else + engine.targetArrowTimer = -1; + + engine.keyState[SDLK_t] = 0; + } + + if ((engine.missionCompleteTimer == 0) && (engine.targetArrowTimer == -1)) + { + if ((enemy[engine.targetIndex].shield < 1) && (currentGame.area != 10)) + { + engine.targetIndex = rand() % MAX_ALIENS; + + if (enemy[engine.targetIndex].flags & FL_FRIEND) + engine.targetIndex = 0; + else + setTarget(engine.targetIndex); + } + } + + if (((currentGame.area == 18) && (enemy[WC_BOSS].shield > 0)) || (currentGame.area == 24)) + player.face = 0; + + if (engine.done == 0) + { + Math::limitFloat(&player.x, 100, 700); + Math::limitFloat(&player.y, 100, 500); + } + + if (player.shield > engine.lowShield) + addEngine(&player); + + shapeToUse = player.face; + + if (player.hit) + shapeToUse += SHIP_HIT_INDEX; + + Math::limitChar(&--player.hit, 0, 100); + + graphics.blit(graphics.shipShape[shapeToUse], (int)player.x, (int)player.y); + if ((player.shield <= engine.lowShield) && (rand() % 5 < 1)) + addExplosion(player.x + Math::rrand(-10, 10), player.y + Math::rrand(-10, 20), E_SMOKE); + } + else + { + player.active = 0; + player.shield--; + if (player.shield == -1) + { + if ((currentGame.hasWingMate1) || (enemy[WC_KLINE].active)) + getPlayerDeathMessage(); + + // Make it look like the ships are all still moving... + if (currentGame.area == 18) + { + for (int i = 0 ; i < MAX_ALIENS ; i++) + enemy[i].flags += FL_LEAVESECTOR; + } + + playSound(SFX_DEATH); + playSound(SFX_EXPLOSION); + } + + engine.keyState[SDLK_UP] = engine.keyState[SDLK_DOWN] = engine.keyState[SDLK_LEFT] = engine.keyState[SDLK_RIGHT] = 0; + if ((rand() % 3) == 0) + addExplosion(player.x + Math::rrand(-10, 10), player.y + Math::rrand(-10, 10), E_BIG_EXPLOSION); + if (player.shield == -99) + addDebris((int)player.x, (int)player.y, player.maxShield); + } + } + + Math::limitFloat(&engine.ssx, -3, 3); + Math::limitFloat(&engine.ssy, -3, 3); + + // Specific for the mission were you have to chase the Executive Transport + if ((currentGame.area == 18) && (enemy[WC_BOSS].shield > 0) && (player.shield > 0)) + { + engine.ssx = -6; + engine.ssy = 0; + } + + if (currentGame.area == 24) + { + engine.ssx = -6; + engine.ssy = 0; + } + + player.dx = engine.ssx; + player.dy = engine.ssy; +} + +void flushInput() +{ + for (int i = 0 ; i < 350 ; i++) + engine.keyState[i] = 0; + + while (SDL_PollEvent(&engine.event)){} +} + +void getPlayerInput() +{ + if (engine.gameSection == SECTION_INTERMISSION) + { + // Get the current mouse position + int x, y; + SDL_GetMouseState(&x, &y); + engine.cursor_x = x; + engine.cursor_y = y; + } + + if (SDL_PollEvent(&engine.event)) + { + switch (engine.event.type) + { + case SDL_QUIT: + exit(0); + break; + + case SDL_MOUSEBUTTONDOWN: + if (engine.gameSection == SECTION_INTERMISSION) + { + if (engine.event.button.button == SDL_BUTTON_LEFT) engine.keyState[SDLK_LCTRL] = 1; + if (engine.event.button.button == SDL_BUTTON_RIGHT) engine.keyState[SDLK_SPACE] = 1; + } + break; + + case SDL_KEYDOWN: + if (engine.gameSection == SECTION_TITLE) + addKeyEvent(SDL_GetKeyName(engine.event.key.keysym.sym)); + + engine.keyState[engine.event.key.keysym.sym] = 1; + + if (engine.gameSection != SECTION_GAME) + engine.paused = 0; + + break; + + case SDL_KEYUP: + if (engine.event.key.keysym.sym != SDLK_p) + engine.keyState[engine.event.key.keysym.sym] = 0; + break; + + default: + break; + } + } +} + +void leaveSector() +{ + engine.keyState[SDLK_UP] = engine.keyState[SDLK_DOWN] = engine.keyState[SDLK_LEFT] = engine.keyState[SDLK_RIGHT] = 0; + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + + if (engine.done == 0) + engine.done = 3; + + if (engine.done == 3) + { + player.face = 0; + if (player.x > -100) + { + player.x += engine.ssx; + engine.ssx -= 1; + if (player.y > 300) + player.y--; + if (player.y < 300) + player.y++; + } + + if (player.x <= -100) + { + engine.done = 2; + playSound(SFX_FLY); + } + } + + if (engine.done == 2) + { + player.face = 0; + player.x += 12; + engine.ssx -= 0.2; + if (player.x > 1600) + engine.done = 1; + } +} + diff --git a/code/player.h b/code/player.h new file mode 100644 index 0000000..b525362 --- /dev/null +++ b/code/player.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void fireBullet(object *attacker, int weaponType); +extern void addExplosion(float x, float y, int type); +extern void playSound(int sid); +extern void addEngine(object *craft); +extern void addKeyEvent(char *keyName); +extern void addDebris(int x, int y, int amount); +extern void setRadioMessage(signed char face, char *in, int priority); +extern void setInfoLine(char *in, int color); +extern void getPlayerDeathMessage(); +extern void playSound(int sid); +extern void setTarget(int index); + +extern globalEngineVariables engine; +extern object player; +extern mission currentMission; +extern Game currentGame; +extern object weapon[MAX_WEAPONS]; +extern object enemy[MAX_ALIENS]; +extern Graphics graphics; diff --git a/code/resources.cpp b/code/resources.cpp new file mode 100644 index 0000000..8c24121 --- /dev/null +++ b/code/resources.cpp @@ -0,0 +1,221 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "resources.h" + +void loadBackground(char *filename) +{ + if (graphics.background != NULL) + { + SDL_FreeSurface(graphics.background); + graphics.background = NULL; + } + graphics.background = loadImage(filename); + SDL_SetColorKey(graphics.background, 0, 0); +} + +void loadGameGraphics() +{ + int index; + char string[75]; + + graphics.freeGraphics(); + + graphics.shipShape[0] = loadImage("gfx/firefly1.png"); + graphics.shipShape[1] = loadImage("gfx/firefly2.png"); + + strcpy(string, ""); + switch(currentGame.system) + { + case 0: + strcpy(string, "data/resources_spirit.dat"); + break; + case 1: + strcpy(string, "data/resources_eyananth.dat"); + break; + case 2: + strcpy(string, "data/resources_mordor.dat"); + break; + case 3: + strcpy(string, "data/resources_sol.dat"); + break; + } + + FILE *fp; + + #if USEPACK + int dataLocation = locateDataInPak(string, 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen(string, "rb"); + #endif + + if (fp == NULL) + exit(1); + + fscanf(fp, "%d %s", &index, string); + while (index != -1) + { + graphics.shipShape[index] = loadImage(string); + fscanf(fp, "%d %s", &index, string); + } + + fclose(fp); + + /* + Overlay a red alpha surface onto + */ + SDL_Surface *hitRect; + for (int i = SHIP_HIT_INDEX ; i < MAX_SHIPSHAPES ; i++) + { + if (graphics.shipShape[i - SHIP_HIT_INDEX] == NULL) + continue; + graphics.shipShape[i] = graphics.createSurface(graphics.shipShape[i - SHIP_HIT_INDEX]->w, graphics.shipShape[i- SHIP_HIT_INDEX]->h); + graphics.blit(graphics.shipShape[i - SHIP_HIT_INDEX], 0, 0, graphics.shipShape[i]); + hitRect = graphics.alphaRect(graphics.shipShape[i]->w, graphics.shipShape[i]->h, 255, 0, 0); + graphics.blit(hitRect, 0, 0, graphics.shipShape[i]); + SDL_SetColorKey(graphics.shipShape[i], (SDL_SRCCOLORKEY|SDL_RLEACCEL), SDL_MapRGB(graphics.shipShape[i]->format, 127, 0, 0)); + SDL_FreeSurface(hitRect); + } + + strcpy(string, "data/resources_all.dat"); + + #if USEPACK + dataLocation = locateDataInPak(string, 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen(string, "rb"); + #endif + + fscanf(fp, "%d %s", &index, string); + while (index != -1) + { + graphics.shape[index] = loadImage(string); + fscanf(fp, "%d %s", &index, string); + } + + fclose(fp); + + loadBackground((char *)systemBackground[currentGame.system]); + + setAlienShapes(); + + setWeaponShapes(); +} + +void loadSound() +{ + sound[SFX_EXPLOSION] = loadSound("sound/explode.wav"); + sound[SFX_HIT] = loadSound("sound/explode2.wav"); + sound[SFX_DEATH] = loadSound("sound/maledeath.wav"); + sound[SFX_MISSILE] = loadSound("sound/missile.wav"); + sound[SFX_PLASMA] = loadSound("sound/plasma.wav"); + sound[SFX_CLOCK] = loadSound("sound/clock.wav"); + sound[SFX_FLY] = loadSound("sound/flyby.wav"); + sound[SFX_ENERGYRAY] = loadSound("sound/beamLaser.wav"); + sound[SFX_PICKUP] = loadSound("sound/item.wav"); + sound[SFX_SHIELDUP] = loadSound("sound/shield.wav"); + sound[SFX_CLOAK] = loadSound("sound/cloak.wav"); + sound[SFX_DEBRIS] = loadSound("sound/explode3.wav"); + sound[SFX_DEBRIS2] = loadSound("sound/explode4.wav"); + sound[SFX_LASER] = loadSound("sound/laser.wav"); + sound[SFX_PLASMA2] = loadSound("sound/plasma2.wav"); + sound[SFX_PLASMA3] = loadSound("sound/plasma3.wav"); +} + +void freeSound() +{ + for (int i = 0 ; i < MAX_SOUNDS ; i++) + { + if (sound[i] != NULL) + Mix_FreeChunk(sound[i]); + } + + if (engine.music != NULL) + Mix_FreeMusic(engine.music); +} + + +void setFontColor(SDL_Surface *image, int red, int green, int blue) +{ + SDL_Color colors[256]; + colors[0].r = 0; + colors[0].g = 0; + colors[0].b = 0; + for (int i = 1 ; i < 256 ; i++) + { + colors[i].r = red; + colors[i].g = green; + colors[i].b = blue; + } + + SDL_SetPalette(image, SDL_LOGPAL|SDL_PHYSPAL, colors, 0, 256); +} + +/* +Custom loading to alter the font color before doing +all other things +*/ +void loadFont() +{ + SDL_Surface *image, *newImage; + + for (int i = 0 ; i < MAX_FONTSHAPES ; i++) + { + #if USEPACK + unpack("gfx/smallFont.bmp", PAK_FONT); + image = IMG_Load_RW(engine.sdlrw, 1); + #else + image = IMG_Load("gfx/smallFont.bmp"); + #endif + + if (image == NULL) { + printf("Couldn't load game font! (%s) Exitting.\n", SDL_GetError()); + exit(1); + } + + switch(i) + { + case 1: + setFontColor(image, 255, 0, 0); + break; + case 2: + setFontColor(image, 255, 255, 0); + break; + case 3: + setFontColor(image, 0, 255, 0); + break; + case 4: + setFontColor(image, 0, 255, 255); + break; + case 5: + setFontColor(image, 0, 0, 10); + break; + } + + newImage = SDL_DisplayFormat(image); + + graphics.fontShape[i] = graphics.setTransparent(newImage); + + SDL_FreeSurface(image); + } +} diff --git a/code/resources.h b/code/resources.h new file mode 100644 index 0000000..2f535b0 --- /dev/null +++ b/code/resources.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void unpack(char *file, signed char fileType); +extern SDL_Surface *loadImage(char *filename); +extern Mix_Chunk *loadSound(char *filename); +extern int locateDataInPak(char *file, signed char required); +extern void setAlienShapes(); +extern void setWeaponShapes(); +extern void loadGameGraphics(); + +extern globalEngineVariables engine; +extern object weapon[MAX_WEAPONS]; +extern Mix_Chunk *sound[MAX_SOUNDS]; +extern object enemy[MAX_ALIENS]; +extern Graphics graphics; +extern Game currentGame; diff --git a/code/script.cpp b/code/script.cpp new file mode 100644 index 0000000..e25c61e --- /dev/null +++ b/code/script.cpp @@ -0,0 +1,312 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "script.h" + +void setKlineGreeting() +{ + char greet[][50] = { + "How nice to see you again, Bainfield!", + "It all ends here, rebel!", + "I hope you won't disappoint me this time...", + "Do you really think you can defeat us?!" + }; + + gameEvent[0].time = 2; + gameEvent[0].face = FACE_KLINE; + strcpy(gameEvent[0].message, greet[rand() % 4]); + gameEvent[0].entity = -1; + gameEvent[0].flag = 0; +} + +void loadScriptEvents() +{ + for (int i = 0 ; i < 10 ; i++) + { + gameEvent[i].time = 0; + strcpy(gameEvent[i].message, ""); + gameEvent[i].entity = -1; + gameEvent[i].flag = 0; + } + + if ((currentGame.area == 27) && (enemy[WC_KLINE].classDef == CD_KLINE)) + setKlineGreeting(); + + char filename[255]; + strcpy(filename, ""); + sprintf(filename, "data/script%d.txt", currentGame.area); + + FILE *fp; + int i = 0; + + int time, entity, flags; + char face[255], message[255]; + + #if USEPACK + int dataLocation = locateDataInPak(filename, 0); + if (dataLocation == -1) + return; + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen(filename, "rb"); + if (fp == NULL) + return; + #endif + + fscanf(fp, "%d ", &time); + + while (time != 0) + { + fscanf(fp, "%s %d %d ", face, &entity, &flags); + fscanf(fp, "%[^\n]%*c", message); + + gameEvent[i].time = time; + gameEvent[i].face = getFace(face); + gameEvent[i].entity = entity; + gameEvent[i].flag = flags; + strcpy(gameEvent[i].message, message); + + i++; + + fscanf(fp, "%d ", &time); + } + + fclose(fp); +} + +void checkScriptEvents() +{ + for (int i = 0 ; i < 10 ; i++) + { + if (engine.timeTaken == gameEvent[i].time) + { + if (strcmp(gameEvent[i].message, "@none@") != 0) + { + setRadioMessage(gameEvent[i].face, gameEvent[i].message, 1); + } + + if (gameEvent[i].entity > -1) + { + if (gameEvent[i].flag != -FL_ACTIVATE) + { + enemy[gameEvent[i].entity].flags += gameEvent[i].flag; + } + else + { + enemy[gameEvent[i].entity].active = 1; + enemy[gameEvent[i].entity].x = Math::rrand((int)player.x + 400, (int)player.x + 800); + enemy[gameEvent[i].entity].y = Math::rrand((int)player.y - 400, (int)player.y + 800); + } + } + + gameEvent[i].time = 0; + } + } +} + +void syncScriptEvents() +{ + for (int i = 0 ; i < 10 ; i++) + { + if (gameEvent[i].time < 0) + { + gameEvent[i].time = engine.timeTaken + abs(gameEvent[i].time); + } + } +} + +void setScene(int scene) +{ + FILE *fp; + char string[255], face[255]; + float sx, sy, x, y, speed; + int index, shape; + + strcpy(string, ""); + sprintf(string, "data/cutscene%d.dat", scene); + + #if USEPACK + int dataLocation = locateDataInPak(string, 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen(string, "rb"); + #endif + + // Load in the specified background + fscanf(fp, "%s", string); + loadBackground(string); + + // Set the star speed + fscanf(fp, "%f %f", &sx, &sy); + engine.ssx = sx; + engine.ssy = sy; + + // Read in the specs for each ship + for (int i = 0 ; i < 15 ; i++) + { + fscanf(fp, "%d %d %f %f %f", &index, &shape, &x, &y, &speed); + + if (x < 0) x = (rand() % abs((int)x)); + if (y < 0) y = (rand() % abs((int)y)); + if (speed <= -1) speed = 1 + (rand() % abs((int)speed)); + + if (shape > -1) + { + enemy[index].image[0] = graphics.shipShape[shape]; + enemy[index].x = x; + enemy[index].y = y; + enemy[index].dx = speed; + enemy[index].active = 1; + } + } + + // And finally read in the messages + for (int i = 0 ; i < 10 ; i++) + { + fscanf(fp, "%s%*c", face); + fscanf(fp, "%[^\n]", string); + + if (strcmp(string, "@none@") == 0) + break; + + cutMessage[i].face = getFace(face); + strcpy(cutMessage[i].message, string); + } + + fclose(fp); +} + +void doCutscene(int scene) +{ + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + graphics.clearScreen(graphics.black); + SDL_Delay(1000); + + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + + engine.ssx = -0.5; + engine.ssy = 0; + + graphics.flushBuffer(); + graphics.freeGraphics(); + resetLists(); + loadGameGraphics(); + + for (int i = 0 ; i < 15 ; i++) + { + enemy[i] = defEnemy[0]; + enemy[i].face = 0; + enemy[i].active = 0; + } + + for (int i = 0 ; i < 10 ; i++) + { + strcpy(cutMessage[i].message, ""); + cutMessage[i].face = -1; + } + + setScene(scene); + + /* + Because we can fiddle with the images, we need to set the engines to + the correct places on the craft. Otherwise it will look wrong + */ + for (int i = 0 ; i < 15 ; i++) + { + enemy[i].engineX = enemy[i].image[0]->w; + enemy[i].engineY = (enemy[i].image[0]->h / 2); + } + + unsigned long frameLimit = SDL_GetTicks(); + signed char showMessage = 0; + signed char currentMessage = -1; + int timer = 60 * 4; + + graphics.drawBackGround(); + + SDL_Surface *face; + + flushInput(); + + while (true) + { + graphics.updateScreen(); + graphics.unBuffer(); + getPlayerInput(); + doStarfield(); + doExplosions(); + + for (int i = 0 ; i < 15 ; i++) + { + if (enemy[i].active) + { + addEngine(&enemy[i]); + enemy[i].x += enemy[i].dx; + enemy[i].x += engine.ssx; + graphics.blit(enemy[i].image[0], (int)enemy[i].x, (int)enemy[i].y); + if (enemy[i].x > 850) + { + enemy[i].x = -50; + enemy[i].y = rand() % 560; + } + } + } + + timer--; + if (timer == 0) + { + showMessage = 1 - showMessage; + timer = 120; + if (showMessage) + { + timer = 60 * 7; + currentMessage++; + + if (currentMessage == 10) + break; + + if (strcmp(cutMessage[currentMessage].message, "") == 0) + break; + + face = NULL; + if (cutMessage[currentMessage].face != -1) + face = graphics.shape[cutMessage[currentMessage].face]; + graphics.createMessageBox(face, cutMessage[currentMessage].message, 0); + } + } + + if ((showMessage) && (graphics.messageBox != NULL)) + graphics.blit(graphics.messageBox, (800 - graphics.messageBox->w) / 2, 500); + + while (SDL_GetTicks() < (frameLimit + 16)){} + frameLimit = SDL_GetTicks(); + + if (engine.keyState[SDLK_ESCAPE]) + break; + } + + graphics.flushBuffer(); + graphics.freeGraphics(); + graphics.clearScreen(graphics.black); + graphics.updateScreen(); +} diff --git a/code/script.h b/code/script.h new file mode 100644 index 0000000..f0443d4 --- /dev/null +++ b/code/script.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern void doStarfield(); +extern void getPlayerInput(); +extern void loadGameGraphics(); +extern void loadBackground(char *filename); +extern void setRadioMessage(signed char face, char *in, int priority); +extern void doExplosions(); +extern void addEngine(object *craft); +extern void doExplosions(); +extern void resetLists(); +extern int getFace(char *face); +extern int locateDataInPak(char *file, signed char required); +extern void flushInput(); + +extern Game currentGame; +extern object enemy[MAX_ALIENS]; +extern object player; +extern object defEnemy[MAX_DEFALIENS]; +extern globalEngineVariables engine; +extern Graphics graphics; +extern event gameEvent[10]; +extern cutMsg cutMessage[10]; diff --git a/code/shop.cpp b/code/shop.cpp new file mode 100644 index 0000000..0594030 --- /dev/null +++ b/code/shop.cpp @@ -0,0 +1,772 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "shop.h" + +void drawSecondaryWeaponSurface() +{ + char description[50]; + strcpy(description, ""); + + graphics.drawString("Secondary Weapon", 10, 3, FONT_WHITE, graphics.shopSurface[2]); + + switch (player.weaponType[1]) + { + case W_NONE: + strcpy(description, "Type : None"); + break; + case W_ROCKETS: + strcpy(description, "Type : Rockets"); + break; + case W_DOUBLE_ROCKETS: + strcpy(description, "Type : Dbl Rockets"); + break; + case W_MICRO_ROCKETS: + strcpy(description, "Type : Micro Rockets"); + break; + case W_LASER: + strcpy(description, "Type : Laser"); + break; + case W_CHARGER: + strcpy(description, "Type : Charger"); + break; + case W_HOMING_MISSILE: + strcpy(description, "Type : Homing Missile"); + break; + case W_DOUBLE_HOMING_MISSILES: + strcpy(description, "Type : Dbl Homing Missiles"); + break; + case W_MICRO_HOMING_MISSILES: + strcpy(description, "Type : Mcr Homing Missiles"); + break; + } + graphics.drawString(description, 10, 22, FONT_WHITE, graphics.shopSurface[2]); + + if ((player.weaponType[1] != W_LASER) && (player.weaponType[1] != W_CHARGER) && (player.weaponType[1] != W_NONE)) + { + sprintf(description, "Capacity : %d", currentGame.maxRocketAmmo); + graphics.drawString(description, 10, 37, FONT_WHITE, graphics.shopSurface[2]); + } +} + +void adjustShopPrices() +{ + shopItems[0].price = (500 * currentGame.maxPlasmaOutput); + shopItems[1].price = (500 * currentGame.maxPlasmaDamage); + shopItems[2].price = (500 * (16 - currentGame.maxPlasmaRate)); + + shopItems[5].price = (2000 * weapon[0].ammo[0]); + shopItems[6].price = (2000 * weapon[0].damage); + shopItems[7].price = (2000 * (16 - weapon[0].reload[0])); + + shopItems[8].price = (5 * currentGame.maxPlasmaAmmo); + shopItems[9].price = (25 * currentGame.maxRocketAmmo); + + if (currentGame.maxPlasmaOutput == 5) + shopItems[0].price = 0; + + if (currentGame.maxPlasmaDamage == 5) + shopItems[1].price = 0; + + if (currentGame.maxPlasmaRate == 7) + shopItems[2].price = 0; + + if (weapon[0].ammo[0] == 3) + shopItems[5].price = 0; + + if (weapon[0].damage == 3) + shopItems[6].price = 0; + + if (weapon[0].reload[0] == 11) + shopItems[7].price = 0; +} + +void drawShop() +{ + adjustShopPrices(); + + for (int i = 0 ; i < MAX_SHOPSHAPES ; i++) + { + if (graphics.shopSurface[i] != NULL) + { + SDL_FreeSurface(graphics.shopSurface[i]); + } + } + + for (int i = 0 ; i < 3 ; i++) + graphics.shopSurface[i] = graphics.createSurface(246, 91); + + for (int i = 0 ; i < 3 ; i++) + { + graphics.blevelRect(graphics.shopSurface[i], 0, 0, 245, 90, 0x00, 0x00, 0x55); + graphics.blevelRect(graphics.shopSurface[i], 0, 0, 245, 20, 0x00, 0x00, 0x99); + } + + graphics.shopSurface[4] = graphics.alphaRect(601, 101, 0x00, 0x00, 0x00); + graphics.blevelRect(graphics.shopSurface[4], 0, 0, 600, 100, 0x00, 0x00, 0x33); + + switch (shopSelectedItem) + { + case -1: + case -2: + case -3: + case -4: + case -5: + case -6: + break; + case 0: + case 1: + case 2: + case 8: + graphics.blevelRect(graphics.shopSurface[1], 0, 0, 245, 90, 0x55, 0x00, 0x00); + graphics.blevelRect(graphics.shopSurface[1], 0, 0, 245, 20, 0x99, 0x00, 0x00); + break; + case 3: + case 4: + graphics.blevelRect(graphics.shopSurface[4], 0, 0, 600, 100, 0x33, 0x00, 0x00); + break; + case 5: + case 6: + case 7: + graphics.blevelRect(graphics.shopSurface[0], 0, 0, 245, 90, 0x55, 0x00, 0x00); + graphics.blevelRect(graphics.shopSurface[0], 0, 0, 245, 20, 0x99, 0x00, 0x00); + break; + default: + graphics.blevelRect(graphics.shopSurface[2], 0, 0, 245, 90, 0x55, 0x00, 0x00); + graphics.blevelRect(graphics.shopSurface[2], 0, 0, 245, 20, 0x99, 0x00, 0x00); + break; + } + + char description[100]; + strcpy(description, ""); + + graphics.drawString("Primary Weapon", 10, 3, FONT_WHITE, graphics.shopSurface[0]); + sprintf(description, "Plasma Cannons : %d", weapon[0].ammo[0]); + graphics.drawString(description, 10, 22, FONT_WHITE, graphics.shopSurface[0]); + sprintf(description, "Plasma Power : Stage %d", weapon[0].damage); + graphics.drawString(description, 10, 37, FONT_WHITE, graphics.shopSurface[0]); + sprintf(description, "Cooler : Stage %d", ((15 - weapon[0].reload[0]) / 2) + 1); + graphics.drawString(description, 10, 52, FONT_WHITE, graphics.shopSurface[0]); + + graphics.drawString("Powerup Weapon", 10, 3, FONT_WHITE, graphics.shopSurface[1]); + sprintf(description, "Plasma Output : Stage %d", currentGame.maxPlasmaOutput); + graphics.drawString(description, 10, 22, FONT_WHITE, graphics.shopSurface[1]); + sprintf(description, "Plasma Condensor : Stage %d", currentGame.maxPlasmaDamage); + graphics.drawString(description, 10, 37, FONT_WHITE, graphics.shopSurface[1]); + sprintf(description, "Liquid Nitrogen : Stage %d", ((15 - currentGame.maxPlasmaRate) / 2) + 1); + graphics.drawString(description, 10, 52, FONT_WHITE, graphics.shopSurface[1]); + sprintf(description, "Plasma Capacity : %d", currentGame.maxPlasmaAmmo); + graphics.drawString(description, 10, 67, FONT_WHITE, graphics.shopSurface[1]); + + drawSecondaryWeaponSurface(); + + graphics.shopSurface[3] = graphics.createSurface(601, 121); + + graphics.blevelRect(graphics.shopSurface[3], 0, 0, 600, 120, 0x00, 0x00, 0x22); + + graphics.drawString("Temporary Weapons", 10, 2, FONT_WHITE, graphics.shopSurface[3]); + graphics.drawString("Ammo and Storage", 260, 2, FONT_WHITE, graphics.shopSurface[3]); + + graphics.drawString("Primary Weapons", 10, 62, FONT_WHITE, graphics.shopSurface[3]); + + graphics.drawString("Secondary Weapons", 260, 62, FONT_WHITE, graphics.shopSurface[3]); + + signed char icons = MAX_SHOPITEMS; + + if (currentGame.system == 0) + icons = 11; + else if (currentGame.system == 1) + icons = 13; + else if (currentGame.system == 2) + icons = 15; + + for (int i = 0 ; i < icons ; i++) + graphics.blit(graphics.shape[shopItems[i].image], shopItems[i].x - 90, shopItems[i].y - 178, graphics.shopSurface[3]); + + sprintf(description, "Shield Units : %d", currentGame.shieldUnits * 25); + graphics.drawString(description, 10, 4, FONT_WHITE, graphics.shopSurface[4]); + sprintf(description, "Cash : $%d", currentGame.cash); + graphics.drawString(description, 10, 80, FONT_WHITE, graphics.shopSurface[4]); + sprintf(description, "Plasma Cells : %.3d", player.ammo[0]); + graphics.drawString(description, 430, 4, FONT_WHITE, graphics.shopSurface[4]); + sprintf(description, "Rockets : %.3d", player.ammo[1]); + graphics.drawString(description, 475, 80, FONT_WHITE, graphics.shopSurface[4]); + + graphics.shopSurface[5] = graphics.createSurface(601, 56); + graphics.blevelRect(graphics.shopSurface[5], 0, 0, 600, 35, 0x00, 0x99, 0x00); + graphics.blevelRect(graphics.shopSurface[5], 0, 20, 600, 35, 0x00, 0x33, 0x00); + graphics.drawString("Information", 5, 4, FONT_WHITE, graphics.shopSurface[5]); + + switch (shopSelectedItem) + { + case -1: + break; + case -2: + graphics.drawString("You don't have enough money", 20, 30, FONT_WHITE, graphics.shopSurface[5]); + break; + case -3: + graphics.drawString("Cannot upgrade ship", 5, 22, FONT_WHITE, graphics.shopSurface[5]); + graphics.drawString("Hardware capacity has been reached", 20, 38, FONT_CYAN, graphics.shopSurface[5]); + break; + case -4: + graphics.drawString("Ammunition limit reached", 20, 30, FONT_WHITE, graphics.shopSurface[5]); + break; + case -5: + graphics.drawString("You cannot sell that item", 20, 30, FONT_WHITE, graphics.shopSurface[5]); + break; + case -6: + graphics.drawString("Nothing to sell", 20, 30, FONT_WHITE, graphics.shopSurface[5]); + break; + case -7: + graphics.drawString("Rockets cannot be bought for Laser or Charger Cannon", 5, 30, FONT_WHITE, graphics.shopSurface[5]); + break; + case -8: + graphics.drawString("You already have that weapon", 20, 30, FONT_WHITE, graphics.shopSurface[5]); + break; + case -9: + graphics.drawString("This weapon's ammo limit has been reached", 20, 30, FONT_WHITE, graphics.shopSurface[5]); + break; + default: + if (shopItems[shopSelectedItem].price != 0) + { + sprintf(description, "%s ($%d)", shopItems[shopSelectedItem].description, shopItems[shopSelectedItem].price); + } + else + { + sprintf(description, "%s (N/A)", shopItems[shopSelectedItem].description); + } + graphics.drawString(shopItems[shopSelectedItem].name, 5, 22, FONT_WHITE, graphics.shopSurface[5]); + graphics.drawString(description, 20, 38, FONT_CYAN, graphics.shopSurface[5]); + break; + } +} + +#if USEPACK + +void loadShop() +{ + char name[255], description[255]; + int price, image, x, y; + + FILE *fp; + + #if USEPACK + int dataLocation = locateDataInPak("data/shop.dat", 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen("data/shop.dat", "rb"); + #endif + + for (int i = 0 ; i < MAX_SHOPITEMS ; i++) + { + fscanf(fp, "%[^\n]%*c", name); + fscanf(fp, "%[^\n]%*c", description); + fscanf(fp, "%d", &price); + fscanf(fp, "%d", &image); + fscanf(fp, "%d", &x); + fscanf(fp, "%d%*c", &y); + + strcpy(shopItems[i].name, name); + strcpy(shopItems[i].description, description); + shopItems[i].price = price; + shopItems[i].image = image; + shopItems[i].x = x; + shopItems[i].y = y; + } + + fclose(fp); + + shopSelectedItem = -1; + + player.image[0] = graphics.shape[0]; + player.x = 380; + player.y = 95; + + drawShop(); +} + +void initShop(){loadShop();} + +#else + +void saveShop() +{ + FILE *fp; + + fp = fopen("data/shop.dat", "wb"); + if (fp == NULL) + { + printf("Unable to write Shop Data File\n"); + exit(1); + } + + for (int i = 0 ; i < MAX_SHOPITEMS ; i++) + { + fprintf(fp, "%s\n", shopItems[i].name); + fprintf(fp, "%s\n", shopItems[i].description); + fprintf(fp, "%d ", shopItems[i].price); + fprintf(fp, "%d ", shopItems[i].image); + fprintf(fp, "%d ", shopItems[i].x); + fprintf(fp, "%d\n", shopItems[i].y); + } + + // Put an extra line for the PAK file "just in case" + fprintf(fp, "\n"); + + fclose(fp); +} + +/* +Throw into a data file in final build +*/ +void initShop() +{ + /* ----------- Temporary Items ----------- */ + + shopItems[0].price = 1000; + strcpy(shopItems[0].name, "Plasma channel splitter"); + strcpy(shopItems[0].description, "Improves poweredup plasma output"); + shopItems[0].image = 9; + + shopItems[1].price = 1000; + strcpy(shopItems[1].name, "Plasma capacity condensor"); + strcpy(shopItems[1].description, "Increases poweredup plasma damage"); + shopItems[1].image = 10; + + shopItems[2].price = 1000; + strcpy(shopItems[2].name, "Liquid nitrogen capsules"); + strcpy(shopItems[2].description, "Increases plasma firing rate"); + shopItems[2].image = 11; + + shopItems[3].price = 50; + strcpy(shopItems[3].name, "10 Plasma cells"); + strcpy(shopItems[3].description, "Plasma ammunition"); + shopItems[3].image = 12; + + shopItems[4].price = 50; + strcpy(shopItems[4].name, "Rocket Ammo"); + strcpy(shopItems[4].description, "High velocity dumb fire rocket"); + shopItems[4].image = 13; + + /* ----------- Permanent Items ----------- */ + + shopItems[5].price = 2000; + strcpy(shopItems[5].name, "Additional Plasma Cannon"); + strcpy(shopItems[5].description, "Adds an extra plasma cannon to the Firefly"); + shopItems[5].image = 14; + + shopItems[6].price = 2000; + strcpy(shopItems[6].name, "Plasma Power Booster"); + strcpy(shopItems[6].description, "Increases power of plasma shots"); + shopItems[6].image = 15; + + shopItems[7].price = 2000; + strcpy(shopItems[7].name, "Plasma Cooling Booster"); + strcpy(shopItems[7].description, "Permanently increases firing rate"); + shopItems[7].image = 16; + + /* ----------- Ammo Items -------------- */ + + shopItems[8].price = 250; + strcpy(shopItems[8].name, "Plasma compressor"); + strcpy(shopItems[8].description, "Increases plasma ammo capacity"); + shopItems[8].image = 17; + + shopItems[9].price = 250; + strcpy(shopItems[9].name, "Rocket Pod"); + strcpy(shopItems[9].description, "Allows for an additional 5 rockets to be carried"); + shopItems[9].image = 18; + + /* ---------- Weaponary --------------- */ + + shopItems[10].price = 2000; + strcpy(shopItems[10].name, "Dual Rocket Launcher"); + strcpy(shopItems[10].description, "Allows for two rockets to be fired at once"); + shopItems[10].image = 19; + + shopItems[11].price = 2500; + strcpy(shopItems[11].name, "Micro Rocket Launcher"); + strcpy(shopItems[11].description, "Launches several less powerful rockets at once"); + shopItems[11].image = 20; + + shopItems[12].price = 5000; + strcpy(shopItems[12].name, "Laser Cannon"); + strcpy(shopItems[12].description, "Laser Cannon"); + shopItems[12].image = 21; + + shopItems[13].price = 7500; + strcpy(shopItems[13].name, "Homing Missile Launcher"); + strcpy(shopItems[13].description, "Fires homing missile"); + shopItems[13].image = 22; + + shopItems[14].price = 10000; + strcpy(shopItems[14].name, "Charge Cannon"); + strcpy(shopItems[14].description, "A charge up cannon"); + shopItems[14].image = 23; + + shopItems[15].price = 20000; + strcpy(shopItems[15].name, "Dual Homing Missile Launcher"); + strcpy(shopItems[15].description, "Fires two homing missiles"); + shopItems[15].image = 24; + + shopItems[16].price = 25000; + strcpy(shopItems[16].name, "Homing Micro Missile Launcher"); + strcpy(shopItems[16].description, "Fires several small homing missiles"); + shopItems[16].image = 25; + + for (int i = 0 ; i < 3 ; i++) + { + shopItems[i].x = 100 + (i * 50); + shopItems[i].y = 200; + } + + shopItems[3].x = 350; + shopItems[3].y = 200; + + shopItems[4].x = 400; + shopItems[4].y = 200; + + for (int i = 0 ; i < 3 ; i++) + { + shopItems[i + 5].x = 100 + (i * 50); + shopItems[i + 5].y = 260; + } + + for (int i = 0 ; i < 2 ; i++) + { + shopItems[i + 8].x = 450 + (i * 50); + shopItems[i + 8].y = 200; + } + + for (int i = 0 ; i < 8 ; i++) + { + shopItems[i + 10].x = 350 + (i * 50); + shopItems[i + 10].y = 260; + } + + shopSelectedItem = -1; + + player.image[0] = graphics.shape[0]; + player.x = 380; + player.y = 95; + + saveShop(); + + drawShop(); +} + +#endif + +void buy(int i) +{ + if ((currentGame.cash < shopItems[i].price) && (!engine.cheatCash)) + { + shopSelectedItem = -2; + drawShop(); + return; + } + + switch(i) + { + case 0: + if (currentGame.maxPlasmaOutput == 5) + {shopSelectedItem = -3; return;} + currentGame.maxPlasmaOutput++; + break; + case 1: + if (currentGame.maxPlasmaDamage == 5) + {shopSelectedItem = -3; return;} + currentGame.maxPlasmaDamage++; + break; + case 2: + if (currentGame.maxPlasmaRate == 7) + {shopSelectedItem = -3; return;} + currentGame.maxPlasmaRate -= 2; + break; + case 3: + if (player.ammo[0] == currentGame.maxPlasmaAmmo) + {shopSelectedItem = -4; return;} + Math::limitChar(&(player.ammo[0] += 10), 0, currentGame.maxPlasmaAmmo); + break; + case 4: + if ((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER)) + {shopSelectedItem = -7; return;} + if (player.ammo[1] == currentGame.maxRocketAmmo) + {shopSelectedItem = -4; return;} + if ((player.weaponType[1] == W_HOMING_MISSILE) && (player.ammo[1] == 20)) + {shopSelectedItem = -9; return;} + if ((player.weaponType[1] == W_DOUBLE_HOMING_MISSILES) && (player.ammo[1] == 10)) + {shopSelectedItem = -9; return;} + if ((player.weaponType[1] == W_MICRO_HOMING_MISSILES) && (player.ammo[1] == 10)) + {shopSelectedItem = -9; return;} + player.ammo[1]++; + break; + case 5: + if (weapon[0].ammo[0] == 3) + {shopSelectedItem = -3; return;} + weapon[0].ammo[0]++; + if (currentGame.maxPlasmaOutput < weapon[0].ammo[0]) + currentGame.maxPlasmaOutput = weapon[0].ammo[0]; + break; + case 6: + if (weapon[0].damage == 3) + {shopSelectedItem = -3; return;} + weapon[0].damage++; + if (currentGame.maxPlasmaDamage < weapon[0].damage) + currentGame.maxPlasmaDamage = weapon[0].damage; + break; + case 7: + if (weapon[0].reload[0] == 11) + {shopSelectedItem = -3; return;} + weapon[0].reload[0] -= 2; + if (currentGame.maxPlasmaRate > weapon[0].reload[0]) + currentGame.maxPlasmaRate = weapon[0].reload[0]; + break; + case 8: + if (currentGame.maxPlasmaAmmo == 250) + {shopSelectedItem = -3; return;} + Math::limitChar(&(currentGame.maxPlasmaAmmo += 10), 0, 250); + break; + case 9: + if ((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER)) + {shopSelectedItem = -7; return;} + if (currentGame.maxRocketAmmo == 50) + {shopSelectedItem = -3; return;} + currentGame.maxRocketAmmo += 5; + break; + case 10: + if (player.weaponType[1] == W_DOUBLE_ROCKETS) + {shopSelectedItem = -8; return;} + player.weaponType[1] = W_DOUBLE_ROCKETS; + shopSelectedItem = -1; + break; + case 11: + if (player.weaponType[1] == W_MICRO_ROCKETS) + {shopSelectedItem = -8; return;} + player.weaponType[1] = W_MICRO_ROCKETS; + shopSelectedItem = -1; + break; + case 12: + if (player.weaponType[1] == W_LASER) + {shopSelectedItem = -8; return;} + player.weaponType[1] = W_LASER; + player.ammo[1] = 0; + shopSelectedItem = -1; + break; + case 13: + if (player.weaponType[1] == W_HOMING_MISSILE) + {shopSelectedItem = -8; return;} + player.weaponType[1] = W_HOMING_MISSILE; + Math::limitChar(&player.ammo[1], 0, 20); + shopSelectedItem = -1; + break; + case 14: + if (player.weaponType[1] == W_CHARGER) + {shopSelectedItem = -8; return;} + player.weaponType[1] = W_CHARGER; + player.ammo[1] = 0; + shopSelectedItem = -1; + break; + case 15: + if (player.weaponType[1] == W_DOUBLE_HOMING_MISSILES) + {shopSelectedItem = -8; return;} + player.weaponType[1] = W_DOUBLE_HOMING_MISSILES; + Math::limitChar(&player.ammo[1], 0, 10); + shopSelectedItem = -1; + break; + case 16: + if (player.weaponType[1] == W_MICRO_HOMING_MISSILES) + {shopSelectedItem = -8; return;} + player.weaponType[1] = W_MICRO_HOMING_MISSILES; + Math::limitChar(&player.ammo[1], 0, 10); + shopSelectedItem = -1; + break; + } + + if (!engine.cheatCash) + currentGame.cash -= shopItems[i].price; +} + +void sell(int i) +{ + switch (i) + { + case 0: + if (currentGame.maxPlasmaOutput == 2) + {shopSelectedItem = -5; return;} + currentGame.maxPlasmaOutput--; + break; + case 1: + if (currentGame.maxPlasmaDamage == 2) + {shopSelectedItem = -5; return;} + currentGame.maxPlasmaDamage--; + break; + case 2: + if (currentGame.maxPlasmaRate == 13) + {shopSelectedItem = -5; return;} + currentGame.maxPlasmaRate += 2; + break; + case 3: + if (player.ammo[0] == 0) + {shopSelectedItem = -6; return;} + if (player.ammo[0] > 9) + Math::limitChar(&(player.ammo[0] -= 10), 0, currentGame.maxPlasmaAmmo); + else + player.ammo[0] = 0; + break; + case 4: + if (player.ammo[1] == 0) + {shopSelectedItem = -6; return;} + player.ammo[1]--; + break; + case 5: + if (weapon[0].ammo[0] == 1) + {shopSelectedItem = -5; return;} + weapon[0].ammo[0]--; + break; + case 6: + if (weapon[0].damage == 1) + {shopSelectedItem = -5; return;} + weapon[0].damage--; + break; + case 7: + if (weapon[0].reload[0] == 15) + {shopSelectedItem = -5; return;} + weapon[0].reload[0] += 2; + break; + case 8: + if (currentGame.maxPlasmaAmmo == 100) + {shopSelectedItem = -1; return;} + currentGame.maxPlasmaAmmo -= 10; + Math::limitChar(&player.ammo[0], 0, currentGame.maxPlasmaAmmo); + break; + case 9: + if (currentGame.maxRocketAmmo == 0) + {shopSelectedItem = -1; return;} + currentGame.maxRocketAmmo -= 5; + Math::limitChar(&player.ammo[1], 0, currentGame.maxRocketAmmo); + break; + case 10: + if (player.weaponType[1] != W_DOUBLE_ROCKETS) + {shopSelectedItem = -1; return;} + player.weaponType[1] = W_NONE; + shopSelectedItem = -1; + break; + case 11: + if (player.weaponType[1] != W_MICRO_ROCKETS) + {shopSelectedItem = -1; return;} + player.weaponType[1] = W_NONE; + shopSelectedItem = -1; + break; + case 12: + if (player.weaponType[1] != W_LASER) + {shopSelectedItem = -1; return;} + player.weaponType[1] = W_NONE; + player.ammo[1] = 0; + shopSelectedItem = -1; + break; + case 13: + if (player.weaponType[1] != W_HOMING_MISSILE) + {shopSelectedItem = -1; return;} + player.weaponType[1] = W_NONE; + shopSelectedItem = -1; + break; + case 14: + if (player.weaponType[1] != W_CHARGER) + {shopSelectedItem = -1; return;} + player.weaponType[1] = W_NONE; + player.ammo[1] = 0; + shopSelectedItem = -1; + break; + case 15: + if (player.weaponType[1] != W_DOUBLE_HOMING_MISSILES) + {shopSelectedItem = -1; return;} + player.weaponType[1] = W_NONE; + shopSelectedItem = -1; + break; + case 16: + if (player.weaponType[1] != W_MICRO_HOMING_MISSILES) + {shopSelectedItem = -1; return;} + player.weaponType[1] = W_NONE; + shopSelectedItem = -1; + break; + } + + currentGame.cash += (shopItems[i].price / 2); +} + +void showShop() +{ + graphics.blit(graphics.shopSurface[0], 20, 395); + graphics.blit(graphics.shopSurface[1], 275, 395); + graphics.blit(graphics.shopSurface[2], 530, 395); + graphics.blit(graphics.shopSurface[3], 100, 180); + graphics.blit(graphics.shopSurface[4], 100, 50); + graphics.blit(graphics.shopSurface[5], 100, 320); + + if (shopSelectedItem > -1) + { + graphics.blit(graphics.shape[27], 60, 350); + graphics.blit(graphics.shape[28], 710, 350); + } + + graphics.blit(graphics.shape[29], (int)player.x, (int)player.y); + + signed char icons = MAX_SHOPITEMS; + + if (currentGame.system == 0) + icons = 11; + else if (currentGame.system == 1) + icons = 13; + else if (currentGame.system == 2) + icons = 15; + + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL])) + { + for (int i = 0 ; i < icons ; i++) + { + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, shopItems[i].x, shopItems[i].y, 32, 25)) + { + shopSelectedItem = i; + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + drawShop(); + } + } + + if (shopSelectedItem > -1) + { + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 60, 350, 24, 16)) + { + buy(shopSelectedItem); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + drawShop(); + } + + if (Collision::collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 700, 350, 24, 16)) + { + sell(shopSelectedItem); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + drawShop(); + } + } + } +} + diff --git a/code/shop.h b/code/shop.h new file mode 100644 index 0000000..27c25e4 --- /dev/null +++ b/code/shop.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern int locateDataInPak(char *file, signed char required); + +extern object player; +extern globalEngineVariables engine; +extern object weapon[MAX_WEAPONS]; +extern Game currentGame; +extern Graphics graphics; +extern devVariables dev; +extern ShopItem shopItems[MAX_SHOPITEMS]; + +signed char shopSelectedItem; + diff --git a/code/structs.h b/code/structs.h new file mode 100644 index 0000000..ea1aa7b --- /dev/null +++ b/code/structs.h @@ -0,0 +1,306 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +typedef struct object { + + signed char active; + signed char classDef; // Used by aliens to determine what they are + signed char AIType; // Type of articifial intelligence + + signed char id; // The "job" of the object + object *target; // index target in enemy array + + signed char reload[2]; + + int systemPower; // computer systems for craft + int shield; // current shield + int maxShield; // max shield (for recharging) + int deathCounter; // how long to explode for + + signed char speed; + unsigned char damage; // Contact damage for bullets + int score; // How much a kill of this is worth + unsigned char ammo[2]; // Ammo for 2nd weapon. Max of 100 (except laser) + + signed char face; // Either 0 or 1 + + object *owner; // Who owns this object + + int chance[2]; // Chance of using the weapons (out of 1000) + + SDL_Surface *image[2]; // For facing left and right + unsigned char imageIndex[2]; // used for loading + signed char hit; // used to make a craft "flash" if it is struck by a shot + + int engineX; // The place for the engine on the other side of the craft + int engineY; // The middle of the engine on the craft + + int thinktime; // When the object will next react + signed char weaponType[2]; // Weapon types + + signed char collectChance; // Chance of dropping the object + signed char collectType; // What the object is carrying + unsigned char collectValue; // What it is worth + + int flags; // Various flags for an object + + float x, y, dx, dy; + + object *next; + +} object; + +typedef struct mission { + + char primaryObjective[3][50]; // Description + signed char primaryType[3]; // The type of mission this is + signed char target1[3]; // index of target in enemy array + int targetValue1[3]; // Number of things to collect (slaves, cash, etc) + signed char timeLimit1[3]; // In minutes + int completed1[3]; + + char secondaryObjective[3][50]; // Description + signed char secondaryType[3]; // The type of mission this is + signed char target2[3]; // index of target in enemy array + int targetValue2[3]; // Number of things to collect (slaves, cash, etc) + signed char timeLimit2[3]; // In minutes + int completed2[3]; + + signed char remainingObjectives1; + signed char remainingObjectives2; + int addAliens; // How often to add new enemies + +}; + +typedef struct Star { + + float x, y, dx, dy; + signed char speed; // How fast the star moves + +}; + +typedef struct collectables { + + signed char active; + float x, y, dx, dy; + SDL_Surface *image; + signed char type; // What kind of collectable is it? + unsigned char value; // How much is it worth? + int life; // How long it will stay around for + + collectables *next; + +}; + +typedef struct textObject { + + SDL_Surface *image; + unsigned char life; + float x, y; + signed char fontColor; + char text[255]; + +}; + +typedef struct Game { + + object thePlayer; + object playerWeapon; + object playerWeapon2; + + unsigned char system; + unsigned char area; + unsigned char musicVolume; + unsigned char sfxVolume; + + signed char fullScreen; + signed char useMusic; + signed char useSound; + signed char autoSaveSlot; + + unsigned int cash; + unsigned int cashEarned; + unsigned int shots; + unsigned int hits; + unsigned char accuracy; + unsigned char hasWingMate1, hasWingMate2; + unsigned int totalKills, wingMate1Kills, wingMate2Kills; + unsigned char wingMate1Ejects, wingMate2Ejects; + unsigned int totalOtherKills; + unsigned char secondaryMissions, secondaryMissionsCompleted; + unsigned int shieldPickups, rocketPickups, cellPickups, powerups, minesKilled, cargoPickups; + + // slaves for Eyananth + unsigned int slavesRescued; + + // remaining shield for experimental fighter + unsigned int experimentalShield; + + unsigned int timeTaken; // In seconds + unsigned char missionCompleted[10]; + + signed char stationedPlanet; + signed char destinationPlanet; + + char stationedName[20]; + char destinationName[20]; + int distanceCovered; + + unsigned char maxPlasmaRate; + unsigned char maxPlasmaDamage; + unsigned char maxPlasmaOutput; + unsigned char maxPlasmaAmmo; + unsigned char maxRocketAmmo; + unsigned char shieldUnits; + +}; + +typedef struct ShopItem { + + int x, y; + unsigned int price; + char name[50]; + char description[255]; + unsigned char image; +}; + +typedef struct bRect { + + int x, y, w, h; + bRect *next; + +} bRect; + +typedef struct Planet { + + int y; + char name[50]; + SDL_Surface *image; + + signed char missionNumber; // associated mission number + signed char missionCompleted; // whether it has been completed + + signed char messageMission; + signed char messageSlot; + signed char faceImage; + char from[50]; + char subject[100]; +}; + + +typedef struct globalEngineVariables { + + SDL_Event event; + signed char done; + + SDL_RWops *sdlrw; + + float musicVolume; + + signed char maxAliens; + + float ssx; + float ssy; + + Mix_Music *music; + + object *bulletHead; + object *bulletTail; + object *explosionHead; + object *explosionTail; + collectables *collectableHead; + collectables *collectableTail; + object *debrisHead; + object *debrisTail; + + int cursor_x, cursor_y; + + signed char commsSection; + + signed char eventTimer; + + signed char lowShield; + signed char averageShield; + + float targetShield; + signed char targetIndex; + signed char targetArrow; + int targetArrowTimer; + + // Mission completion timer (allows for 4 seconds before leaving sector) + unsigned long missionCompleteTimer; + + // Times the mission normally + unsigned int counter2; + int timeTaken; // In seconds + + // For missions with a time limit + signed char timeMission; + unsigned int counter; + signed char seconds; + signed char minutes; + + // Mission Related stuff + signed char allAliensDead; + int addAliens; + + signed char paused; + signed char gameSection; + + signed char useAudio; + + // This really only applies to Linux users. + char userHomeDirectory[1024]; + + char keyState[350]; + + signed char cheat; // overall cheat + signed char cheatShield; + signed char cheatCash; + signed char cheatAmmo; + signed char cheatTime; + signed char cheatCredits; +}; + +typedef struct event { + + int time; + char message[255]; + signed char face; + signed char entity; + int flag; +}; + +typedef struct cutMsg { + + int face; + char message[255]; + +}; + +typedef struct devVariables { + + signed char moveAliens; + signed char fireAliens; + +}; + + + diff --git a/code/title.cpp b/code/title.cpp new file mode 100644 index 0000000..9a22b5d --- /dev/null +++ b/code/title.cpp @@ -0,0 +1,738 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "version.h" +#include "title.h" + +signed char showGameMenu(signed char continueSaveSlot) +{ + graphics.blitText(2); + if (continueSaveSlot != 0) + { + graphics.blitText(3); + graphics.blitText(4); + } + graphics.blitText(5); + if (engine.cheat) + { + graphics.textShape[7].y = 450; + graphics.blitText(6); + } + else + { + graphics.textShape[7].y = 430; + } + graphics.blitText(7); + + if (engine.cheat) + return 6; + + return 5; +} + +signed char showLoadMenu() +{ + signed char rtn = 1; + + for (int i = 13 ; i < 18 ; i++) + { + if (graphics.textShape[i].image != NULL) + { + graphics.blitText(i); + rtn++; + graphics.textShape[12].y = graphics.textShape[i].y + 40; + } + } + graphics.blitText(12); + + return rtn; +} + +void createOptionsMenu() +{ + if (currentGame.useSound) + graphics.textSurface(8, "SOUND - ON", -1, 350, FONT_WHITE); + else + graphics.textSurface(8, "SOUND - OFF", -1, 350, FONT_WHITE); + + if (currentGame.useMusic) + graphics.textSurface(9, "MUSIC - ON", -1, 370, FONT_WHITE); + else + graphics.textSurface(9, "MUSIC - OFF", -1, 370, FONT_WHITE); + + if (currentGame.fullScreen) + graphics.textSurface(10, "FULLSCREEN - ON", -1, 390, FONT_WHITE); + else + graphics.textSurface(10, "FULLSCREEN - OFF", -1, 390, FONT_WHITE); + + char string[50]; + strcpy(string, "AUTO SAVE SLOT - NONE"); + if (currentGame.autoSaveSlot > -1) + sprintf(string, "AUTO SAVE SLOT - #%d", currentGame.autoSaveSlot + 1); + graphics.textSurface(11, string, -1, 410, FONT_WHITE); +} + +signed char showOptionsMenu() +{ + graphics.textShape[12].y = 450; + + graphics.blitText(8); + graphics.blitText(9); + graphics.blitText(10); + graphics.blitText(11); + graphics.blitText(12); + + return 5; +} + +void createCheatMenu() +{ + if (engine.cheatShield) + graphics.textSurface(18, "UNLIMITED SHIELD - ON", -1, 350, FONT_WHITE); + else + graphics.textSurface(18, "UNLIMITED SHIELD - OFF", -1, 350, FONT_WHITE); + + if (engine.cheatAmmo) + graphics.textSurface(19, "UNLIMITED AMMO - ON", -1, 370, FONT_WHITE); + else + graphics.textSurface(19, "UNLIMITED AMMO - OFF", -1, 370, FONT_WHITE); + + if (engine.cheatCash) + graphics.textSurface(20, "UNLIMITED CASH - ON", -1, 390, FONT_WHITE); + else + graphics.textSurface(20, "UNLIMITED CASH - OFF", -1, 390, FONT_WHITE); + + if (engine.cheatTime) + graphics.textSurface(21, "UNLIMITED TIME - ON", -1, 410, FONT_WHITE); + else + graphics.textSurface(21, "UNLIMITED TIME - OFF", -1, 410, FONT_WHITE); +} + +signed char showCheatMenu() +{ + graphics.textShape[12].y = 450; + + graphics.blitText(18); + graphics.blitText(19); + graphics.blitText(20); + graphics.blitText(21); + graphics.blitText(12); + + return 5; +} + +/* +This is the main title screen, with the stars whirling past and the +"Parallel Realities, Present..." text. Nothing too special. +*/ +int doTitle() +{ + newGame(); + + engine.gameSection = SECTION_TITLE; + + graphics.flushBuffer(); + graphics.freeGraphics(); + resetLists(); + + // required to stop the title screen crashing + currentGame.system = 0; + currentGame.area = 0; + + loadGameGraphics(); + + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + graphics.clearScreen(graphics.black); + SDL_Delay(1000); + + signed char continueSaveSlot = initSaveSlots(); + + loadMusic("music/Platinum.mod"); + + loadBackground("gfx/spirit.jpg"); + + SDL_Surface *prlogo, *sflogo; + prlogo = loadImage("gfx/prlogo.gif"); + sflogo = loadImage("gfx/sflogo.gif"); + + int prx = ((800 - prlogo->w) / 2); + int pry = ((600 - prlogo->h) / 2); + + int sfx = ((800 - sflogo->w) / 2); + int sfy = ((600 - sflogo->h) / 2); + + graphics.textSurface(0, "PRESENTS", -1, 300, FONT_WHITE); + graphics.textSurface(1, "AN SDL GAME", -1, 300, FONT_WHITE); + graphics.textSurface(2, "START NEW GAME", -1, 350, FONT_WHITE); + graphics.textSurface(3, "LOAD GAME", -1, 370, FONT_WHITE); + graphics.textSurface(4, "CONTINUE CURRENT GAME", -1, 390, FONT_WHITE); + graphics.textSurface(5, "OPTIONS", -1, 410, FONT_WHITE); + graphics.textSurface(6, "CHEAT OPTIONS", -1, 430, FONT_WHITE); + graphics.textSurface(7, "QUIT", -1, 430, FONT_WHITE); + + createOptionsMenu(); + graphics.textSurface(12, "BACK TO MAIN MENU", -1, 0, FONT_WHITE); + + createCheatMenu(); + + // Set the star motion + engine.ssx = -0.5; + engine.ssy = 0; + + int then = SDL_GetTicks(); + int now; + + for (int i = 0 ; i < 15 ; i++) + { + enemy[i] = defEnemy[rand() % 3]; + if ((rand() % 5) == 0) + enemy[i] = defEnemy[CD_TRANSPORTSHIP]; + if ((rand() % 5) == 0) + enemy[i] = defEnemy[CD_MINER]; + enemy[i].x = rand() % 800; + enemy[i].y = rand() % 560; + enemy[i].dx = 1 + rand() % 3; + enemy[i].face = 0; + } + + int redGlow = 255; + signed char redDir = -2; + char buildVersion[25]; + sprintf(buildVersion, "Version "VERSION); + + SDL_Rect optionRec; + + optionRec.x = 290; + optionRec.y = 345; + optionRec.h = 22; + optionRec.w = 215; + + signed char selectedOption = 1; + if (continueSaveSlot > 0) + {selectedOption = 3; optionRec.y += 40;} + + signed char skip = 0; + signed char listLength = 5; // menu list length + signed char menuType = 0; + + graphics.drawBackGround(); + unsigned long frameLimit = SDL_GetTicks(); + + engine.done = 0; + flushInput(); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = 0; + + if ((currentGame.useMusic) && (engine.useAudio)) + Mix_PlayMusic(engine.music, 1); + + while (!engine.done) + { + graphics.updateScreen(); + graphics.unBuffer(); + + now = SDL_GetTicks(); + + doStarfield(); + doExplosions(); + + for (int i = 0 ; i < 15 ; i++) + { + addEngine(&enemy[i]); + enemy[i].x += enemy[i].dx; + graphics.blit(enemy[i].image[0], (int)enemy[i].x, (int)enemy[i].y); + if (enemy[i].x > 830) + { + enemy[i].x = -10; + enemy[i].y = rand() % 580; + enemy[i].dx = 1 + rand() % 3; + } + } + + if ((now - then > 2000) && (now - then < 8000) && (!skip)) + { + graphics.blit(prlogo, prx, pry); + } + else if ((now - then > 9000) && (now - then < 15000) && (!skip)) + { + graphics.blitText(0); + } + else if ((now - then > 16000) && (now - then < 21000) && (!skip)) + { + graphics.blitText(1); + } + else if ((now - then > 25500) || (skip)) + { + graphics.blit(sflogo, sfx, sfy); + + if ((now - then >= 27500) || (skip)) + { + graphics.addBuffer(280, 345, 235, 145); + + graphics.blevelRect(optionRec.x, optionRec.y, optionRec.w, optionRec.h, redGlow, 0x00, 0x00); + + switch(menuType) + { + case 0: + listLength = showGameMenu(continueSaveSlot); + break; + case 1: + listLength = showLoadMenu(); + break; + case 2: + listLength = showOptionsMenu(); + break; + case 3: + listLength = showCheatMenu(); + break; + } + + redGlow += redDir; + if (redGlow <= 0) {redDir = 2; redGlow = 0;} + if (redGlow >= 255) {redDir = -2; redGlow = 255;} + + if (engine.keyState[SDLK_UP]) + { + engine.keyState[SDLK_UP] = 0; + Math::wrapChar(&(--selectedOption), 1, listLength); + if (menuType == 0) + if ((selectedOption == 2) || (selectedOption == 3)) + if (continueSaveSlot == 0) + selectedOption = 1; + } + if (engine.keyState[SDLK_DOWN]) + { + engine.keyState[SDLK_DOWN] = 0; + Math::wrapChar(&(++selectedOption), 1, listLength); + if (menuType == 0) + if ((selectedOption == 2) || (selectedOption == 3)) + if (continueSaveSlot == 0) + selectedOption = 4; + } + + optionRec.y = 326 + (20 * selectedOption); + if (menuType > 0) + if (selectedOption == listLength) + optionRec.y += 20; + + if (!skip) + { + graphics.drawString("Copyright Parallel Realities 2003", 5, 580, FONT_WHITE, graphics.background); + graphics.drawString(buildVersion, 695, 580, FONT_WHITE, graphics.background); + graphics.addBuffer(0, 580, 800, 20); + skip = 1; + } + } + } + + getPlayerInput(); + + // if someone has invoked the credits cheat + if (engine.cheatCredits) + { + doCredits(); + engine.cheatCredits = 0; + } + + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL]) || (engine.keyState[SDLK_SPACE])) + { + if ((now - then <= 27500) && (!skip)) + { + graphics.drawString("Copyright Parallel Realities 2003", 5, 580, FONT_WHITE, graphics.background); + graphics.drawString(buildVersion, 695, 580, FONT_WHITE, graphics.background); + graphics.addBuffer(0, 580, 800, 20); + skip = 1; + } + else + { + switch(menuType) + { + case 0: + if ((selectedOption == 1) || (selectedOption == 3)) + engine.done = 1; + else if (selectedOption == 2) + {menuType = 1; selectedOption = 1;} + else if (selectedOption == 4) + {menuType = 2; selectedOption = 1;} + else if (selectedOption == 5) + { + if (engine.cheat) + {menuType = 3; selectedOption = 1;} + else + engine.done = 1; + } + else if (selectedOption == 6) + engine.done = 1; + break; + + case 1: + if (selectedOption != listLength) + {engine.done = 1; continueSaveSlot = selectedOption; selectedOption = 3;} + else + {menuType = 0; selectedOption = 1;} + break; + + case 2: + if ((selectedOption == 1) && (engine.useAudio)) + currentGame.useSound = 1 - currentGame.useSound; + else if ((selectedOption == 2) && (engine.useAudio)) + { + currentGame.useMusic = 1 - currentGame.useMusic; + + if (currentGame.useMusic) + { + if (Mix_PausedMusic() == 1) + Mix_ResumeMusic(); + else + Mix_PlayMusic(engine.music, 1); + } + else + { + Mix_PauseMusic(); + } + } + else if (selectedOption == 3) + { + currentGame.fullScreen = 1 - currentGame.fullScreen; + #if LINUX + SDL_WM_ToggleFullScreen(graphics.screen); + #else + if (currentGame.fullScreen) + graphics.screen = SDL_SetVideoMode(800, 600, 16, SDL_HWSURFACE|SDL_HWPALETTE|SDL_FULLSCREEN); + else + graphics.screen = SDL_SetVideoMode(800, 600, 0, SDL_HWSURFACE|SDL_HWPALETTE); + + graphics.drawBackground(); + flushBuffer(); + #endif + } + else if (selectedOption == 4) + Math::wrapChar(&(++currentGame.autoSaveSlot), -1, 4); + else if (selectedOption == listLength) + {menuType = 0; selectedOption = 1;} + createOptionsMenu(); + break; + + case 3: + if (selectedOption == 1) + engine.cheatShield = 1 - engine.cheatShield; + else if (selectedOption == 2) + engine.cheatAmmo = 1 - engine.cheatAmmo; + else if (selectedOption == 3) + engine.cheatCash = 1 - engine.cheatCash; + else if (selectedOption == 4) + engine.cheatTime = 1 - engine.cheatTime; + else if (selectedOption == listLength) + {menuType = 0; selectedOption = 1;} + createCheatMenu(); + break; + + case 4: + if (selectedOption == listLength) + {menuType = 0; selectedOption = 1;} + break; + } + } + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + } + + while (SDL_GetTicks() < (frameLimit + 16)){} + frameLimit = SDL_GetTicks(); + } + + Mix_HaltMusic(); + + SDL_FreeSurface(prlogo); + SDL_FreeSurface(sflogo); + + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + + resetLists(); + + if (selectedOption == 1) + selectedOption = 2; // go straight to mission 0 + + if (selectedOption == 3) + { + newGame(); + selectedOption = loadGame(continueSaveSlot); + } + + // Send back a negative number... + if (selectedOption > 4) + { + selectedOption = -1; + exit(0); + } + + return selectedOption; +} + +/* +Scrolls the intro text up the screen and nothing else. The text will be placed +into a data file when the game is finished. +*/ +void showStory() +{ + graphics.freeGraphics(); + + int y = 620; + + FILE *fp; + + #if USEPACK + int dataLocation = locateDataInPak("data/intro.txt", 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen("data/intro.txt", "rb"); + #endif + + int i = 0; + int nextPos = -1; + char string[255]; + + fscanf(fp, "%d ", &nextPos); + + while (nextPos != -1) + { + fscanf(fp, "%[^\n]%*c", string); + + y += nextPos; + graphics.textSurface(i, string, -1, y, FONT_WHITE); + + i++; + + fscanf(fp, "%d ", &nextPos); + } + + fclose(fp); + + loadBackground("gfx/startUp.jpg"); + graphics.blit(graphics.background, 0, 0); + graphics.flushBuffer(); + + unsigned long frameLimit = SDL_GetTicks(); + + flushInput(); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + + while (true) + { + graphics.updateScreen(); + graphics.unBuffer(); + + getPlayerInput(); + + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL]) || (engine.keyState[SDLK_SPACE])) + break; + + if (graphics.textShape[8].y > 450) + { + for (int i = 0 ; i < 9 ; i++) + { + graphics.textShape[i].y -= 0.25; + graphics.blitText(i); + } + } + else + { + SDL_Delay(3000); + break; + } + + while (SDL_GetTicks() < (frameLimit + 16)){} + frameLimit = SDL_GetTicks(); + } +} + +/* +The game over screen :( +*/ +void gameover() +{ + graphics.flushBuffer(); + graphics.freeGraphics(); + SDL_FillRect(graphics.background, NULL, graphics.black); + + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + engine.gameSection = SECTION_INTERMISSION; + + loadMusic("music/Wybierak.mod"); + + SDL_Surface *gameover = loadImage("gfx/gameover.png"); + + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + graphics.clearScreen(graphics.black); + SDL_Delay(1000); + + if ((currentGame.useMusic) && (engine.useAudio)) + { + Mix_VolumeMusic(100); + Mix_PlayMusic(engine.music, 1); + } + + int x = (800 - gameover->w) / 2; + int y = (600 - gameover->h) / 2; + + unsigned long frameLimit = SDL_GetTicks(); + graphics.updateScreen(); + + flushInput(); + engine.keyState[SDLK_LCTRL] = engine.keyState[SDLK_RCTRL] = engine.keyState[SDLK_SPACE] = 0; + + while (true) + { + getPlayerInput(); + + if ((engine.keyState[SDLK_LCTRL]) || (engine.keyState[SDLK_RCTRL]) || (engine.keyState[SDLK_SPACE])) + break; + + graphics.updateScreen(); + + graphics.unBuffer(); + x = ((800 - gameover->w) / 2) - Math::rrand(-2, 2); + y = ((600 - gameover->h) / 2) - Math::rrand(-2, 2); + graphics.blit(gameover, x, y); + + // Limit us to 60 frame a second + while (SDL_GetTicks() < (frameLimit + 16)){} + frameLimit = SDL_GetTicks(); + } + + SDL_FreeSurface(gameover); + + if ((currentGame.useMusic) && (engine.useAudio)) + Mix_HaltMusic(); + + graphics.flushBuffer(); +} + +void doCredits() +{ + loadBackground("gfx/credits.jpg"); + graphics.flushBuffer(); + graphics.freeGraphics(); + + if ((currentGame.useMusic) && (engine.useAudio)) + loadMusic("music/Solace.s3m"); + + FILE *fp; + int numberOfCredits = 0; + int lastCredit = 0; + + int yPos = 0; + int yPos2 = 510; + char text[255]; + + textObject *credit; + + graphics.clearScreen(graphics.black); + graphics.updateScreen(); + graphics.clearScreen(graphics.black); + SDL_Delay(1000); + + graphics.drawBackGround(); + + #if USEPACK + int dataLocation = locateDataInPak("data/credits.txt", 1); + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + #else + fp = fopen("data/credits.txt", "rb"); + #endif + + for (int i = 0 ; i < 6 ; i++) + { + fscanf(fp, "%[^\n]%*c", text); + graphics.drawString(text, -1, 240 + (i * 20), FONT_WHITE); + } + + fscanf(fp, "%d%*c", &numberOfCredits); + + credit = (textObject*) malloc(sizeof(textObject) * numberOfCredits); + + for (int i = 0 ; i < numberOfCredits ; i++) + { + fscanf(fp, "%d %[^\n]%*c", &yPos, text); + credit[i].image = graphics.textSurface(text, FONT_WHITE); + credit[i].x = (800 - credit[i].image->w) / 2; + yPos2 += yPos; + credit[i].y = yPos2; + } + + fclose(fp); + + if ((currentGame.useMusic) && (engine.useAudio)) + { + Mix_VolumeMusic(100); + Mix_PlayMusic(engine.music, 1); + } + + SDL_Delay(3000); + + graphics.updateScreen(); + SDL_Delay(10000); + graphics.drawBackGround(); + + unsigned long frameLimit = SDL_GetTicks(); + engine.done = 0; + + lastCredit = numberOfCredits - 1; + + SDL_Rect r1 = {0, 80, 800, 20}; + SDL_Rect r2 = {0, 500, 800, 20}; + + engine.keyState[SDLK_ESCAPE] = 0; + flushInput(); + + while (true) + { + graphics.updateScreen(); + graphics.unBuffer(); + + getPlayerInput(); + if (engine.keyState[SDLK_ESCAPE]) + break; + + for (int i = 0 ; i < numberOfCredits ; i++) + { + if ((credit[i].y > 80) && (credit[i].y < 500)) + graphics.blit(credit[i].image, (int)credit[i].x, (int)credit[i].y); + if (credit[lastCredit].y > 400) + credit[i].y -= 0.3; + } + + SDL_FillRect(graphics.screen, &r1, graphics.black); + SDL_FillRect(graphics.screen, &r2, graphics.black); + + while (SDL_GetTicks() < (frameLimit + 16)){} + frameLimit = SDL_GetTicks(); + } + + for (int i = 0 ; i < numberOfCredits ; i++) + { + SDL_FreeSurface(credit[i].image); + } + + free(credit); +} + diff --git a/code/title.h b/code/title.h new file mode 100644 index 0000000..c814287 --- /dev/null +++ b/code/title.h @@ -0,0 +1,54 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern SDL_Surface *loadImage(char *filename); +extern void unpack(char *file); +extern void loadMusic(char *filename); +extern void doStarfield(); +extern void doExplosions(); +extern void addEngine(object *craft); +extern void getPlayerInput(); +extern void resetLists(); +extern signed char loadGame(int slot); +extern int initSaveSlots(); +extern void newGame(); +extern void loadGameGraphics(); +extern void loadBackground(char *filename); +extern void doCredits(); +extern int locateDataInPak(char *file, signed char required); +extern void flushInput(); + +extern globalEngineVariables engine; +extern devVariables dev; +extern object defEnemy[MAX_DEFALIENS]; +extern object enemy[MAX_ALIENS]; +extern Game currentGame; +extern Graphics graphics; diff --git a/code/unpack.cpp b/code/unpack.cpp new file mode 100644 index 0000000..963ee5b --- /dev/null +++ b/code/unpack.cpp @@ -0,0 +1,161 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "unpack.h" + +/* +Searches the pak file for the required data. When +it is found, the data is read into a character buffer. +*/ +void unpack(char *file, signed char fileType) +{ + unsigned char *packBuffer; + char packFilename[60]; + int packFSize; + + FILE *pak; + FILE *fp = NULL; // music has to be read-written-read! + char musicFilename[PATH_MAX]; + + strcpy(packFilename, ""); + + pak = fopen(PACKLOCATION, "rb"); + if (pak == NULL) + { + printf("Couldn't access the Project: Starfighter data file!\n"); + exit(1); + } + fseek(pak, 4, SEEK_SET); + + while (true) + { + if (!fread(packFilename, 1, 56, pak)) + { + fclose(pak); + if (fileType != PAK_FONT) + { + showErrorAndExit(0, file); + } + exit(1); + break; + } + + fread(&packFSize, 4, 1, pak); + packFSize = SDL_SwapLE32(packFSize); + + if (strcmp(packFilename, file) == 0) + { + if ((fileType == PAK_MOD) || (fileType == PAK_S3M)) + { + if (fileType == PAK_MOD) + { + sprintf(musicFilename, "%smusic.mod", engine.userHomeDirectory); + fp = fopen(musicFilename, "wb"); + } + else + { + sprintf(musicFilename, "%smusic.s3m", engine.userHomeDirectory); + fp = fopen(musicFilename, "wb"); + } + + if (fp == NULL) + showErrorAndExit(1, ""); + } + packBuffer = (unsigned char*) malloc(packFSize); + fread(packBuffer, 1, packFSize, pak); + if ((fileType == PAK_MOD) || (fileType == PAK_S3M)) + { + fwrite(packBuffer, 1, packFSize, fp); + fclose(fp); + } + break; + } + else + { + fseek(pak, packFSize, SEEK_CUR); + } + } + + if ((fileType != PAK_MOD) && (fileType != PAK_S3M)) + engine.sdlrw = SDL_RWFromMem(packBuffer, packFSize); + + //printf("Extracted: %s\n", file); + + fclose(pak); +} + +/* +Search the data package for the required file. +When it is found, return the location. +*/ +int locateDataInPak(char *file, signed char required) +{ + //printf("Looking for %s...", file); + + char packFilename[60]; + int packFSize; + int location = 0; + + FILE *pak; + + strcpy(packFilename, ""); + + pak = fopen(PACKLOCATION, "rb"); + if (pak == NULL) + { + printf("Couldn't access the Project: Starfighter data file!\n"); + exit(1); + } + fseek(pak, 4, SEEK_SET); + + while (true) + { + if (!fread(packFilename, 1, 56, pak)) + { + fclose(pak); + if (required) + { + showErrorAndExit(0, file); + exit(1); + } + break; + } + fread(&packFSize, 4, 1, pak); + packFSize = SDL_SwapLE32(packFSize); + + if (strcmp(packFilename, file) == 0) + { + location = ftell(pak); + fclose(pak); + + //printf("found it!\n"); + + return location; + } + else + { + fseek(pak, packFSize, SEEK_CUR); + } + } + + //printf("not found (skipping)\n"); + + return -1; // we only get this if it isn't required +} diff --git a/code/unpack.h b/code/unpack.h new file mode 100644 index 0000000..cb115da --- /dev/null +++ b/code/unpack.h @@ -0,0 +1,35 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" +#include "SDL/SDL_endian.h" + +#include "defs.h" +#include "structs.h" + +extern void showErrorAndExit(int errorId, char *name); + +extern globalEngineVariables engine; diff --git a/code/version.h b/code/version.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/code/version.h @@ -0,0 +1 @@ + diff --git a/code/weapons.cpp b/code/weapons.cpp new file mode 100644 index 0000000..11efa08 --- /dev/null +++ b/code/weapons.cpp @@ -0,0 +1,261 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "weapons.h" + +void setWeaponShapes() +{ + for (int i = 0 ; i < MAX_WEAPONS ; i++) + { + weapon[i].image[0] = graphics.shape[weapon[i].imageIndex[0]]; + weapon[i].image[1] = graphics.shape[weapon[i].imageIndex[1]]; + } +} + +#if USEPACK + +void loadWeapons() +{ + int dataLocation = locateDataInPak("data/weapons.dat", 1); + int id, ammo, damage, reload, speed, image1, image2, flags; + + FILE *fp; + + fp = fopen(PACKLOCATION, "rb"); + fseek(fp, dataLocation, SEEK_SET); + + for (int i = 0 ; i < MAX_WEAPONS ; i++) + { + fscanf(fp, "%d", &id); + fscanf(fp, "%d", &ammo); + fscanf(fp, "%d", &damage); + fscanf(fp, "%d", &reload); + fscanf(fp, "%d", &speed); + fscanf(fp, "%d", &image1); + fscanf(fp, "%d", &image2); + fscanf(fp, "%d", &flags); + + weapon[i].id = id; + weapon[i].ammo[0] = ammo; + weapon[i].damage = damage; + weapon[i].reload[0] = reload; + weapon[i].speed = speed; + weapon[i].imageIndex[0] = image1; + weapon[i].imageIndex[1] = image2; + weapon[i].flags = flags; + } + + fclose(fp); +} + +void initWeapons() {loadWeapons();} + +#else + +void saveWeapons() +{ + FILE *fp; + + fp = fopen("data/weapons.dat", "wb"); + if (fp == NULL) + { + printf("Unable to write Weapon Data File\n"); + exit(1); + } + + for (int i = 0 ; i < MAX_WEAPONS ; i++) + { + fprintf(fp, "%d ", weapon[i].id); + fprintf(fp, "%d ", weapon[i].ammo[0]); + fprintf(fp, "%d ", weapon[i].damage); + fprintf(fp, "%d ", weapon[i].reload[0]); + fprintf(fp, "%d ", weapon[i].speed); + fprintf(fp, "%d ", weapon[i].imageIndex[0]); + fprintf(fp, "%d ", weapon[i].imageIndex[1]); + fprintf(fp, "%d\n", weapon[i].flags); + } + + // Put an extra line for the PAK file "just in case" + fprintf(fp, "\n"); + + fclose(fp); +} + +/* +A list of predefined weaponary. Will most probably +be placed into a data file in the final build. +*/ +void initWeapons() +{ + // Player's weapon (this NEVER allocated to anything else) + weapon[W_PLAYER_WEAPON].id = WT_PLASMA; + weapon[W_PLAYER_WEAPON].ammo[0] = 1; + weapon[W_PLAYER_WEAPON].damage = 1; + weapon[W_PLAYER_WEAPON].reload[0] = 15; + weapon[W_PLAYER_WEAPON].speed = 10; + weapon[W_PLAYER_WEAPON].imageIndex[0] = 0; + weapon[W_PLAYER_WEAPON].imageIndex[1] = 0; + weapon[W_PLAYER_WEAPON].flags = WF_STRAIGHT; + + // Nor is this one! + weapon[W_PLAYER_WEAPON2] = weapon[W_PLAYER_WEAPON]; + + // Single Shot + weapon[W_SINGLE_SHOT].id = WT_PLASMA; + weapon[W_SINGLE_SHOT].ammo[0] = 1; + weapon[W_SINGLE_SHOT].damage = 1; + weapon[W_SINGLE_SHOT].reload[0] = 15; + weapon[W_SINGLE_SHOT].speed = 10; + weapon[W_SINGLE_SHOT].imageIndex[0] = 0; + weapon[W_SINGLE_SHOT].imageIndex[1] = 1; + weapon[W_SINGLE_SHOT].flags = WF_STRAIGHT; + + // Double Shot + weapon[W_DOUBLE_SHOT] = weapon[W_SINGLE_SHOT]; + weapon[W_DOUBLE_SHOT].ammo[0] = 2; + + // Triple Shot + weapon[W_TRIPLE_SHOT] = weapon[W_SINGLE_SHOT]; + weapon[W_TRIPLE_SHOT].ammo[0] = 3; + + // Rockets + weapon[W_ROCKETS].id = WT_ROCKET; + weapon[W_ROCKETS].ammo[0] = 1; + weapon[W_ROCKETS].damage = 15; + weapon[W_ROCKETS].reload[0] = 45; + weapon[W_ROCKETS].speed = 20; + weapon[W_ROCKETS].flags = WF_STRAIGHT; + weapon[W_ROCKETS].imageIndex[0] = 2; + weapon[W_ROCKETS].imageIndex[1] = 3; + + // Double Rockets (uses ROCKETS as base) + weapon[W_DOUBLE_ROCKETS] = weapon[W_ROCKETS]; + weapon[W_DOUBLE_ROCKETS].ammo[0] = 2; + weapon[W_DOUBLE_ROCKETS].reload[0] = 80; + + // Micro Rockets + weapon[W_MICRO_ROCKETS].id = WT_ROCKET; + weapon[W_MICRO_ROCKETS].ammo[0] = 5; + weapon[W_MICRO_ROCKETS].damage = 3; + weapon[W_MICRO_ROCKETS].reload[0] = 30; + weapon[W_MICRO_ROCKETS].speed = 15; + weapon[W_MICRO_ROCKETS].flags = WF_STRAIGHT + WF_VARIABLE_SPEED; + weapon[W_MICRO_ROCKETS].imageIndex[0] = 2; + weapon[W_MICRO_ROCKETS].imageIndex[1] = 3; + + // Energy Ray + weapon[W_ENERGYRAY].id = WT_ENERGYRAY; + weapon[W_ENERGYRAY].ammo[0] = 255; + weapon[W_ENERGYRAY].damage = 1; + weapon[W_ENERGYRAY].reload[0] = 25; // reload for energy ray is never used + weapon[W_ENERGYRAY].speed = 15; + weapon[W_ENERGYRAY].flags = WF_STRAIGHT; + + // Laser + weapon[W_LASER].id = WT_LASER; + weapon[W_LASER].ammo[0] = 1; + weapon[W_LASER].damage = 3; + weapon[W_LASER].reload[0] = 1; + weapon[W_LASER].speed = 10; + weapon[W_LASER].imageIndex[0] = 1; + weapon[W_LASER].imageIndex[1] = 1; + weapon[W_LASER].flags = WF_STRAIGHT; + + // Beam up weapon + weapon[W_CHARGER].id = WT_CHARGER; + weapon[W_CHARGER].ammo[0] = 1; + weapon[W_CHARGER].damage = 1; + weapon[W_CHARGER].reload[0] = 0; + weapon[W_CHARGER].speed = 12; + weapon[W_CHARGER].flags = WF_STRAIGHT; + weapon[W_CHARGER].imageIndex[0] = 33; + weapon[W_CHARGER].imageIndex[1] = 34; + + // Homing missile + weapon[W_HOMING_MISSILE].id = WT_ROCKET; + weapon[W_HOMING_MISSILE].ammo[0] = 1; + weapon[W_HOMING_MISSILE].damage = 15; + weapon[W_HOMING_MISSILE].reload[0] = 35; + weapon[W_HOMING_MISSILE].speed = 10; + weapon[W_HOMING_MISSILE].flags = WF_STRAIGHT + WF_HOMING; + weapon[W_HOMING_MISSILE].imageIndex[0] = 4; + weapon[W_HOMING_MISSILE].imageIndex[1] = 4; + + // Double homing missile + weapon[W_DOUBLE_HOMING_MISSILES] = weapon[W_HOMING_MISSILE]; + weapon[W_DOUBLE_HOMING_MISSILES].ammo[0] = 2; + weapon[W_DOUBLE_HOMING_MISSILES].reload[0] = 65; + weapon[W_DOUBLE_HOMING_MISSILES].imageIndex[0] = 4; + weapon[W_DOUBLE_HOMING_MISSILES].imageIndex[1] = 4; + + // Micro homing missiles + weapon[W_MICRO_HOMING_MISSILES].id = WT_ROCKET; + weapon[W_MICRO_HOMING_MISSILES].ammo[0] = 5; + weapon[W_MICRO_HOMING_MISSILES].damage = 12; + weapon[W_MICRO_HOMING_MISSILES].reload[0] = 65; + weapon[W_MICRO_HOMING_MISSILES].speed = 3; + weapon[W_MICRO_HOMING_MISSILES].flags = WF_STRAIGHT + WF_HOMING; + weapon[W_MICRO_HOMING_MISSILES].imageIndex[0] = 4; + weapon[W_MICRO_HOMING_MISSILES].imageIndex[1] = 4; + + // Aimed plasma bolt (2x damage) + weapon[W_AIMED_SHOT].id = WT_DIRECTIONAL; + weapon[W_AIMED_SHOT].ammo[0] = 1; + weapon[W_AIMED_SHOT].damage = 2; + weapon[W_AIMED_SHOT].reload[0] = 15; + weapon[W_AIMED_SHOT].speed = 0; + weapon[W_AIMED_SHOT].flags = WF_STRAIGHT + WF_AIMED; + weapon[W_AIMED_SHOT].imageIndex[0] = 33; + weapon[W_AIMED_SHOT].imageIndex[1] = 34; + + // 3 way spread weapon + weapon[W_SPREADSHOT].id = WT_SPREAD; + weapon[W_SPREADSHOT].ammo[0] = 3; + weapon[W_SPREADSHOT].damage = 1; + weapon[W_SPREADSHOT].reload[0] = 10; + weapon[W_SPREADSHOT].speed = 10; + weapon[W_SPREADSHOT].flags = WF_THIN_SPREAD; + weapon[W_SPREADSHOT].imageIndex[0] = 0; + weapon[W_SPREADSHOT].imageIndex[1] = 1; + + // Sid's ion cannon like weapon + weapon[W_IONCANNON].id = WT_PLASMA; + weapon[W_IONCANNON].ammo[0] = 1; + weapon[W_IONCANNON].damage = 1; + weapon[W_IONCANNON].reload[0] = 2; + weapon[W_IONCANNON].speed = 10; + weapon[W_IONCANNON].flags = WF_STRAIGHT + WF_DISABLE + WF_AIMED; + weapon[W_IONCANNON].imageIndex[0] = 35; + weapon[W_IONCANNON].imageIndex[1] = 35; + + // Directional Shock Missile - Used by Kline in final battle + weapon[W_DIRSHOCKMISSILE].id = WT_ROCKET; + weapon[W_DIRSHOCKMISSILE].ammo[0] = 5; + weapon[W_DIRSHOCKMISSILE].damage = 20; + weapon[W_DIRSHOCKMISSILE].reload[0] = 60; + weapon[W_DIRSHOCKMISSILE].speed = 0; + weapon[W_DIRSHOCKMISSILE].flags = WF_STRAIGHT + WF_AIMED + WF_TIMEDEXPLOSION; + weapon[W_DIRSHOCKMISSILE].imageIndex[0] = 4; + weapon[W_DIRSHOCKMISSILE].imageIndex[1] = 4; + + saveWeapons(); +} + +#endif diff --git a/code/weapons.h b/code/weapons.h new file mode 100644 index 0000000..a271944 --- /dev/null +++ b/code/weapons.h @@ -0,0 +1,36 @@ +/* +Copyright (C) 2003 Parallel Realities + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include +#include + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_mixer.h" + +#include "defs.h" +#include "structs.h" +#include "classes.h" + +extern int locateDataInPak(char *file, signed char required); + +extern Graphics graphics; +extern object weapon[MAX_WEAPONS]; + diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 0000000..e8a612e --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/docs/index.html b/docs/index.html new file mode 100755 index 0000000..9562816 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,368 @@ + + + + + + + + + + + + + + + +
+ +

Gameplay Manual

+
+

+ +
License Agreement
+

Project: Starfighter
+Copyright Parallel Realities 2003
+All Rights Reserved +

+ +

+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+

+ +

+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+

+ +

+See the GNU General Public License for more details.
+

+ +

+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+

+ +
Introduction
+

+Project: Starfighter is an old school 2D shoot 'em up. In the game you take on the role of a +rebel pilot called Chris, who is attempting to overthrow a military corporation called Weapco. Weapco +has seized control of the known universe and currently rules it with an iron fist. Chris can no longer +stand back and watch as millions of people suffer and die. He steals an experimental craft known +as "Firefly" and begins his mission to fight his way to Sol, freeing key systems along the way. The +game opens with Chris attempting to escape a Weapco patrol that has intercepted him. +

+ +

+
Installation
+

Binary files are provided for both Windows and Linux*.

+

Installation under Windows

+ +

+ Unzip the archive into a suitable directory and then double click the Starfighter.exe + file to begin. Games and settings will be saved into this directory. +

+ +

Installation under Linux

+ +
+tar zxf starfighter.tar.gz
+cd Starfighter
+make
+make install
+
+ +

When the game is first run it will attempt to create

+ +
+~/.parallelrealities/starfighter/
+
+ +

Should this fail the game will not run.

+ +

* x86 binary compiled with g++ on Mandrake 9. Source and makefile are provided for Linux users +who need to recompile

+ +

+
Controls
+ +

Menus

+
    +Arrow Keys - Highlight option
    +Left Control / Space - Select menu option
    +
+ +

Intermission Screen

+
    +Mouse - Move cursor
    +Left Mouse Button - Selected option
    +Right Mouse Button - Toggle planet orbit On / Off
    +
+ +

In Game

+
    +Arrow Keys - Control Firefly
    +Left Ctrl - Fire primary weapon
    +Space - Fire secondary weapon
    +Shift - Toggle Primary Weapon Concentrate / Spread (see Weaponry)
    +T - Targetting Arrow On /Off
    +P - Pause / Unpause
    +Escape - Flee (not possible until all primary missions completed)
    +Escape whilst paused - Quit to title screen
    +
+ +

Note - Keys cannot be changed

+ +

+
How to Play
+

When first starting Project: Starfighter you will see the text scrolling introduction. You can +either wait for this to finish or you can skip it by pressing ctrl or space.

+

The title sequence will begin. Once the menu is shown (or you opt to skip to it by pressing +space or ctrl) you may select from the options shown

+ + + + + + + +
Start New Game
Load Game*
Continue Current Game*
Options
Quit
+ +

* will only be shown if there are saved games available

+ +

Start New Game
+This will start a new game +

+ +

Load Game
+This will bring up a list of currently available games to be loaded, along with the planet that +the game was saved in. This option is only shown when there is at least one saved game available. +

+ +

Continue Current Game
+This will automatically load the most recently saved game. This option is only shown when there is +at least one saved game available. +

+ +

Options
+This will show a list of game options. Options for sound, music and an auto save slot can be changed. +Music and Sound can be switched to either On or Off. The Auto Save Slot allows the player to select +a save slot that will automatically be updated when the player finishes a mission. This can be +switched from None to 1, 2, 3, 4 or 5. When this option is set to a number, that save slot will be +used to save a game at the end of a mission. Note that any currently existing game will be +automatically overwritten. +

+ +

Quit
+This will quit the game. Quitting is immediate, without prompting (the game can also be quit at +anytime by clicking the close button of the window). +

+ +

+
Loading and Saving Games
+

Games can be loaded from the title screen. When there are saved games available, the option "Load +Game" will be shown. Selecting this will show a list of available games to be loaded. Select a game +from the list to load, you will then be taken to the Intermission screen. To go back, selected +Back to Main Menu.

+

Games can be saved in two ways. The first way is to save a game on the Intermission screen. Move +the cursor to the Save Game icon and select it. You will see a list of five game slots that can be +used to save a game to. Click one of these and then click the "Save" button to save the game. The +second way is to use the Auto Save function. This will automatically save your game after you have +successfully completed a mission. To make use of this feature you must choose a save slot that +you wish to auto save into. This can be done on the title screen in the options section or on +the intermission screen at the options section.

+ +

+
Getting Missions
+

In each System, the player can get missions by going to the Comms section of the Intermission +screen. Here allies will inform you of tasks that need to be performed and what planet these tasks +apply to. Once the player has decided which task they will perform, they must go to the corresponding +planet in the system (see Moving Around for more details). Once stationed at the planet +click "Start Mission" to proceed to the mission briefing screen. The "Start Mission" icon will not +be shown if the mission of the planet has been completed.

+ +

+
Mission Briefing
+

Before the beginning of each mission you will be presented with a mission briefing screen. This +will outline the mission's primary and (if any) secondary objectives. It will also inform you of +mission restrictions, such as time limits. Once you have read this, press ctrl or space to +continue

+ +

+
Completing Missions
+

Each mission in the game has one or more objectives tied to it. These objectives are either +Primary or Secondary objectives. In order to complete the mission, the player must complete all the +primary mission objectives. For example, when the game begins Chris is fleeing a WEAPCO patrol. The +primary objective for this mission is to destroy all the enemy fighters. Once this is achieved the +Firefly will leave the sector and the mission will be marked as a success.

+

One thing to note is that some missions will have both Primary and Secondary objectives. In this +case, the Firefly does not leave the sector if all primary mission objectives are complete and +secondary objectives remain. The player may then attempt to complete remaining secondary +objectives or press Escape to leave the sector. Secondary objectives are optional.

+

During the mission you will see messages appearing at the bottom of the screen. These messages +can be related to items that you pick up, as well as mission related information.

+

+White messages are standard for picking up items such as cash and +power ups
+Green messages signify successful completion of mission objectives
+Light Blue messages give further details about Primary mission +requirements
+Red messages indicate mission failures, warnings and wing mate +ejections
+Yellow messages give further details about Secondary mission requirements +
+

+ +

+
The Target Arrow
+

+ +The Target Arrow can be used to locate enemies and point you in the direction that the enemy craft +is. You can toggle the Target Arrow on or off by pressing "T" on the keyboard. This arrow is very +useful for finding enemies that are evasive (such as transports) on missions that require you to +destroy all present craft. Simply follow the arrow to find the enemy. The arrow will not be displayed +when you are within the immediate vacinity of the target. On certain missions (such as Bosses) the +arrow will initially point towards a certain target that is part of the mission objective. The +target's current remaining shield is also displayed in the bottom right hand corner of the screen. +Please note that due to the nature of the game, the target arrow cannot be cycled through enemies. +

+ +

+
Moving Around Systems
+

To play a mission in Starfighter you must be stationed at the relevant planet. To get to the +planet you require, you will need to click on the planet whilst viewing it on the Show System section +of the Intermission screen. Travelling between planets can be dangerous, but luckily Spirit is a +peaceful place and there are no chances of interceptions, so travel is instantaneous.

+

Other systems are not as friendly and whilst travelling to a new destination the player runs +the risk of being intercepted by a WEAPCO patrol. After Spirit the player will select a planet +to travel to by clicking on it with the mouse. "Destination", followed by the planet's name +will appear in the bottom right hand corner of the screen. A new icon labelled "Go To Destination" +will also appear. Clicking this icon will make the player travel to the destination planet.

+

Travelling to new planets is represented at the bottom of the screen by the two planets (the +one being travelled from and the one being travelled to). A red bar will fill up as the journey +progresses. The speed the bar fills up will vary occurring to how far away the planets are from +one another. At any point during this time the planet can be intercepted (see Interceptions). +Once the red bar has filled up completely the journey will be completed and you will be +stationed that the new planet.

+ +

+
Interceptions
+

Interceptions can take place whilst travelling between two planets within a system. When the +player is intercepted they will go directly into a mission-like scenario. The objective of this +interception is to clear all attacking forces. Once this is done, the player will be free to +leave.

+

Interceptions also serve other purposes - Sometimes the WEAPCO patrol may have slave +transports with them. One of the objectives of a later system is to rescue a certain amount of +slaves. This is only possible during interceptions.

+

One important thing to remember is that any damage the player receives during an interception +will NOT be repaired until they have reached the destination planet. Therefore if the player +is heavily damaged during one interception that damage will still be present if they are +attacked again. This can make interceptions very dangerous.

+ +

+
Weaponry and Upgrades
+

During the course of the game you will receive money. Money is gained from destroying enemy craft +and picking up cash spheres in game (please be aware that due to the nature of the game cash is not +earned for destroying ships during Interceptions).

+

Money can be used to upgrade the Firefly and purchase additional ammunition for weapons. Items +can be purchased from a shop on the Intermission screen.

+ +

Temporary Upgrades +

    Temporary upgrades are used to boost the Firefly's ability to be powered up. For example, at +the start of the game the Firefly can only be powered up to fire two plasma bolts are once. By +purchasing an upgrade for the Firefly, you can allow for power ups to enable you to fire a maximum +of up to five plasma bolts at once. Note that this only affects power ups and these will still be +ammunition limited. To upgrade your default weapon, you will need permanent power ups (see below). +
+

+ +

Permanent Upgrades

+
    These power ups are more expensive than temporary upgrades, but are permanent. Whereas a temporary +upgrade requires you to make use of power ups on an ammunition limited base, permanent power ups are +not limited. This is highly useful when facing heavily shielded and tough opponents, with no means +of getting plasma ammo or transports in sight! When you power up your permanent weapon, your +powered up weapon level will also be automatically upgraded if it is less powerful than your new +current power up level.

    +When the Firefly's Primary Weapon has been upgraded to its maximum output (3 plasma cannons) the +player can toggle the output type by pressing Shift. The output type can either be Concentrate (the +default firing type) or Spread. Various situations can call for varying the output type. +

+ +

Secondary Weapons

+
    +As well as primary weapons and temporary upgrades, the Firefly is also capable of using a +secondary weapon. At the start of the game this is a rocket launcher. Like the other weapons in the +game this can be upgraded by purchasing a new weapon from the shop. At the start of the game, a +Double Rocket Launcher and Micro Rocket Launcher are available for purchase. Secondary weapons are +used in the same way the rocket launcher is used (Space to fire). However the Laser Cannon and +Charge Cannon work differently.

    +Neither the Charge Cannon or the Laser Cannon are ammunition limited (unlike the other rocket based +weapons). The Charge Cannon works by the player holding down the Space bar and releasing it. A +meter at the bottom of the screen shows how much charge has been built up. The Laser Cannon works +by the player holding down the Space bar to fire a stream of laser fire. It is prone to over heating +and must be allowed to cool after usage. +

+ +

Certain weapons and upgrades will not be available to you until later in the game, so remember +to save your money for them

+ +

+
Item Spheres
+

When an enemy craft is destroyed they will sometimes release ammo and cash spheres. Certain +enemy craft will release power up spheres that can give your weapon a temporary boost. The following +are spheres that can be picked up during missions,

+ + + + + + + + +
Cash Sphere - Provides you with an additional cash bonus
Plasma Ammo Sphere - Increases your current plasma ammo
Rocket Ammo Sphere - Increases your current rocket ammo
Power Sphere - Boosts your plasma power
Output Sphere - Boosts the amount of plasma shots you can fire
Cooler Sphere - Increases your plasma firing rate
Super Sphere - Three / Five way spread, full power and cooling (Rare)
+

+ +

+
Cut Scenes
+

During the course of the game mini cut scenes will be shown after certain missions. These scenes +serve to extend the plot of the game and provide the player with gameplay tips. If you wish to +skip a cut scene press Escape. It is advised that you only skip cut scenes if you have already seen +them.

+ +

+
Ending the Game
+

The game is over when the Firefly's shield units are reduced to 0 (zero), or a Primary Mission +objective is failed. At this point, you will see the Game Over screen. To continue, press Ctrl +or Space. You will then be taken back to the title screen. In certain missions the game will end +if Sid Wilson is killed.

+ +

+
About
+ +

Parallel Realities started off writing games on the Amiga using AMOS and then, later, Blitz Basic +2. Games written included the BOTSS Trilogy and most notably TANX Squadron. TANX Squadron was awarded +Amiga Format's contributor prize of the month in the summer of 1999. Project: Starfighter originally +started life on the Amiga but was never completed. Development began again for Linux in 2002 with this +being our first C program. The game matured quickly from the initial ideas and this is the finished +product. We do hope you enjoy playing it.

+ +

Project: Starfighter
+Copyright Parallel Realities 2003
+All Rights Reserved

+

Created using the SDL library

+ +

www.parallelrealities.co.uk

+ + + diff --git a/makefile b/makefile new file mode 100755 index 0000000..4daa419 --- /dev/null +++ b/makefile @@ -0,0 +1,37 @@ +CFLAGS = `sdl-config --cflags` -Wall -DLINUX +LIBS = `sdl-config --libs` -lSDL_mixer -lSDL_image +OBJS = ai.o aliens.o audio.o bullets.o cargo.o collectable.o comms.o debris.o events.o explosions.o game.o globals.o graphics.o init.o intermission.o loadSave.o messages.o misc.o missions.o player.o resources.o script.o shop.o Starfighter.o title.o unpack.o weapons.o + +VERSION = 1.1 +PROG = starfighter +PACK = starfighter.pak +DOCS = docs/* + +BINDIR = /usr/games/ +DATADIR = /usr/share/games/parallelrealities/ +DOCDIR = /usr/share/doc/starfighter/ +# top-level rule to create the program. +all: $(PROG) + +# compiling other source files. +%.o: code/%.cpp code/%.h code/structs.h code/defs.h code/classes.h + $(CXX) $(CFLAGS) -c -O3 -DVERSION=\"$(VERSION)\" -DPACKLOCATION=\"$(DATADIR)$(PACK)\" $< + +# linking the program. +$(PROG): $(OBJS) + $(CXX) $(LIBS) $(OBJS) -o $(PROG) + +# cleaning everything that can be automatically recreated with "make". +clean: + $(RM) $(OBJS) + +distclean: + $(RM) $(PROG) + +# install +install: + mkdir -p $(DATADIR) + strip $(PROG) + install -o root -g games -m 755 $(PROG) $(BINDIR)$(PROG) + install -o root -g games -m 644 $(PACK) $(DATADIR)$(PACK) + cp $(DOCS) $(DOCDIR)