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
+
+
+
+
+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.
+
+
+
+
+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.
+
+
+
+
+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
+
+
+
+
+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
+
+
+
+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).
+
+
+
+
+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.
+
+
+
+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.
+
+
+
+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
+
+
+
+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 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.
+
+
+
+
+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 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.
+
+
+
+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
+
+
+
+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) |
+
+
+
+
+
+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.
+
+
+
+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.
+
+
+
+
+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)